mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 10:38:45 +02:00
Compare commits
86 Commits
2.7.0-alph
...
support/2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8295eaed90 | ||
|
|
219b970703 | ||
|
|
76c139253e | ||
|
|
10cfb373f2 | ||
|
|
97d6d413bb | ||
|
|
3f8f57fa9a | ||
|
|
f916f9cde8 | ||
|
|
8a65a592f3 | ||
|
|
5e48400cb1 | ||
|
|
252562ace9 | ||
|
|
770ac8ffe5 | ||
|
|
ed3c387712 | ||
|
|
81a2a9278c | ||
|
|
e15d4bfab6 | ||
|
|
3e8dd2f4a5 | ||
|
|
51a49dfce8 | ||
|
|
066b71686d | ||
|
|
be633001a5 | ||
|
|
84426c6634 | ||
|
|
dbaf924171 | ||
|
|
8adf743cc7 | ||
|
|
75450ded1d | ||
|
|
2beb795f9a | ||
|
|
e8d314e1f6 | ||
|
|
e29f1825be | ||
|
|
9b854dbcc7 | ||
|
|
7757f1f2d2 | ||
|
|
a353317746 | ||
|
|
723eb90160 | ||
|
|
b3f827ed5e | ||
|
|
eaf8a187aa | ||
|
|
34f64c61f6 | ||
|
|
92a9a8c65f | ||
|
|
834ac00d37 | ||
|
|
5691ca0327 | ||
|
|
86f649affc | ||
|
|
4f5c987d8b | ||
|
|
e441e5e78a | ||
|
|
43daa2ef08 | ||
|
|
db6e813cba | ||
|
|
066a6d8b36 | ||
|
|
b9ca2ac13d | ||
|
|
65e43e8d04 | ||
|
|
5fee2438ab | ||
|
|
8b1c20cc11 | ||
|
|
0001e8ffc4 | ||
|
|
df5aacca42 | ||
|
|
1f53757318 | ||
|
|
090119147c | ||
|
|
1551694198 | ||
|
|
bef1832ac7 | ||
|
|
45e366745d | ||
|
|
1e634a8bba | ||
|
|
228a945da9 | ||
|
|
834297e675 | ||
|
|
3c9318d56a | ||
|
|
30d10b6f11 | ||
|
|
f8e39877b3 | ||
|
|
0a3f7d7ef7 | ||
|
|
222eb47bd2 | ||
|
|
c5b1f02d2b | ||
|
|
f81ab4d71a | ||
|
|
b88b9dabdb | ||
|
|
06b17e82db | ||
|
|
2add79a473 | ||
|
|
3a37e24496 | ||
|
|
b1d703bff3 | ||
|
|
a3a34a94e7 | ||
|
|
6edc365685 | ||
|
|
4b7f736af0 | ||
|
|
016fbaed36 | ||
|
|
bfcd137e52 | ||
|
|
f9af8fc912 | ||
|
|
bd083d632f | ||
|
|
bd9da07734 | ||
|
|
3dbbf296b8 | ||
|
|
50a8af4082 | ||
|
|
6a1125875b | ||
|
|
878c23892d | ||
|
|
248dab9289 | ||
|
|
4f0e3430c0 | ||
|
|
3347f400b8 | ||
|
|
6082308e20 | ||
|
|
5fce2a2c1c | ||
|
|
03b8ed5ce4 | ||
|
|
a625733885 |
@@ -1,8 +1,8 @@
|
|||||||
# Phpdoc dokuwiki template
|
# Phpdoc dokuwiki template
|
||||||
This directory contains a template for rendering iTop phpdoc as dokuwiki pages.
|
This directory contains a template rendering iTop phpdoc as wiki pages.
|
||||||
|
|
||||||
|
|
||||||
Conventional tags that you should use:
|
conventional tag that you should use:
|
||||||
* `@internal` : exclude from the documentation.
|
* `@internal` : exclude from the documentation.
|
||||||
* `@api` : it means that a method is an api, thus it may be interacted with.
|
* `@api` : it means that a method is an api, thus it may be interacted with.
|
||||||
* `@see` : it points to another documented method
|
* `@see` : it points to another documented method
|
||||||
@@ -14,7 +14,7 @@ Conventional tags that you should use:
|
|||||||
|
|
||||||
## Special instructions
|
## Special instructions
|
||||||
|
|
||||||
Some iTop specific tags were added :
|
some tags where added :
|
||||||
* `@api-advanced`: it means that a method is an `@api` but mark it also as "complex" to use
|
* `@api-advanced`: it means that a method is an `@api` but mark it also as "complex" to use
|
||||||
* `@overwritable-hook`: used to mark a method as "designed to be extended"
|
* `@overwritable-hook`: used to mark a method as "designed to be extended"
|
||||||
* `@extension-hook`: not used for now
|
* `@extension-hook`: not used for now
|
||||||
@@ -39,12 +39,12 @@ examples:
|
|||||||
#### Do not use inline tags, they do not work properly, example:
|
#### Do not use inline tags, they do not work properly, example:
|
||||||
```
|
```
|
||||||
/**
|
/**
|
||||||
* This is a texts with an inline tag {@see [FQSEN] [<description>]} it must never be used
|
* This is a texts with ans inline tag {@see [FQSEN] [<description>]} it must never be used
|
||||||
*/
|
*/
|
||||||
```
|
```
|
||||||
|
|
||||||
#### The `@example` tag must respect this very precise syntax
|
#### The `@example` tag must respect this very precise syntax
|
||||||
* the sentence in the first line (next to the tag) is the title, it must be enclosed by double quotes
|
* the sentence in the first line (next to the tag) is the title, it must be enclose by double quotes
|
||||||
* the following lines are the sample code.
|
* the following lines are the sample code.
|
||||||
* 💔 since we simply hack the official tag, this syntax must be respected carefully 💔
|
* 💔 since we simply hack the official tag, this syntax must be respected carefully 💔
|
||||||
example:
|
example:
|
||||||
@@ -82,15 +82,14 @@ Then, **for a method** of an eligible class:
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
```
|
```
|
||||||
cd .doc
|
|
||||||
composer require phpdocumentor/phpdocumentor:~2 --dev
|
composer require phpdocumentor/phpdocumentor:~2 --dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Generation
|
## Generation
|
||||||
`./bin/build-doc-object-manipulation` and `./bin/build-doc-extensions` contains examples of doc. generation, beware: they have to be called from the .doc directory:
|
`.doc/bin/build-doc-object-manipulation` and `.doc/bin/build-doc-extensions` contains examples of doc. generation, beware: they have to be called from iTop root directory:
|
||||||
```shell
|
```shell
|
||||||
cd /path/to/itop/.doc
|
cd /path/to/itop/
|
||||||
./bin/build-doc-object-manipulation
|
./.doc/bin/build-doc-object-manipulation
|
||||||
```
|
```
|
||||||
|
|
||||||
the resulting documentation is written into `data/phpdocumentor/output`
|
the resulting documentation is written into `data/phpdocumentor/output`
|
||||||
@@ -101,3 +100,4 @@ the resulting documentation is written into `data/phpdocumentor/output`
|
|||||||
* the generated files have to be placed under an arbitrary directory of `[/path/to/dokuwiki]/data/pages`.
|
* the generated files have to be placed under an arbitrary directory of `[/path/to/dokuwiki]/data/pages`.
|
||||||
* the html has to be activated [config:htmlok](https://www.dokuwiki.org/config:htmlok)
|
* the html has to be activated [config:htmlok](https://www.dokuwiki.org/config:htmlok)
|
||||||
* the generated files have to be in lowercase
|
* the generated files have to be in lowercase
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/bin/sh -x
|
#!/bin/sh -x
|
||||||
|
|
||||||
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/extensions/ && rm -rf data/phpdocumentor/temp/extensions/ && .doc/vendor/bin/phpdoc -c .doc/phpdoc-extensions.dist.xml -vvv
|
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/extensions/ && rm -rf data/phpdocumentor/temp/extensions/ && vendor/bin/phpdoc -c .doc/phpdoc-extensions.dist.xml -vvv
|
||||||
|
|
||||||
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
|
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
|
||||||
cd data/phpdocumentor/output/extensions/ && for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done
|
cd data/phpdocumentor/output/extensions/
|
||||||
|
for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/bin/sh -x
|
#!/bin/sh -x
|
||||||
|
|
||||||
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf ../data/phpdocumentor/output/objects-manipulation/ && rm -rf ../data/phpdocumentor/temp/objects-manipulation/ && ./vendor/bin/phpdoc -c ./phpdoc-objects-manipulation.dist.xml -vvv
|
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/objects-manipulation/ && rm -rf data/phpdocumentor/temp/objects-manipulation/ && vendor/bin/phpdoc -c .doc/phpdoc-objects-manipulation.dist.xml -vvv
|
||||||
|
|
||||||
|
|
||||||
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
|
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
|
||||||
cd ../data/phpdocumentor/output/objects-manipulation/ && for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done
|
cd data/phpdocumentor/output/objects-manipulation/
|
||||||
|
for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"require-dev": {
|
|
||||||
"phpdocumentor/phpdocumentor": "~2",
|
|
||||||
"jms/serializer": "1.7.*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
|
|
||||||
{{ structure.summary|raw }}
|
{{ structure.summary|raw }}
|
||||||
[[{{structureName}}|More information]]
|
[[{{structureName}}|More informations]]
|
||||||
|
|
||||||
</WRAP>{# group #}
|
</WRAP>{# group #}
|
||||||
|
|
||||||
|
|||||||
379
.editorconfig
379
.editorconfig
@@ -1,3 +1,5 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
@@ -11,16 +13,18 @@ ij_formatter_off_tag = @formatter:off
|
|||||||
ij_formatter_on_tag = @formatter:on
|
ij_formatter_on_tag = @formatter:on
|
||||||
ij_formatter_tags_enabled = false
|
ij_formatter_tags_enabled = false
|
||||||
ij_smart_tabs = false
|
ij_smart_tabs = false
|
||||||
ij_visual_guides = 140
|
ij_visual_guides = 80,120
|
||||||
ij_wrap_on_typing = true
|
ij_wrap_on_typing = true
|
||||||
|
|
||||||
[*.css]
|
[*.css]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
ij_smart_tabs = true
|
ij_smart_tabs = true
|
||||||
|
ij_visual_guides = none
|
||||||
ij_css_align_closing_brace_with_properties = false
|
ij_css_align_closing_brace_with_properties = false
|
||||||
ij_css_blank_lines_around_nested_selector = 1
|
ij_css_blank_lines_around_nested_selector = 1
|
||||||
ij_css_blank_lines_between_blocks = 1
|
ij_css_blank_lines_between_blocks = 1
|
||||||
ij_css_brace_placement = 0
|
ij_css_brace_placement = end_of_line
|
||||||
|
ij_css_enforce_quotes_on_format = false
|
||||||
ij_css_hex_color_long_format = false
|
ij_css_hex_color_long_format = false
|
||||||
ij_css_hex_color_lower_case = false
|
ij_css_hex_color_lower_case = false
|
||||||
ij_css_hex_color_short_format = false
|
ij_css_hex_color_short_format = false
|
||||||
@@ -31,59 +35,18 @@ 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_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_after_colon = true
|
||||||
ij_css_space_before_opening_brace = true
|
ij_css_space_before_opening_brace = true
|
||||||
ij_css_value_alignment = 0
|
ij_css_use_double_quotes = true
|
||||||
|
ij_css_value_alignment = do_not_align
|
||||||
[*.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]
|
[*.scss]
|
||||||
indent_style = tab
|
indent_size = 2
|
||||||
|
tab_width = 2
|
||||||
|
ij_visual_guides = none
|
||||||
ij_scss_align_closing_brace_with_properties = false
|
ij_scss_align_closing_brace_with_properties = false
|
||||||
ij_scss_blank_lines_around_nested_selector = 1
|
ij_scss_blank_lines_around_nested_selector = 1
|
||||||
ij_scss_blank_lines_between_blocks = 1
|
ij_scss_blank_lines_between_blocks = 1
|
||||||
ij_scss_brace_placement = 0
|
ij_scss_brace_placement = 0
|
||||||
|
ij_scss_enforce_quotes_on_format = false
|
||||||
ij_scss_hex_color_long_format = false
|
ij_scss_hex_color_long_format = false
|
||||||
ij_scss_hex_color_lower_case = false
|
ij_scss_hex_color_lower_case = false
|
||||||
ij_scss_hex_color_short_format = false
|
ij_scss_hex_color_short_format = false
|
||||||
@@ -94,17 +57,20 @@ 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_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_after_colon = true
|
||||||
ij_scss_space_before_opening_brace = true
|
ij_scss_space_before_opening_brace = true
|
||||||
|
ij_scss_use_double_quotes = true
|
||||||
ij_scss_value_alignment = 0
|
ij_scss_value_alignment = 0
|
||||||
|
|
||||||
[*.twig]
|
[*.twig]
|
||||||
indent_style = tab
|
|
||||||
ij_smart_tabs = true
|
ij_smart_tabs = true
|
||||||
|
ij_visual_guides = none
|
||||||
ij_wrap_on_typing = false
|
ij_wrap_on_typing = false
|
||||||
ij_twig_keep_indents_on_empty_lines = 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_delimiters = true
|
||||||
ij_twig_spaces_inside_variable_delimiters = true
|
ij_twig_spaces_inside_variable_delimiters = true
|
||||||
|
|
||||||
[.editorconfig]
|
[.editorconfig]
|
||||||
|
ij_visual_guides = none
|
||||||
ij_editorconfig_align_group_field_declarations = false
|
ij_editorconfig_align_group_field_declarations = false
|
||||||
ij_editorconfig_space_after_colon = false
|
ij_editorconfig_space_after_colon = false
|
||||||
ij_editorconfig_space_after_comma = true
|
ij_editorconfig_space_after_comma = true
|
||||||
@@ -112,10 +78,44 @@ ij_editorconfig_space_before_colon = false
|
|||||||
ij_editorconfig_space_before_comma = false
|
ij_editorconfig_space_before_comma = false
|
||||||
ij_editorconfig_spaces_around_assignment_operators = true
|
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 = false
|
||||||
|
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
|
||||||
|
|
||||||
[{*.cjs,*.js}]
|
[{*.cjs,*.js}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
ij_continuation_indent_size = 4
|
ij_continuation_indent_size = 4
|
||||||
ij_smart_tabs = true
|
ij_smart_tabs = true
|
||||||
|
ij_visual_guides = none
|
||||||
ij_javascript_align_imports = false
|
ij_javascript_align_imports = false
|
||||||
ij_javascript_align_multiline_array_initializer_expression = false
|
ij_javascript_align_multiline_array_initializer_expression = false
|
||||||
ij_javascript_align_multiline_binary_operation = false
|
ij_javascript_align_multiline_binary_operation = false
|
||||||
@@ -134,13 +134,13 @@ ij_javascript_array_initializer_wrap = off
|
|||||||
ij_javascript_assignment_wrap = off
|
ij_javascript_assignment_wrap = off
|
||||||
ij_javascript_binary_operation_sign_on_next_line = false
|
ij_javascript_binary_operation_sign_on_next_line = false
|
||||||
ij_javascript_binary_operation_wrap = off
|
ij_javascript_binary_operation_wrap = off
|
||||||
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**/*
|
ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/**
|
||||||
ij_javascript_blank_lines_after_imports = 1
|
ij_javascript_blank_lines_after_imports = 1
|
||||||
ij_javascript_blank_lines_around_class = 1
|
ij_javascript_blank_lines_around_class = 1
|
||||||
ij_javascript_blank_lines_around_field = 0
|
ij_javascript_blank_lines_around_field = 0
|
||||||
ij_javascript_blank_lines_around_function = 1
|
ij_javascript_blank_lines_around_function = 1
|
||||||
ij_javascript_blank_lines_around_method = 1
|
ij_javascript_blank_lines_around_method = 1
|
||||||
ij_javascript_block_brace_style = next_line
|
ij_javascript_block_brace_style = end_of_line
|
||||||
ij_javascript_call_parameters_new_line_after_left_paren = false
|
ij_javascript_call_parameters_new_line_after_left_paren = false
|
||||||
ij_javascript_call_parameters_right_paren_on_new_line = false
|
ij_javascript_call_parameters_right_paren_on_new_line = false
|
||||||
ij_javascript_call_parameters_wrap = off
|
ij_javascript_call_parameters_wrap = off
|
||||||
@@ -148,15 +148,15 @@ ij_javascript_catch_on_new_line = false
|
|||||||
ij_javascript_chained_call_dot_on_new_line = true
|
ij_javascript_chained_call_dot_on_new_line = true
|
||||||
ij_javascript_class_brace_style = end_of_line
|
ij_javascript_class_brace_style = end_of_line
|
||||||
ij_javascript_comma_on_new_line = false
|
ij_javascript_comma_on_new_line = false
|
||||||
ij_javascript_do_while_brace_force = never
|
ij_javascript_do_while_brace_force = always
|
||||||
ij_javascript_else_on_new_line = true
|
ij_javascript_else_on_new_line = false
|
||||||
ij_javascript_enforce_trailing_comma = keep
|
ij_javascript_enforce_trailing_comma = keep
|
||||||
ij_javascript_extends_keyword_wrap = off
|
ij_javascript_extends_keyword_wrap = off
|
||||||
ij_javascript_extends_list_wrap = off
|
ij_javascript_extends_list_wrap = off
|
||||||
ij_javascript_field_prefix = _
|
ij_javascript_field_prefix = _
|
||||||
ij_javascript_file_name_style = relaxed
|
ij_javascript_file_name_style = relaxed
|
||||||
ij_javascript_finally_on_new_line = false
|
ij_javascript_finally_on_new_line = false
|
||||||
ij_javascript_for_brace_force = never
|
ij_javascript_for_brace_force = always
|
||||||
ij_javascript_for_statement_new_line_after_left_paren = false
|
ij_javascript_for_statement_new_line_after_left_paren = false
|
||||||
ij_javascript_for_statement_right_paren_on_new_line = false
|
ij_javascript_for_statement_right_paren_on_new_line = false
|
||||||
ij_javascript_for_statement_wrap = off
|
ij_javascript_for_statement_wrap = off
|
||||||
@@ -192,6 +192,9 @@ ij_javascript_parentheses_expression_new_line_after_left_paren = false
|
|||||||
ij_javascript_parentheses_expression_right_paren_on_new_line = false
|
ij_javascript_parentheses_expression_right_paren_on_new_line = false
|
||||||
ij_javascript_place_assignment_sign_on_next_line = false
|
ij_javascript_place_assignment_sign_on_next_line = false
|
||||||
ij_javascript_prefer_as_type_cast = 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_prefer_parameters_wrap = false
|
||||||
ij_javascript_reformat_c_style_comments = false
|
ij_javascript_reformat_c_style_comments = false
|
||||||
ij_javascript_space_after_colon = true
|
ij_javascript_space_after_colon = true
|
||||||
@@ -272,11 +275,11 @@ ij_javascript_use_path_mapping = always
|
|||||||
ij_javascript_use_public_modifier = false
|
ij_javascript_use_public_modifier = false
|
||||||
ij_javascript_use_semicolon_after_statement = true
|
ij_javascript_use_semicolon_after_statement = true
|
||||||
ij_javascript_var_declaration_wrap = normal
|
ij_javascript_var_declaration_wrap = normal
|
||||||
ij_javascript_while_brace_force = never
|
ij_javascript_while_brace_force = always
|
||||||
ij_javascript_while_on_new_line = false
|
ij_javascript_while_on_new_line = false
|
||||||
ij_javascript_wrap_comments = false
|
ij_javascript_wrap_comments = false
|
||||||
|
|
||||||
[{*.module,*.hphp,*.phtml,*.php5,*.php4,*.php,*.ctp,*.inc}]
|
[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
ij_continuation_indent_size = 4
|
ij_continuation_indent_size = 4
|
||||||
ij_smart_tabs = true
|
ij_smart_tabs = true
|
||||||
@@ -296,12 +299,14 @@ ij_php_align_multiline_parameters_in_calls = false
|
|||||||
ij_php_align_multiline_ternary_operation = false
|
ij_php_align_multiline_ternary_operation = false
|
||||||
ij_php_align_phpdoc_comments = false
|
ij_php_align_phpdoc_comments = false
|
||||||
ij_php_align_phpdoc_param_names = false
|
ij_php_align_phpdoc_param_names = false
|
||||||
|
ij_php_anonymous_brace_style = end_of_line
|
||||||
ij_php_api_weight = 1
|
ij_php_api_weight = 1
|
||||||
ij_php_array_initializer_new_line_after_left_brace = true
|
ij_php_array_initializer_new_line_after_left_brace = true
|
||||||
ij_php_array_initializer_right_brace_on_new_line = true
|
ij_php_array_initializer_right_brace_on_new_line = true
|
||||||
ij_php_array_initializer_wrap = on_every_item
|
ij_php_array_initializer_wrap = on_every_item
|
||||||
ij_php_assignment_wrap = off
|
ij_php_assignment_wrap = off
|
||||||
ij_php_author_weight = 7
|
ij_php_attributes_wrap = off
|
||||||
|
ij_php_author_weight = 8
|
||||||
ij_php_binary_operation_sign_on_next_line = false
|
ij_php_binary_operation_sign_on_next_line = false
|
||||||
ij_php_binary_operation_wrap = off
|
ij_php_binary_operation_wrap = off
|
||||||
ij_php_blank_lines_after_class_header = 0
|
ij_php_blank_lines_after_class_header = 0
|
||||||
@@ -318,7 +323,8 @@ ij_php_blank_lines_before_imports = 1
|
|||||||
ij_php_blank_lines_before_method_body = 0
|
ij_php_blank_lines_before_method_body = 0
|
||||||
ij_php_blank_lines_before_package = 1
|
ij_php_blank_lines_before_package = 1
|
||||||
ij_php_blank_lines_before_return_statement = 1
|
ij_php_blank_lines_before_return_statement = 1
|
||||||
ij_php_block_brace_style = next_line
|
ij_php_blank_lines_between_imports = 0
|
||||||
|
ij_php_block_brace_style = end_of_line
|
||||||
ij_php_call_parameters_new_line_after_left_paren = false
|
ij_php_call_parameters_new_line_after_left_paren = false
|
||||||
ij_php_call_parameters_right_paren_on_new_line = false
|
ij_php_call_parameters_right_paren_on_new_line = false
|
||||||
ij_php_call_parameters_wrap = normal
|
ij_php_call_parameters_wrap = normal
|
||||||
@@ -328,11 +334,11 @@ ij_php_class_brace_style = next_line
|
|||||||
ij_php_comma_after_last_array_element = true
|
ij_php_comma_after_last_array_element = true
|
||||||
ij_php_concat_spaces = false
|
ij_php_concat_spaces = false
|
||||||
ij_php_copyright_weight = 28
|
ij_php_copyright_weight = 28
|
||||||
ij_php_deprecated_weight = 28
|
ij_php_deprecated_weight = 2
|
||||||
ij_php_do_while_brace_force = always
|
ij_php_do_while_brace_force = always
|
||||||
ij_php_else_if_style = as_is
|
ij_php_else_if_style = as_is
|
||||||
ij_php_else_on_new_line = true
|
ij_php_else_on_new_line = false
|
||||||
ij_php_example_weight = 3
|
ij_php_example_weight = 4
|
||||||
ij_php_extends_keyword_wrap = off
|
ij_php_extends_keyword_wrap = off
|
||||||
ij_php_extends_list_wrap = off
|
ij_php_extends_list_wrap = off
|
||||||
ij_php_fields_default_visibility = private
|
ij_php_fields_default_visibility = private
|
||||||
@@ -343,6 +349,8 @@ ij_php_for_statement_new_line_after_left_paren = false
|
|||||||
ij_php_for_statement_right_paren_on_new_line = false
|
ij_php_for_statement_right_paren_on_new_line = false
|
||||||
ij_php_for_statement_wrap = off
|
ij_php_for_statement_wrap = off
|
||||||
ij_php_force_short_declaration_array_style = false
|
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_global_weight = 28
|
||||||
ij_php_group_use_wrap = on_every_item
|
ij_php_group_use_wrap = on_every_item
|
||||||
ij_php_if_brace_force = always
|
ij_php_if_brace_force = always
|
||||||
@@ -362,7 +370,8 @@ ij_php_keep_control_statement_in_one_line = true
|
|||||||
ij_php_keep_first_column_comment = true
|
ij_php_keep_first_column_comment = true
|
||||||
ij_php_keep_indents_on_empty_lines = false
|
ij_php_keep_indents_on_empty_lines = false
|
||||||
ij_php_keep_line_breaks = true
|
ij_php_keep_line_breaks = true
|
||||||
ij_php_keep_rparen_and_lbrace_on_one_line = true
|
ij_php_keep_rparen_and_lbrace_on_one_line = false
|
||||||
|
ij_php_keep_simple_classes_in_one_line = false
|
||||||
ij_php_keep_simple_methods_in_one_line = false
|
ij_php_keep_simple_methods_in_one_line = false
|
||||||
ij_php_lambda_brace_style = end_of_line
|
ij_php_lambda_brace_style = end_of_line
|
||||||
ij_php_license_weight = 28
|
ij_php_license_weight = 28
|
||||||
@@ -370,6 +379,7 @@ ij_php_line_comment_add_space = false
|
|||||||
ij_php_line_comment_at_first_column = true
|
ij_php_line_comment_at_first_column = true
|
||||||
ij_php_link_weight = 28
|
ij_php_link_weight = 28
|
||||||
ij_php_lower_case_boolean_const = true
|
ij_php_lower_case_boolean_const = true
|
||||||
|
ij_php_lower_case_keywords = true
|
||||||
ij_php_lower_case_null_const = true
|
ij_php_lower_case_null_const = true
|
||||||
ij_php_method_brace_style = next_line
|
ij_php_method_brace_style = next_line
|
||||||
ij_php_method_call_chain_wrap = off
|
ij_php_method_call_chain_wrap = off
|
||||||
@@ -380,9 +390,11 @@ ij_php_method_weight = 28
|
|||||||
ij_php_modifier_list_wrap = false
|
ij_php_modifier_list_wrap = false
|
||||||
ij_php_multiline_chained_calls_semicolon_on_new_line = false
|
ij_php_multiline_chained_calls_semicolon_on_new_line = false
|
||||||
ij_php_namespace_brace_style = 1
|
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_null_type_position = in_the_end
|
||||||
ij_php_package_weight = 28
|
ij_php_package_weight = 28
|
||||||
ij_php_param_weight = 4
|
ij_php_param_weight = 5
|
||||||
|
ij_php_parameters_attributes_wrap = off
|
||||||
ij_php_parentheses_expression_new_line_after_left_paren = false
|
ij_php_parentheses_expression_new_line_after_left_paren = false
|
||||||
ij_php_parentheses_expression_right_paren_on_new_line = false
|
ij_php_parentheses_expression_right_paren_on_new_line = false
|
||||||
ij_php_phpdoc_blank_line_before_tags = true
|
ij_php_phpdoc_blank_line_before_tags = true
|
||||||
@@ -399,11 +411,12 @@ ij_php_property_read_weight = 28
|
|||||||
ij_php_property_weight = 28
|
ij_php_property_weight = 28
|
||||||
ij_php_property_write_weight = 28
|
ij_php_property_write_weight = 28
|
||||||
ij_php_return_type_on_new_line = false
|
ij_php_return_type_on_new_line = false
|
||||||
ij_php_return_weight = 5
|
ij_php_return_weight = 6
|
||||||
ij_php_see_weight = 2
|
ij_php_see_weight = 3
|
||||||
ij_php_since_weight = 28
|
ij_php_since_weight = 28
|
||||||
ij_php_sort_phpdoc_elements = true
|
ij_php_sort_phpdoc_elements = true
|
||||||
ij_php_space_after_colon = 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_colon_in_return_type = true
|
||||||
ij_php_space_after_comma = true
|
ij_php_space_after_comma = true
|
||||||
ij_php_space_after_for_semicolon = true
|
ij_php_space_after_for_semicolon = true
|
||||||
@@ -417,6 +430,7 @@ ij_php_space_before_catch_parentheses = true
|
|||||||
ij_php_space_before_class_left_brace = true
|
ij_php_space_before_class_left_brace = true
|
||||||
ij_php_space_before_closure_left_parenthesis = true
|
ij_php_space_before_closure_left_parenthesis = true
|
||||||
ij_php_space_before_colon = 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_colon_in_return_type = false
|
||||||
ij_php_space_before_comma = false
|
ij_php_space_before_comma = false
|
||||||
ij_php_space_before_do_left_brace = true
|
ij_php_space_before_do_left_brace = true
|
||||||
@@ -433,6 +447,7 @@ ij_php_space_before_method_call_parentheses = false
|
|||||||
ij_php_space_before_method_left_brace = true
|
ij_php_space_before_method_left_brace = true
|
||||||
ij_php_space_before_method_parentheses = false
|
ij_php_space_before_method_parentheses = false
|
||||||
ij_php_space_before_quest = true
|
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_left_brace = true
|
||||||
ij_php_space_before_switch_parentheses = true
|
ij_php_space_before_switch_parentheses = true
|
||||||
ij_php_space_before_try_left_brace = true
|
ij_php_space_before_try_left_brace = true
|
||||||
@@ -465,11 +480,11 @@ ij_php_spaces_within_parentheses = false
|
|||||||
ij_php_spaces_within_short_echo_tags = true
|
ij_php_spaces_within_short_echo_tags = true
|
||||||
ij_php_spaces_within_switch_parentheses = false
|
ij_php_spaces_within_switch_parentheses = false
|
||||||
ij_php_spaces_within_while_parentheses = false
|
ij_php_spaces_within_while_parentheses = false
|
||||||
ij_php_special_else_if_treatment = false
|
ij_php_special_else_if_treatment = true
|
||||||
ij_php_subpackage_weight = 28
|
ij_php_subpackage_weight = 28
|
||||||
ij_php_ternary_operation_signs_on_next_line = false
|
ij_php_ternary_operation_signs_on_next_line = false
|
||||||
ij_php_ternary_operation_wrap = off
|
ij_php_ternary_operation_wrap = off
|
||||||
ij_php_throws_weight = 6
|
ij_php_throws_weight = 7
|
||||||
ij_php_todo_weight = 28
|
ij_php_todo_weight = 28
|
||||||
ij_php_unknown_tag_weight = 28
|
ij_php_unknown_tag_weight = 28
|
||||||
ij_php_upper_case_boolean_const = false
|
ij_php_upper_case_boolean_const = false
|
||||||
@@ -481,10 +496,25 @@ ij_php_version_weight = 28
|
|||||||
ij_php_while_brace_force = always
|
ij_php_while_brace_force = always
|
||||||
ij_php_while_on_new_line = false
|
ij_php_while_on_new_line = false
|
||||||
|
|
||||||
[{*.sht,*.htm,*.html,*.shtm,*.shtml}]
|
[{*.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}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
ij_smart_tabs = true
|
ij_smart_tabs = true
|
||||||
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
ij_visual_guides = none
|
||||||
|
ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
|
||||||
ij_html_align_attributes = true
|
ij_html_align_attributes = true
|
||||||
ij_html_align_text = false
|
ij_html_align_text = false
|
||||||
ij_html_attribute_wrap = normal
|
ij_html_attribute_wrap = normal
|
||||||
@@ -503,209 +533,24 @@ ij_html_keep_whitespaces_inside = span,pre,textarea
|
|||||||
ij_html_line_comment_at_first_column = true
|
ij_html_line_comment_at_first_column = true
|
||||||
ij_html_new_line_after_last_attribute = never
|
ij_html_new_line_after_last_attribute = never
|
||||||
ij_html_new_line_before_first_attribute = never
|
ij_html_new_line_before_first_attribute = never
|
||||||
ij_html_quote_style = double
|
ij_html_quote_style = none
|
||||||
ij_html_remove_new_line_before_tags = br
|
ij_html_remove_new_line_before_tags = br
|
||||||
ij_html_space_after_tag_name = false
|
ij_html_space_after_tag_name = false
|
||||||
ij_html_space_around_equality_in_attribute = false
|
ij_html_space_around_equality_in_attribute = false
|
||||||
ij_html_space_inside_empty_tag = false
|
ij_html_space_inside_empty_tag = false
|
||||||
ij_html_text_wrap = normal
|
ij_html_text_wrap = normal
|
||||||
|
ij_html_uniform_ident = false
|
||||||
|
|
||||||
[{*.ts,*.ats}]
|
[{*.yaml, *.yml}]
|
||||||
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
|
|
||||||
|
|
||||||
[{*.yml,*.yaml}]
|
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
ij_continuation_indent_size = 2
|
ij_visual_guides = none
|
||||||
|
ij_yaml_align_values_properties = do_not_align
|
||||||
|
ij_yaml_autoinsert_sequence_marker = true
|
||||||
|
ij_yaml_block_mapping_on_new_line = false
|
||||||
|
ij_yaml_indent_sequence_value = true
|
||||||
ij_yaml_keep_indents_on_empty_lines = false
|
ij_yaml_keep_indents_on_empty_lines = false
|
||||||
ij_yaml_keep_line_breaks = true
|
ij_yaml_keep_line_breaks = true
|
||||||
|
ij_yaml_sequence_on_new_line = false
|
||||||
[{*.zsh,*.bash,*.sh}]
|
ij_yaml_space_before_colon = false
|
||||||
ij_shell_binary_ops_start_line = false
|
ij_yaml_spaces_within_braces = true
|
||||||
ij_shell_keep_column_alignment_padding = false
|
ij_yaml_spaces_within_brackets = true
|
||||||
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
|
|
||||||
|
|||||||
9
.gitflow
9
.gitflow
@@ -1,9 +0,0 @@
|
|||||||
[gitflow "branch"]
|
|
||||||
master = master
|
|
||||||
develop = develop
|
|
||||||
[gitflow "prefix"]
|
|
||||||
feature = feature/
|
|
||||||
release = release/
|
|
||||||
hotfix = hotfix/
|
|
||||||
versiontag =
|
|
||||||
support = support/
|
|
||||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -3,19 +3,15 @@
|
|||||||
/toolkit
|
/toolkit
|
||||||
/env-*
|
/env-*
|
||||||
|
|
||||||
# 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"
|
# composer reserver directory, from sources, populate/update using "composer install"
|
||||||
vendor/*
|
vendor/*
|
||||||
test/vendor/*
|
test/vendor/*
|
||||||
|
|
||||||
|
# all conf but listing prevention
|
||||||
|
/conf/**
|
||||||
|
!/conf/.htaccess
|
||||||
|
!/conf/web.config
|
||||||
|
|
||||||
# all datas but listing prevention
|
# all datas but listing prevention
|
||||||
/data/**
|
/data/**
|
||||||
!/data/.htaccess
|
!/data/.htaccess
|
||||||
|
|||||||
34
.idea/codeStyles/Project.xml
generated
34
.idea/codeStyles/Project.xml
generated
@@ -1,56 +1,37 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
<code_scheme name="Project" version="173">
|
<code_scheme name="Project" version="173">
|
||||||
<option name="LINE_SEPARATOR" value=" " />
|
<option name="LINE_SEPARATOR" value=" " />
|
||||||
<option name="RIGHT_MARGIN" value="140" />
|
<option name="RIGHT_MARGIN" value="320" />
|
||||||
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
|
|
||||||
<option name="SOFT_MARGINS" value="140" />
|
|
||||||
<HTMLCodeStyleSettings>
|
<HTMLCodeStyleSettings>
|
||||||
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="html,body,thead,tbody,tfoot,style,script,head" />
|
<option name="HTML_DO_NOT_INDENT_CHILDREN_OF" value="html,body,thead,tbody,tfoot,script,style" />
|
||||||
|
<option name="HTML_DO_NOT_ALIGN_CHILDREN_OF_MIN_LINES" value="4" />
|
||||||
</HTMLCodeStyleSettings>
|
</HTMLCodeStyleSettings>
|
||||||
<JSCodeStyleSettings version="0">
|
|
||||||
<option name="USE_CHAINED_CALLS_GROUP_INDENTS" value="true" />
|
|
||||||
</JSCodeStyleSettings>
|
|
||||||
<PHPCodeStyleSettings>
|
<PHPCodeStyleSettings>
|
||||||
<option name="CONCAT_SPACES" value="false" />
|
<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_LINE_BEFORE_TAGS" value="true" />
|
||||||
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
|
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
|
||||||
<option name="PHPDOC_WRAP_LONG_LINES" 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_BOOLEAN_CONST" value="true" />
|
||||||
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
||||||
<option name="BLANK_LINES_BEFORE_RETURN_STATEMENT" value="1" />
|
<option name="BLANK_LINES_BEFORE_RETURN_STATEMENT" value="1" />
|
||||||
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
|
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
|
||||||
<option name="PHPDOC_USE_FQCN" value="true" />
|
<option name="PHPDOC_USE_FQCN" value="true" />
|
||||||
</PHPCodeStyleSettings>
|
</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">
|
<codeStyleSettings language="JavaScript">
|
||||||
<option name="BRACE_STYLE" value="2" />
|
<option name="BRACE_STYLE" value="2" />
|
||||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||||
<option name="SPACE_AROUND_ADDITIVE_OPERATORS" value="false" />
|
|
||||||
<option name="IF_BRACE_FORCE" value="3" />
|
<option name="IF_BRACE_FORCE" value="3" />
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="USE_TAB_CHARACTER" value="true" />
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="PHP">
|
<codeStyleSettings language="PHP">
|
||||||
|
<option name="RIGHT_MARGIN" value="320" />
|
||||||
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
|
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
|
||||||
<option name="BRACE_STYLE" value="2" />
|
<option name="BRACE_STYLE" value="2" />
|
||||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
<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="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||||
|
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||||
<option name="METHOD_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_LPAREN_ON_NEXT_LINE" value="true" />
|
||||||
@@ -67,13 +48,10 @@
|
|||||||
<option name="SMART_TABS" value="true" />
|
<option name="SMART_TABS" value="true" />
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="SCSS">
|
<codeStyleSettings language="XML">
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="USE_TAB_CHARACTER" value="true" />
|
<option name="USE_TAB_CHARACTER" value="true" />
|
||||||
</indentOptions>
|
</indentOptions>
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="XML">
|
|
||||||
<option name="WRAP_ON_TYPING" value="1" />
|
|
||||||
</codeStyleSettings>
|
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
||||||
4
.idea/inspectionProfiles/Combodo.xml
generated
4
.idea/inspectionProfiles/Combodo.xml
generated
@@ -1,14 +1,10 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
<component name="InspectionProjectProfileManager">
|
||||||
<profile version="1.0">
|
<profile version="1.0">
|
||||||
<option name="myName" value="Combodo" />
|
<option name="myName" value="Combodo" />
|
||||||
<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="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
<inspection_tool class="MysqlParsingInspection" 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="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="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">
|
<inspection_tool class="PhpTooManyParametersInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
<option name="limit" value="7" />
|
<option name="limit" value="7" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/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
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#!/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
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -x
|
|
||||||
|
|
||||||
whoami
|
|
||||||
pwd
|
|
||||||
ls
|
|
||||||
|
|
||||||
echo "$BRANCH_NAME:${BRANCH_NAME}"
|
|
||||||
|
|
||||||
echo "printenv :"
|
|
||||||
printenv
|
|
||||||
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -x
|
|
||||||
|
|
||||||
cd test
|
|
||||||
|
|
||||||
export DEBUG_UNIT_TEST="0"
|
|
||||||
|
|
||||||
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -x
|
|
||||||
|
|
||||||
cd toolkit
|
|
||||||
php unattended_install.php default-params.xml
|
|
||||||
@@ -1,281 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Configuration file, generated by the iTop configuration wizard
|
|
||||||
*
|
|
||||||
* The file is used in MetaModel::LoadConfig() which does all the necessary initialization job
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
$MySettings = array(
|
|
||||||
|
|
||||||
// access_message: Message displayed to the users when there is any access restriction
|
|
||||||
// default: 'iTop is temporarily frozen, please wait... (the admin team)'
|
|
||||||
'access_message' => 'iTop is temporarily frozen, please wait... (the admin team)',
|
|
||||||
|
|
||||||
// access_mode: Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3
|
|
||||||
// default: 3
|
|
||||||
'access_mode' => 3,
|
|
||||||
|
|
||||||
'allowed_login_types' => 'form|basic|external',
|
|
||||||
|
|
||||||
// apc_cache.enabled: If set, the APC cache is allowed (the PHP extension must also be active)
|
|
||||||
// default: true
|
|
||||||
'apc_cache.enabled' => true,
|
|
||||||
|
|
||||||
// apc_cache.query_ttl: Time to live set in APC for the prepared queries (seconds - 0 means no timeout)
|
|
||||||
// default: 3600
|
|
||||||
'apc_cache.query_ttl' => 3600,
|
|
||||||
|
|
||||||
// app_root_url: Root URL used for navigating within the application, or from an email to the application (you can put $SERVER_NAME$ as a placeholder for the server's name)
|
|
||||||
// default: ''
|
|
||||||
'app_root_url' => 'http://127.0.0.1/itop/svn/trunk/',
|
|
||||||
|
|
||||||
// buttons_position: Position of the forms buttons: bottom | top | both
|
|
||||||
// default: 'both'
|
|
||||||
'buttons_position' => 'both',
|
|
||||||
|
|
||||||
// cas_include_path: The path where to find the phpCAS library
|
|
||||||
// default: '/usr/share/php'
|
|
||||||
'cas_include_path' => '/usr/share/php',
|
|
||||||
|
|
||||||
// cron_max_execution_time: Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout
|
|
||||||
// default: 600
|
|
||||||
'cron_max_execution_time' => 600,
|
|
||||||
|
|
||||||
// csv_file_default_charset: Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).
|
|
||||||
// default: 'ISO-8859-1'
|
|
||||||
'csv_file_default_charset' => 'ISO-8859-1',
|
|
||||||
|
|
||||||
'csv_import_charsets' => array (
|
|
||||||
),
|
|
||||||
|
|
||||||
// csv_import_history_display: Display the history tab in the import wizard
|
|
||||||
// default: false
|
|
||||||
'csv_import_history_display' => false,
|
|
||||||
|
|
||||||
// date_and_time_format: Format for date and time display (per language)
|
|
||||||
// default: array (
|
|
||||||
// 'default' =>
|
|
||||||
// array (
|
|
||||||
// 'date' => 'Y-m-d',
|
|
||||||
// 'time' => 'H:i:s',
|
|
||||||
// 'date_time' => '$date $time',
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
'date_and_time_format' => array (
|
|
||||||
'default' =>
|
|
||||||
array (
|
|
||||||
'date' => 'Y-m-d',
|
|
||||||
'time' => 'H:i:s',
|
|
||||||
'date_time' => '$date $time',
|
|
||||||
),
|
|
||||||
'FR FR' =>
|
|
||||||
array (
|
|
||||||
'date' => 'd/m/Y',
|
|
||||||
'time' => 'H:i:s',
|
|
||||||
'date_time' => '$date $time',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
'db_host' => '',
|
|
||||||
|
|
||||||
'db_name' => 'itop_ci_main',
|
|
||||||
|
|
||||||
'db_pwd' => 'IKnowYouSeeMeInJenkinsConf',
|
|
||||||
|
|
||||||
'db_subname' => '',
|
|
||||||
|
|
||||||
'db_user' => 'jenkins_itop',
|
|
||||||
|
|
||||||
// deadline_format: The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$
|
|
||||||
// default: '$difference$'
|
|
||||||
'deadline_format' => '$difference$',
|
|
||||||
|
|
||||||
'default_language' => 'EN US',
|
|
||||||
|
|
||||||
// draft_attachments_lifetime: Lifetime (in seconds) of drafts' attachments and inline images: after this duration, the garbage collector will delete them.
|
|
||||||
// default: 3600
|
|
||||||
'draft_attachments_lifetime' => 3600,
|
|
||||||
|
|
||||||
// email_asynchronous: If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode
|
|
||||||
// default: false
|
|
||||||
'email_asynchronous' => false,
|
|
||||||
|
|
||||||
// email_default_sender_address: Default address provided in the email from header field.
|
|
||||||
// default: ''
|
|
||||||
'email_default_sender_address' => '',
|
|
||||||
|
|
||||||
// email_default_sender_label: Default label provided in the email from header field.
|
|
||||||
// default: ''
|
|
||||||
'email_default_sender_label' => '',
|
|
||||||
|
|
||||||
// email_transport: Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)
|
|
||||||
// default: 'PHPMail'
|
|
||||||
'email_transport' => 'SMTP',
|
|
||||||
|
|
||||||
// email_transport_smtp.host: host name or IP address (optional)
|
|
||||||
// default: 'localhost'
|
|
||||||
'email_transport_smtp.host' => 'smtp.combodo.com',
|
|
||||||
|
|
||||||
// email_transport_smtp.password: Authentication password (optional)
|
|
||||||
// default: ''
|
|
||||||
'email_transport_smtp.password' => 'IDoNotWork',
|
|
||||||
|
|
||||||
// email_transport_smtp.port: port number (optional)
|
|
||||||
// default: 25
|
|
||||||
'email_transport_smtp.port' => 25,
|
|
||||||
|
|
||||||
// email_transport_smtp.username: Authentication user (optional)
|
|
||||||
// default: ''
|
|
||||||
'email_transport_smtp.username' => 'test2@combodo.com',
|
|
||||||
|
|
||||||
// email_validation_pattern: Regular expression to validate/detect the format of an eMail address
|
|
||||||
// default: '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}'
|
|
||||||
'email_validation_pattern' => '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}',
|
|
||||||
|
|
||||||
'encryption_key' => '@iT0pEncr1pti0n!',
|
|
||||||
|
|
||||||
'ext_auth_variable' => '$_SERVER[\'REMOTE_USER\']',
|
|
||||||
|
|
||||||
'fast_reload_interval' => '60',
|
|
||||||
|
|
||||||
// graphviz_path: Path to the Graphviz "dot" executable for graphing objects lifecycle
|
|
||||||
// default: '/usr/bin/dot'
|
|
||||||
'graphviz_path' => '/usr/bin/dot',
|
|
||||||
|
|
||||||
// inline_image_max_display_width: The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.
|
|
||||||
// default: '250'
|
|
||||||
'inline_image_max_display_width' => 250,
|
|
||||||
|
|
||||||
// inline_image_max_storage_width: The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.
|
|
||||||
// default: '1600'
|
|
||||||
'inline_image_max_storage_width' => 1600,
|
|
||||||
|
|
||||||
// link_set_attribute_qualifier: Link set from string: attribute qualifier (encloses both the attcode and the value)
|
|
||||||
// default: '\''
|
|
||||||
'link_set_attribute_qualifier' => '\'',
|
|
||||||
|
|
||||||
// link_set_attribute_separator: Link set from string: attribute separator
|
|
||||||
// default: ';'
|
|
||||||
'link_set_attribute_separator' => ';',
|
|
||||||
|
|
||||||
// link_set_item_separator: Link set from string: line separator
|
|
||||||
// default: '|'
|
|
||||||
'link_set_item_separator' => '|',
|
|
||||||
|
|
||||||
// link_set_value_separator: Link set from string: value separator (between the attcode and the value itself
|
|
||||||
// default: ':'
|
|
||||||
'link_set_value_separator' => ':',
|
|
||||||
|
|
||||||
'log_global' => true,
|
|
||||||
|
|
||||||
'log_issue' => true,
|
|
||||||
|
|
||||||
'log_notification' => true,
|
|
||||||
|
|
||||||
'log_web_service' => true,
|
|
||||||
|
|
||||||
// max_combo_length: The maximum number of elements in a drop-down list. If more then an autocomplete will be used
|
|
||||||
// default: 50
|
|
||||||
'max_combo_length' => 50,
|
|
||||||
|
|
||||||
'max_display_limit' => '15',
|
|
||||||
|
|
||||||
// max_linkset_output: Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.
|
|
||||||
// default: 100
|
|
||||||
'max_linkset_output' => 100,
|
|
||||||
|
|
||||||
'min_display_limit' => '10',
|
|
||||||
|
|
||||||
// online_help: Hyperlink to the online-help web page
|
|
||||||
// default: 'http://www.combodo.com/itop-help'
|
|
||||||
'online_help' => 'http://www.combodo.com/itop-help',
|
|
||||||
|
|
||||||
// php_path: Path to the php executable in CLI mode
|
|
||||||
// default: 'php'
|
|
||||||
'php_path' => 'php',
|
|
||||||
|
|
||||||
// portal_tickets: CSV list of classes supported in the portal
|
|
||||||
// default: 'UserRequest'
|
|
||||||
'portal_tickets' => 'UserRequest',
|
|
||||||
|
|
||||||
'query_cache_enabled' => true,
|
|
||||||
|
|
||||||
// search_manual_submit: Force manual submit of search requests (class => true)
|
|
||||||
// default: false
|
|
||||||
'search_manual_submit' => array (
|
|
||||||
'Person' => true,
|
|
||||||
),
|
|
||||||
|
|
||||||
'secure_connection_required' => false,
|
|
||||||
|
|
||||||
// session_name: The name of the cookie used to store the PHP session id
|
|
||||||
// default: 'iTop'
|
|
||||||
'session_name' => 'iTop',
|
|
||||||
|
|
||||||
// shortcut_actions: Actions that are available as direct buttons next to the "Actions" menu
|
|
||||||
// default: 'UI:Menu:Modify,UI:Menu:New'
|
|
||||||
'shortcut_actions' => 'UI:Menu:Modify,UI:Menu:New',
|
|
||||||
|
|
||||||
// source_dir: Source directory for the datamodel files. (which gets compiled to env-production).
|
|
||||||
// default: ''
|
|
||||||
'source_dir' => 'datamodels/2.x/',
|
|
||||||
|
|
||||||
'standard_reload_interval' => '300',
|
|
||||||
|
|
||||||
// synchro_trace: Synchronization details: none, display, save (includes 'display')
|
|
||||||
// default: 'none'
|
|
||||||
'synchro_trace' => 'none',
|
|
||||||
|
|
||||||
// timezone: Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP
|
|
||||||
// default: 'Europe/Paris'
|
|
||||||
'timezone' => 'Europe/Paris',
|
|
||||||
|
|
||||||
// tracking_level_linked_set_default: Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)
|
|
||||||
// default: 1
|
|
||||||
'tracking_level_linked_set_default' => 0,
|
|
||||||
|
|
||||||
// url_validation_pattern: Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)
|
|
||||||
// default: '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?'
|
|
||||||
'url_validation_pattern' => '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?',
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Modules specific settings
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
$MyModuleSettings = array(
|
|
||||||
'itop-attachments' => array (
|
|
||||||
'allowed_classes' => array (
|
|
||||||
0 => 'Ticket',
|
|
||||||
),
|
|
||||||
'position' => 'relations',
|
|
||||||
'preview_max_width' => 290,
|
|
||||||
),
|
|
||||||
'itop-backup' => array (
|
|
||||||
'mysql_bindir' => '',
|
|
||||||
'week_days' => 'monday, tuesday, wednesday, thursday, friday',
|
|
||||||
'time' => '23:30',
|
|
||||||
'retention_count' => 5,
|
|
||||||
'enabled' => true,
|
|
||||||
'debug' => false,
|
|
||||||
),
|
|
||||||
'molkobain-console-tooltips' => array (
|
|
||||||
'decoration_class' => 'fas fa-question',
|
|
||||||
'enabled' => true,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Data model modules to be loaded. Names are specified as relative paths
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
$MyModules = array(
|
|
||||||
'addons' => array (
|
|
||||||
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
?>
|
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
<?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.'/bootstrap.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 (".$mysqli->connect_errno . ") ".$mysqli->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;
|
|
||||||
}
|
|
||||||
47
.make/release/update-versions.php
Normal file
47
.make/release/update-versions.php
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Tool to automate version update before release
|
||||||
|
*
|
||||||
|
* Will update version in the following files :
|
||||||
|
*
|
||||||
|
* * datamodels/2.x/.../module.*.php
|
||||||
|
* * datamodels/2.x/version.xml
|
||||||
|
* * css/css-variables.scss $version
|
||||||
|
*
|
||||||
|
* Usage :
|
||||||
|
* `php .make\release\update-versions.php "2.7.0-rc"`
|
||||||
|
*
|
||||||
|
* @since 2.7.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
require_once (__DIR__.'/../../approot.inc.php');
|
||||||
|
require_once (__DIR__.DIRECTORY_SEPARATOR.'update.classes.inc.php');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @var \FileVersionUpdater[] $aFilesUpdaters */
|
||||||
|
$aFilesUpdaters = array(
|
||||||
|
new iTopVersionFileUpdater(),
|
||||||
|
new CssVariablesFileUpdater(),
|
||||||
|
new DatamodelsModulesFiles(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (count($argv) === 1)
|
||||||
|
{
|
||||||
|
echo '/!\ You must pass the new version as parameter';
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
$sVersionLabel = $argv[1];
|
||||||
|
if (empty($sVersionLabel))
|
||||||
|
{
|
||||||
|
echo 'Version passed as parameter is empty !';
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($aFilesUpdaters as $oFileVersionUpdater)
|
||||||
|
{
|
||||||
|
$oFileVersionUpdater->UpdateAllFiles($sVersionLabel);
|
||||||
|
}
|
||||||
36
.make/release/update-xml.php
Normal file
36
.make/release/update-xml.php
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Tool to automate datamodel version update in XML
|
||||||
|
*
|
||||||
|
* Will update version in the following files :
|
||||||
|
*
|
||||||
|
* datamodels/2.x/.../datamodel.*.xml
|
||||||
|
*
|
||||||
|
* Usage :
|
||||||
|
* `php .make\release\update-xml.php "1.7"`
|
||||||
|
*
|
||||||
|
* @since 2.7.0
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
require_once (__DIR__.'/../../approot.inc.php');
|
||||||
|
require_once (__DIR__.DIRECTORY_SEPARATOR.'update.classes.inc.php');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (count($argv) === 1)
|
||||||
|
{
|
||||||
|
echo '/!\ You must pass the new version as parameter';
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
$sVersionLabel = $argv[1];
|
||||||
|
if (empty($sVersionLabel))
|
||||||
|
{
|
||||||
|
echo 'Version passed as parameter is empty !';
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oFileVersionUpdater = new DatamodelsXmlFiles();
|
||||||
|
$oFileVersionUpdater->UpdateAllFiles($sVersionLabel);
|
||||||
169
.make/release/update.classes.inc.php
Normal file
169
.make/release/update.classes.inc.php
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* Classes for updater tools
|
||||||
|
*
|
||||||
|
* @see update-versions.php
|
||||||
|
* @see update-xml.php
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
require_once (__DIR__.'/../../approot.inc.php');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
abstract class FileVersionUpdater
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return string[] full path of files to modify
|
||||||
|
*/
|
||||||
|
abstract public function GetFiles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warnign : will consume lots of memory on larger files !
|
||||||
|
*
|
||||||
|
* @param string $sVersionLabel
|
||||||
|
* @param string $sFileContent
|
||||||
|
* @param string $sFileFullPath
|
||||||
|
*
|
||||||
|
* @return string file content with replaced values
|
||||||
|
*/
|
||||||
|
abstract public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath);
|
||||||
|
|
||||||
|
public function UpdateAllFiles($sVersionLabel)
|
||||||
|
{
|
||||||
|
$aFilesToUpdate = $this->GetFiles();
|
||||||
|
$sFileUpdaterName = get_class($this);
|
||||||
|
echo "# Updater : $sFileUpdaterName\n";
|
||||||
|
foreach ($aFilesToUpdate as $sFileToUpdateFullPath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$sCurrentFileContent = file_get_contents($sFileToUpdateFullPath);
|
||||||
|
$sNewFileContent = $this->UpdateFileContent($sVersionLabel, $sCurrentFileContent, $sFileToUpdateFullPath);
|
||||||
|
file_put_contents($sFileToUpdateFullPath, $sNewFileContent);
|
||||||
|
echo " - $sFileToUpdateFullPath : OK !\n";
|
||||||
|
}
|
||||||
|
catch (Exception $e)
|
||||||
|
{
|
||||||
|
echo " - $sFileToUpdateFullPath : Error :(\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractSingleFileVersionUpdater extends FileVersionUpdater
|
||||||
|
{
|
||||||
|
private $sFileToUpdate;
|
||||||
|
|
||||||
|
public function __construct($sFileToUpdate)
|
||||||
|
{
|
||||||
|
$this->sFileToUpdate = $sFileToUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetFiles()
|
||||||
|
{
|
||||||
|
return array(APPROOT.$this->sFileToUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class iTopVersionFileUpdater extends AbstractSingleFileVersionUpdater
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct('datamodels/2.x/version.xml');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
|
||||||
|
{
|
||||||
|
return preg_replace(
|
||||||
|
'/(<version>)[^<]*(<\/version>)/',
|
||||||
|
'${1}'.$sVersionLabel.'${2}',
|
||||||
|
$sFileContent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CssVariablesFileUpdater extends AbstractSingleFileVersionUpdater
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct('css/css-variables.scss');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
|
||||||
|
{
|
||||||
|
return preg_replace(
|
||||||
|
'/(\$version: "v)[^"]*(";)/',
|
||||||
|
'${1}'.$sVersionLabel.'${2}',
|
||||||
|
$sFileContent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class AbstractGlobFileVersionUpdater extends FileVersionUpdater
|
||||||
|
{
|
||||||
|
protected $sGlobPattern;
|
||||||
|
|
||||||
|
public function __construct($sGlobPattern)
|
||||||
|
{
|
||||||
|
$this->sGlobPattern = $sGlobPattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetFiles()
|
||||||
|
{
|
||||||
|
return glob($this->sGlobPattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatamodelsModulesFiles extends AbstractGlobFileVersionUpdater
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct(APPROOT.'datamodels/2.x/*/module.*.php');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
|
||||||
|
{
|
||||||
|
$sModulePath = realpath($sFileFullPath);
|
||||||
|
$sModuleFileName = basename($sModulePath, 1);
|
||||||
|
$sModuleName = preg_replace('/[^.]+\.([^.]+)\.php/', '$1', $sModuleFileName);
|
||||||
|
|
||||||
|
return preg_replace(
|
||||||
|
"/('$sModuleName\/)[^']+(')/",
|
||||||
|
'${1}'.$sVersionLabel.'${2}',
|
||||||
|
$sFileContent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatamodelsXmlFiles extends AbstractGlobFileVersionUpdater
|
||||||
|
{
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct(APPROOT.'datamodels/2.x/*/datamodel.*.xml');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
|
||||||
|
{
|
||||||
|
return preg_replace(
|
||||||
|
'/(<itop_design .* version=")[^"]+(">)/',
|
||||||
|
'${1}'.$sVersionLabel.'${2}',
|
||||||
|
$sFileContent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
128
CONTRIBUTING.md
128
CONTRIBUTING.md
@@ -1,128 +0,0 @@
|
|||||||
# Contributing to iTop
|
|
||||||
|
|
||||||
You want to contribute to iTop? Many thanks to you! 🎉 👍
|
|
||||||
|
|
||||||
Here are some guidelines that will help us integrate your work!
|
|
||||||
|
|
||||||
|
|
||||||
## Contributions
|
|
||||||
|
|
||||||
### Subjects
|
|
||||||
You are welcome to create pull requests on any of those subjects:
|
|
||||||
|
|
||||||
* 🐛 `:bug:` bug fix
|
|
||||||
* 🌐 `:globe_with_meridians:` translation / i18n / l10n
|
|
||||||
|
|
||||||
If you want to implement a **new feature**, please [create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) for review.
|
|
||||||
If you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the ticket.
|
|
||||||
|
|
||||||
For all **security related subjects**, please see our [security policy](SECURITY.md).
|
|
||||||
|
|
||||||
All **datamodel modification** should be done in an extension. Beware that such change would
|
|
||||||
impact all existing customers, and could prevent them from
|
|
||||||
upgrading!
|
|
||||||
Combodo has a long experience of datamodel changes: they are very disruptive!
|
|
||||||
This is why we avoid them in iTop core, especially the changes on existing objects/fields.
|
|
||||||
If you have an idea you're sure would benefit to all of iTop users, you may
|
|
||||||
[create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) to submit it, but be warned that there are lots of good
|
|
||||||
reasons to refuse such changes.
|
|
||||||
|
|
||||||
### License
|
|
||||||
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file),
|
|
||||||
your code must comply with this license.
|
|
||||||
|
|
||||||
If you want to use another license, you may [create an extension][wiki new ext].
|
|
||||||
|
|
||||||
[license.txt]: https://github.com/Combodo/iTop/blob/develop/license.txt
|
|
||||||
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
|
|
||||||
|
|
||||||
|
|
||||||
## Branch model
|
|
||||||
|
|
||||||
TL;DR:
|
|
||||||
> **create a fork from iTop main repository,
|
|
||||||
> create a branch based on the develop branch**
|
|
||||||
|
|
||||||
We are using the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model. That means we have in our repo those
|
|
||||||
main branches:
|
|
||||||
|
|
||||||
- develop: ongoing development version
|
|
||||||
- release/\*: if present, that means we are working on a beta version
|
|
||||||
- master: previous stable version
|
|
||||||
- support/\*: maintenance branches for older versions
|
|
||||||
|
|
||||||
For example, if no beta version is currently ongoing we could have:
|
|
||||||
|
|
||||||
- develop containing future 2.8.0 version
|
|
||||||
- master containing 2.7.x maintenance version
|
|
||||||
- support/2.6 containing 2.6.x maintenance version
|
|
||||||
- support/2.5 containing 2.5.x maintenance version
|
|
||||||
|
|
||||||
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
|
|
||||||
- master: 2.7.x maintenance version
|
|
||||||
- support/2.6 containing 2.6.x maintenance version
|
|
||||||
- support/2.5 containing 2.5.x maintenance version
|
|
||||||
|
|
||||||
And when 2.8.0 final will be out:
|
|
||||||
|
|
||||||
- develop: future 2.9.0 version
|
|
||||||
- master: 2.8.x maintenance version
|
|
||||||
- support/2.7 : 2.7.x maintenance version
|
|
||||||
- support/2.6 containing 2.6.x maintenance version
|
|
||||||
- support/2.5 containing 2.5.x maintenance version
|
|
||||||
|
|
||||||
Most of the time you should based your developments on the develop branch.
|
|
||||||
That may be different if you want to fix a bug, please use develop anyway and ask in your PR if rebase is possible.
|
|
||||||
|
|
||||||
|
|
||||||
## Coding
|
|
||||||
|
|
||||||
### PHP styleguide
|
|
||||||
|
|
||||||
Please follow [our guidelines](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Acoding_standards).
|
|
||||||
|
|
||||||
### 🌐 Translations
|
|
||||||
|
|
||||||
A [dedicated page](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Atranslation) is available in the official wiki.
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
Please create tests that covers as much as possible the code you're submitting.
|
|
||||||
|
|
||||||
Our tests are located in the `test/` directory, containing a PHPUnit config file : `phpunit.xml.dist`.
|
|
||||||
|
|
||||||
### Git Commit Messages
|
|
||||||
|
|
||||||
* Describe the functional change instead of the technical modifications
|
|
||||||
* 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.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
|
|
||||||
* 🐛 `:bug:` when fixing a bug
|
|
||||||
* 🔥 `:fire:` when removing code or files
|
|
||||||
* 💚 `:green_heart:` when fixing the CI build
|
|
||||||
* ✅ `:white_check_mark:` when adding tests
|
|
||||||
* 🔒 `:lock:` when dealing with security
|
|
||||||
* ⬆️ `:arrow_up:` when upgrading dependencies
|
|
||||||
* ⬇️ `:arrow_down:` when downgrading dependencies
|
|
||||||
* ♻️ `:recycle:` code refactoring
|
|
||||||
* 💄 `:lipstick:` Updating the UI and style files.
|
|
||||||
|
|
||||||
|
|
||||||
## Pull request
|
|
||||||
|
|
||||||
When your code is working, please:
|
|
||||||
|
|
||||||
* stash as much as possible your commits,
|
|
||||||
* rebase your branch on our repo last commit,
|
|
||||||
* create a pull request.
|
|
||||||
|
|
||||||
Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
|
||||||
70
Jenkinsfile
vendored
70
Jenkinsfile
vendored
@@ -1,65 +1,11 @@
|
|||||||
pipeline {
|
def infra
|
||||||
agent any
|
|
||||||
stages {
|
|
||||||
|
|
||||||
stage('init') {
|
node(){
|
||||||
parallel {
|
checkout scm
|
||||||
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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stage('unattended_install') {
|
infra = load '/var/lib/jenkins/workspace/itop-test-infra_master/src/Infra.groovy'
|
||||||
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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
post {
|
|
||||||
always {
|
|
||||||
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} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
|
|
||||||
}
|
|
||||||
fixed {
|
|
||||||
slackSend(channel: "#jenkins-itop", color: '#FFa500', message: "Yes! Build repaired! (${currentBuild.result}), Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
environment {
|
|
||||||
DEBUG_UNIT_TEST = '0'
|
|
||||||
}
|
|
||||||
options {
|
|
||||||
timeout(time: 20, unit: 'MINUTES')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
infra.call()
|
||||||
|
|
||||||
|
|||||||
130
README.md
130
README.md
@@ -21,88 +21,36 @@ iTop also offers mass import tools and web services to integrate with your IT
|
|||||||
- [Data synchronization][18] (for data federation)
|
- [Data synchronization][18] (for data federation)
|
||||||
|
|
||||||
|
|
||||||
|
## Latest release
|
||||||
|
|
||||||
|
- [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
|
||||||
|
[64]: https://www.itophub.io/wiki/page?id=latest:install:start
|
||||||
|
[65]: https://sourceforge.net/projects/itop/files/latest/download
|
||||||
|
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
- [iTop Forums][1]: community support
|
- [iTop Forums][1]: for support request
|
||||||
- [iTop Tickets][2]: for feature requests and bug reports
|
- [iTop Tickets][2]: for feature requests and bug reports
|
||||||
- [Releases download][3]
|
- [Releases download][3]
|
||||||
- [Documentation][4] covering both iTop and its official extensions
|
- [iTop documentation][4] for iTop and official extensions
|
||||||
- [iTop Hub][5] : discover and install extensions !
|
- [iTop extensions][5] for discovering and installing 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/
|
|
||||||
|
|
||||||
[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
|
|
||||||
[12]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#service_management
|
|
||||||
[13]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#change_management
|
|
||||||
[14]: https://www.itophub.io/wiki/page?id=latest%3Aimplementation%3Astart#service_level_agreements_and_targets
|
|
||||||
[15]: https://www.itophub.io/wiki/page?id=latest%3Auser%3Aactions#relations
|
|
||||||
[16]: https://www.itophub.io/wiki/page?id=latest%3Auser%3Abulk_modify#uploading_data
|
|
||||||
[17]: https://www.itophub.io/wiki/page?id=latest%3Aadmin%3Aaudit
|
|
||||||
[18]: https://www.itophub.io/wiki/page?id=latest%3Aadvancedtopics%3Adata_synchro_overview
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Last releases
|
|
||||||
|
|
||||||
### Versions 2.6.*
|
|
||||||
- 2.6.0 published on January 9, 2019
|
|
||||||
- [Changes since the previous version][58]
|
|
||||||
- [New features][59]
|
|
||||||
- [Migration notes][60]
|
|
||||||
- [Download iTop 2.6.1][61]
|
|
||||||
|
|
||||||
[58]: https://www.itophub.io/wiki/page?id=2_6_0:release:change_log
|
|
||||||
[59]: https://www.itophub.io/wiki/page?id=2_6_0:release:2_6_whats_new
|
|
||||||
[60]: https://www.itophub.io/wiki/page?id=2_6_0:install:250_to_260_migration_notes
|
|
||||||
[61]: https://sourceforge.net/projects/itop/files/itop/2.6.1
|
|
||||||
|
|
||||||
|
|
||||||
### Versions 2.5.*
|
|
||||||
- 2.5.0 published on July 11, 2018
|
|
||||||
- [Changes since the previous version][54]
|
|
||||||
- [New features][55]
|
|
||||||
- [Migration notes][56]
|
|
||||||
- [Download iTop 2.5.1][57]
|
|
||||||
|
|
||||||
[54]: https://www.itophub.io/wiki/page?id=2_5_0:release:change_log
|
|
||||||
[55]: https://www.itophub.io/wiki/page?id=2_5_0:release:2_5_whats_new
|
|
||||||
[56]: https://www.itophub.io/wiki/page?id=2_5_0:install:240_to_250_migration_notes
|
|
||||||
[57]: https://sourceforge.net/projects/itop/files/itop/2.5.1
|
|
||||||
|
|
||||||
|
# About Us
|
||||||
### Versions 2.4.*
|
|
||||||
- 2.4.0 published on November 16, 2017
|
|
||||||
- [Changes since the previous version][50]
|
|
||||||
- [New features][51]
|
|
||||||
- [Migration notes][52]
|
|
||||||
- [Download iTop 2.4.1][53]
|
|
||||||
|
|
||||||
[50]: https://www.itophub.io/wiki/page?id=2_4_0:release:change_log
|
|
||||||
[51]: https://www.itophub.io/wiki/page?id=2_4_0:release:2_4_whats_new
|
|
||||||
[52]: https://www.itophub.io/wiki/page?id=2_4_0:install:230_to_240_migration_notes
|
|
||||||
[53]: https://sourceforge.net/projects/itop/files/itop/2.4.1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## About Us
|
|
||||||
|
|
||||||
iTop development is sponsored, led and supported by [Combodo][0].
|
iTop development is sponsored, led and supported by [Combodo][0].
|
||||||
|
|
||||||
[0]: https://www.combodo.com
|
|
||||||
|
|
||||||
|
# Contributors
|
||||||
## Contributors
|
|
||||||
|
|
||||||
We would like to give a special thank you to the people from the community who contributed to this project, including:
|
We would like to give a special thank you to the people from the community who contributed to this project, including:
|
||||||
|
|
||||||
### Names
|
|
||||||
- Alves, David
|
- Alves, David
|
||||||
- Beck, Pedro
|
- Beck, Pedro
|
||||||
- Bilger, Jean-François
|
- Bilger, Jean-François
|
||||||
@@ -129,7 +77,7 @@ We would like to give a special thank you to the people from the community who c
|
|||||||
- Tulio, Marco
|
- Tulio, Marco
|
||||||
- Turrubiates, Miguel
|
- Turrubiates, Miguel
|
||||||
|
|
||||||
### Aliases
|
#### Aliases
|
||||||
- chifu1234
|
- chifu1234
|
||||||
- cprobst
|
- cprobst
|
||||||
- Karkoff1212
|
- Karkoff1212
|
||||||
@@ -140,7 +88,45 @@ We would like to give a special thank you to the people from the community who c
|
|||||||
- theBigOne
|
- theBigOne
|
||||||
- ulmerspatz
|
- ulmerspatz
|
||||||
|
|
||||||
### Companies
|
#### Companies
|
||||||
- Hardis
|
- Hardis
|
||||||
- ITOMIG
|
- ITOMIG
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[0]: https://www.combodo.com
|
||||||
|
|
||||||
|
[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/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[10]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#configuration_management_cmdb
|
||||||
|
[11]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#ticketing
|
||||||
|
[12]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#service_management
|
||||||
|
[13]: https://www.itophub.io/wiki/page?id=2_5_0%3Adatamodel%3Astart#change_management
|
||||||
|
[14]: https://www.itophub.io/wiki/page?id=2_5_0%3Aimplementation%3Astart#service_level_agreements_and_targets
|
||||||
|
[15]: https://www.itophub.io/wiki/page?id=2_5_0%3Auser%3Aactions#relations
|
||||||
|
[16]: https://www.itophub.io/wiki/page?id=2_5_0%3Auser%3Abulk_modify#uploading_data
|
||||||
|
[17]: https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Aaudit
|
||||||
|
[18]: https://www.itophub.io/wiki/page?id=2_5_0%3Aadvancedtopics%3Adata_synchro_overview
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[50]: https://www.itophub.io/wiki/page?id=2_4_0:release:change_log
|
||||||
|
[51]: https://www.itophub.io/wiki/page?id=2_4_0:release:2_4_whats_new
|
||||||
|
[52]: https://www.itophub.io/wiki/page?id=2_4_0:install:230_to_240_migration_notes
|
||||||
|
[53]: https://sourceforge.net/projects/itop/files/itop/2.4.1
|
||||||
|
|
||||||
|
[54]: https://www.itophub.io/wiki/page?id=2_5_0:release:change_log
|
||||||
|
[55]: https://www.itophub.io/wiki/page?id=2_5_0:release:2_5_whats_new
|
||||||
|
[56]: https://www.itophub.io/wiki/page?id=2_5_0:install:240_to_250_migration_notes
|
||||||
|
[57]: https://sourceforge.net/projects/itop/files/itop/2.5.1
|
||||||
|
|
||||||
|
[58]: https://www.itophub.io/wiki/page?id=2_6_0:release:change_log
|
||||||
|
[59]: https://www.itophub.io/wiki/page?id=2_6_0:release:2_6_whats_new
|
||||||
|
[60]: https://www.itophub.io/wiki/page?id=2_6_0:install:250_to_260_migration_notes
|
||||||
|
[61]: https://sourceforge.net/projects/itop/files/itop/2.6.3
|
||||||
|
|||||||
36
SECURITY.md
36
SECURITY.md
@@ -1,36 +0,0 @@
|
|||||||
# 🔒 Reporting vulnerabilities
|
|
||||||
|
|
||||||
We take all security bugs seriously. Thank you for improving the security of iTop! We appreciate your efforts and
|
|
||||||
responsible disclosure and will make every effort to acknowledge your contributions.
|
|
||||||
|
|
||||||
|
|
||||||
## ✉️ How to report
|
|
||||||
|
|
||||||
### iTop vulnerabilities
|
|
||||||
Please send a procedure to reproduce iTop vulnerabilities to [itop-security@combodo.com](mailto:itop-security@combodo.com).
|
|
||||||
|
|
||||||
You can send us a standard "given / then / when" report, including iTop version, impacts, and maybe installed modules or data if they are
|
|
||||||
needed to reproduce.
|
|
||||||
|
|
||||||
### Dependencies vulnerabilities
|
|
||||||
Report security bugs in third-party modules to the person or team maintaining the module, and notify us of this report by sending an email
|
|
||||||
to [itop-security@combodo.com](mailto:itop-security@combodo.com).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 📆 Disclosure Policy
|
|
||||||
|
|
||||||
Report sent to us will be acknowledged within the week.
|
|
||||||
|
|
||||||
Then, a Combodo developer will be assigned to the reported issue and will:
|
|
||||||
|
|
||||||
* confirm the problem and determine the affected iTop versions
|
|
||||||
* audit the code to search any potential similar problems
|
|
||||||
* try to find a workaround if any
|
|
||||||
* create fixes for all releases still under maintenance
|
|
||||||
* send you the commit(s) for review
|
|
||||||
* send you the next version(s) that will contain the fix, and the estimated release dates
|
|
||||||
|
|
||||||
Security issues always take precedence over bug fixes and feature work.
|
|
||||||
|
|
||||||
The assignee will keep you informed of the resolution progress, and may ask you for additional information or guidance.
|
|
||||||
@@ -30,32 +30,35 @@ class ajax_page extends WebPage implements iTabbedPage
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Jquery style ready script
|
* Jquery style ready script
|
||||||
* @var Hash
|
* @var Hash
|
||||||
*/
|
*/
|
||||||
protected $m_sReadyScript;
|
protected $m_sReadyScript;
|
||||||
protected $m_oTabs;
|
protected $m_oTabs;
|
||||||
private $m_sMenu; // If set, then the menu will be updated
|
private $m_sMenu; // If set, then the menu will be updated
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* constructor for the web page
|
* constructor for the web page
|
||||||
* @param string $s_title Not used
|
*
|
||||||
*/
|
* @param string $s_title Not used
|
||||||
function __construct($s_title)
|
*/
|
||||||
{
|
function __construct($s_title) {
|
||||||
$sPrintable = utils::ReadParam('printable', '0');
|
$sPrintable = utils::ReadParam('printable', '0');
|
||||||
$bPrintable = ($sPrintable == '1');
|
$bPrintable = ($sPrintable == '1');
|
||||||
|
|
||||||
parent::__construct($s_title, $bPrintable);
|
parent::__construct($s_title, $bPrintable);
|
||||||
$this->m_sReadyScript = "";
|
$this->m_sReadyScript = "";
|
||||||
//$this->add_header("Content-type: text/html; charset=utf-8");
|
//$this->add_header("Content-type: text/html; charset=utf-8");
|
||||||
$this->add_header("Cache-control: 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('X-Frame-Options: deny');
|
||||||
$this->m_oTabs = new TabManager();
|
$this->m_oTabs = new TabManager();
|
||||||
$this->sContentType = 'text/html';
|
$this->sContentType = 'text/html';
|
||||||
$this->sContentDisposition = 'inline';
|
$this->sContentDisposition = 'inline';
|
||||||
$this->m_sMenu = "";
|
$this->m_sMenu = "";
|
||||||
|
|
||||||
utils::InitArchiveMode();
|
utils::InitArchiveMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
||||||
{
|
{
|
||||||
@@ -214,9 +217,7 @@ PrepareWidgets();
|
|||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
$this->outputCollapsibleSectionInit();
|
$s_captured_output = $this->ob_get_clean_safe();
|
||||||
|
|
||||||
$s_captured_output = $this->ob_get_clean_safe();
|
|
||||||
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
|
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
|
||||||
{
|
{
|
||||||
// inline content != attachment && html => filter all scripts for malicious XSS scripts
|
// inline content != attachment && html => filter all scripts for malicious XSS scripts
|
||||||
@@ -291,10 +292,6 @@ EOF
|
|||||||
{
|
{
|
||||||
DBSearch::RecordQueryTrace();
|
DBSearch::RecordQueryTrace();
|
||||||
}
|
}
|
||||||
if (class_exists('ExecutionKPI'))
|
|
||||||
{
|
|
||||||
ExecutionKPI::ReportStats();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -54,7 +54,7 @@ require_once(APPROOT.'sources/application/search/criterionconversion/criterionto
|
|||||||
abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||||
{
|
{
|
||||||
protected $m_iFormId; // The ID of the form used to edit the object (when in edition mode !)
|
protected $m_iFormId; // The ID of the form used to edit the object (when in edition mode !)
|
||||||
protected static $iGlobalFormId = 1;
|
static $iGlobalFormId = 1;
|
||||||
protected $aFieldsMap;
|
protected $aFieldsMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,7 +176,7 @@ EOF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
|
function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
|
||||||
{
|
{
|
||||||
// Standard Header with name, actions menu and history block
|
// Standard Header with name, actions menu and history block
|
||||||
//
|
//
|
||||||
@@ -321,7 +321,7 @@ EOF
|
|||||||
}
|
}
|
||||||
$sLabel = htmlentities(Dict::S('Tag:Synchronized'), ENT_QUOTES, 'UTF-8');
|
$sLabel = htmlentities(Dict::S('Tag:Synchronized'), ENT_QUOTES, 'UTF-8');
|
||||||
$sSynchroTagId = 'synchro_icon-'.$this->GetKey();
|
$sSynchroTagId = 'synchro_icon-'.$this->GetKey();
|
||||||
$aIcons[] = "<div class=\"tag\" id=\"$sSynchroTagId\"><span class=\"object-synchronized fas fa-lock fa-1x\"> </span> $sLabel</div>";
|
$aIcons[] = "<div class=\"tag\" id=\"$sSynchroTagId\"><span class=\"object-synchronized fa fa-lock fa-1x\"> </span> $sLabel</div>";
|
||||||
$sTip = addslashes($sTip);
|
$sTip = addslashes($sTip);
|
||||||
$oPage->add_ready_script("$('#$sSynchroTagId').qtip( { content: '$sTip', show: 'mouseover', hide: { fixed: true }, style: { name: 'dark', tip: 'topLeft' }, position: { corner: { target: 'bottomMiddle', tooltip: 'topLeft' }} } );");
|
$oPage->add_ready_script("$('#$sSynchroTagId').qtip( { content: '$sTip', show: 'mouseover', hide: { fixed: true }, style: { name: 'dark', tip: 'topLeft' }, position: { corner: { target: 'bottomMiddle', tooltip: 'topLeft' }} } );");
|
||||||
}
|
}
|
||||||
@@ -331,13 +331,13 @@ EOF
|
|||||||
{
|
{
|
||||||
$sLabel = htmlentities(Dict::S('Tag:Archived'), ENT_QUOTES, 'UTF-8');
|
$sLabel = htmlentities(Dict::S('Tag:Archived'), ENT_QUOTES, 'UTF-8');
|
||||||
$sTitle = htmlentities(Dict::S('Tag:Archived+'), ENT_QUOTES, 'UTF-8');
|
$sTitle = htmlentities(Dict::S('Tag:Archived+'), ENT_QUOTES, 'UTF-8');
|
||||||
$aIcons[] = "<div class=\"tag\" title=\"$sTitle\"><span class=\"object-archived fas fa-archive fa-1x\"> </span> $sLabel</div>";
|
$aIcons[] = "<div class=\"tag\" title=\"$sTitle\"><span class=\"object-archived fa fa-archive fa-1x\"> </span> $sLabel</div>";
|
||||||
}
|
}
|
||||||
elseif ($this->IsObsolete())
|
elseif ($this->IsObsolete())
|
||||||
{
|
{
|
||||||
$sLabel = htmlentities(Dict::S('Tag:Obsolete'), ENT_QUOTES, 'UTF-8');
|
$sLabel = htmlentities(Dict::S('Tag:Obsolete'), ENT_QUOTES, 'UTF-8');
|
||||||
$sTitle = htmlentities(Dict::S('Tag:Obsolete+'), ENT_QUOTES, 'UTF-8');
|
$sTitle = htmlentities(Dict::S('Tag:Obsolete+'), ENT_QUOTES, 'UTF-8');
|
||||||
$aIcons[] = "<div class=\"tag\" title=\"$sTitle\"><span class=\"object-obsolete fas fa-eye-slash fa-1x\"> </span> $sLabel</div>";
|
$aIcons[] = "<div class=\"tag\" title=\"$sTitle\"><span class=\"object-obsolete fa fa-eye-slash fa-1x\"> </span> $sLabel</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
$sObjectIcon = $this->GetIcon();
|
$sObjectIcon = $this->GetIcon();
|
||||||
@@ -367,7 +367,7 @@ EOF
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0)
|
function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0)
|
||||||
{
|
{
|
||||||
// history block (with as a tab)
|
// history block (with as a tab)
|
||||||
$oHistoryFilter = new DBObjectSearch('CMDBChangeOp');
|
$oHistoryFilter = new DBObjectSearch('CMDBChangeOp');
|
||||||
@@ -378,7 +378,7 @@ EOF
|
|||||||
$oBlock->Display($oPage, 'history');
|
$oBlock->Display($oPage, 'history');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||||
{
|
{
|
||||||
$aFieldsMap = $this->GetBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
|
$aFieldsMap = $this->GetBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
|
||||||
|
|
||||||
@@ -461,7 +461,7 @@ EOF
|
|||||||
* @throws \MySQLHasGoneAwayException
|
* @throws \MySQLHasGoneAwayException
|
||||||
* @throws \OQLException
|
* @throws \OQLException
|
||||||
*/
|
*/
|
||||||
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
||||||
{
|
{
|
||||||
$aRedundancySettings = $this->FindVisibleRedundancySettings();
|
$aRedundancySettings = $this->FindVisibleRedundancySettings();
|
||||||
|
|
||||||
@@ -673,15 +673,14 @@ EOF
|
|||||||
if (count($aTriggers) > 0)
|
if (count($aTriggers) > 0)
|
||||||
{
|
{
|
||||||
$iId = $this->GetKey();
|
$iId = $this->GetKey();
|
||||||
$aParams = array('triggers' => $aTriggers, 'id' => $iId);
|
$sTriggersList = implode(',', $aTriggers);
|
||||||
$aNotifSearches = array();
|
$aNotifSearches = array();
|
||||||
$iNotifsCount = 0;
|
$iNotifsCount = 0;
|
||||||
$aNotificationClasses = MetaModel::EnumChildClasses('EventNotification', ENUM_CHILD_CLASSES_EXCLUDETOP);
|
$aNotificationClasses = MetaModel::EnumChildClasses('EventNotification', ENUM_CHILD_CLASSES_EXCLUDETOP);
|
||||||
foreach($aNotificationClasses as $sNotifClass)
|
foreach($aNotificationClasses as $sNotifClass)
|
||||||
{
|
{
|
||||||
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN (:triggers) AND Ev.object_id = :id");
|
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN ($sTriggersList) AND Ev.object_id = $iId");
|
||||||
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
|
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass]);
|
||||||
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], array());
|
|
||||||
$iNotifsCount += $oNotifSet->Count();
|
$iNotifsCount += $oNotifSet->Count();
|
||||||
}
|
}
|
||||||
// Display notifications regarding the object: on block per subclass to have the intersting columns
|
// Display notifications regarding the object: on block per subclass to have the intersting columns
|
||||||
@@ -698,7 +697,7 @@ EOF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetBareProperties(WebPage $oPage, $bEditMode, $sPrefix, $aExtraParams = array())
|
function GetBareProperties(WebPage $oPage, $bEditMode, $sPrefix, $aExtraParams = array())
|
||||||
{
|
{
|
||||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||||
$sClass = get_class($this);
|
$sClass = get_class($this);
|
||||||
@@ -959,7 +958,7 @@ EOF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DisplayPreview(WebPage $oPage)
|
function DisplayPreview(WebPage $oPage)
|
||||||
{
|
{
|
||||||
$aDetails = array();
|
$aDetails = array();
|
||||||
$sClass = get_class($this);
|
$sClass = get_class($this);
|
||||||
@@ -1273,12 +1272,12 @@ EOF
|
|||||||
return $oDataTable->Display($oPage, $oSettings, $bDisplayMenu, $sSelectMode, $bViewLink, $aExtraParams);
|
return $oDataTable->Display($oPage, $oSettings, $bDisplayMenu, $sSelectMode, $bViewLink, $aExtraParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
|
static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
|
||||||
{
|
{
|
||||||
$oPage->add(self::GetSetAsCSV($oSet, $aParams, $sCharset));
|
$oPage->add(self::GetSetAsCSV($oSet, $aParams, $sCharset));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function GetSetAsCSV(DBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
|
static function GetSetAsCSV(DBObjectSet $oSet, $aParams = array(), $sCharset = 'UTF-8')
|
||||||
{
|
{
|
||||||
$sSeparator = isset($aParams['separator']) ? $aParams['separator'] : ','; // default separator is comma
|
$sSeparator = isset($aParams['separator']) ? $aParams['separator'] : ','; // default separator is comma
|
||||||
$sTextQualifier = isset($aParams['text_qualifier']) ? $aParams['text_qualifier'] : '"'; // default text qualifier is double quote
|
$sTextQualifier = isset($aParams['text_qualifier']) ? $aParams['text_qualifier'] : '"'; // default text qualifier is double quote
|
||||||
@@ -1407,7 +1406,7 @@ EOF
|
|||||||
return $sHtml;
|
return $sHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function DisplaySetAsHTMLSpreadsheet(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
|
static function DisplaySetAsHTMLSpreadsheet(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
|
||||||
{
|
{
|
||||||
$oPage->add(self::GetSetAsHTMLSpreadsheet($oSet, $aParams));
|
$oPage->add(self::GetSetAsHTMLSpreadsheet($oSet, $aParams));
|
||||||
}
|
}
|
||||||
@@ -1416,7 +1415,7 @@ EOF
|
|||||||
* Spreadsheet output: designed for end users doing some reporting
|
* Spreadsheet output: designed for end users doing some reporting
|
||||||
* Then the ids are excluded and replaced by the corresponding friendlyname
|
* Then the ids are excluded and replaced by the corresponding friendlyname
|
||||||
*/
|
*/
|
||||||
public static function GetSetAsHTMLSpreadsheet(DBObjectSet $oSet, $aParams = array())
|
static function GetSetAsHTMLSpreadsheet(DBObjectSet $oSet, $aParams = array())
|
||||||
{
|
{
|
||||||
$aFields = null;
|
$aFields = null;
|
||||||
if (isset($aParams['fields']) && (strlen($aParams['fields']) > 0))
|
if (isset($aParams['fields']) && (strlen($aParams['fields']) > 0))
|
||||||
@@ -1600,7 +1599,7 @@ EOF
|
|||||||
return $sHtml;
|
return $sHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function DisplaySetAsXML(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
|
static function DisplaySetAsXML(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
|
||||||
{
|
{
|
||||||
$bLocalize = true;
|
$bLocalize = true;
|
||||||
if (isset($aParams['localize_values']))
|
if (isset($aParams['localize_values']))
|
||||||
@@ -1904,7 +1903,7 @@ EOF
|
|||||||
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
|
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
|
||||||
$aConfig['language'] = $sLanguage;
|
$aConfig['language'] = $sLanguage;
|
||||||
$aConfig['contentsLanguage'] = $sLanguage;
|
$aConfig['contentsLanguage'] = $sLanguage;
|
||||||
$aConfig['extraPlugins'] = 'disabler,codesnippet';
|
$aConfig['extraPlugins'] = 'disabler';
|
||||||
$aConfig['placeholder'] = Dict::S('UI:CaseLogTypeYourTextHere');
|
$aConfig['placeholder'] = Dict::S('UI:CaseLogTypeYourTextHere');
|
||||||
$sConfigJS = json_encode($aConfig);
|
$sConfigJS = json_encode($aConfig);
|
||||||
|
|
||||||
@@ -2865,7 +2864,7 @@ EOF
|
|||||||
return $aDetails;
|
return $aDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function FlattenZList($aList)
|
static function FlattenZList($aList)
|
||||||
{
|
{
|
||||||
$aResult = array();
|
$aResult = array();
|
||||||
foreach($aList as $value)
|
foreach($aList as $value)
|
||||||
@@ -2967,7 +2966,6 @@ EOF
|
|||||||
$data = $oDoc->GetData();
|
$data = $oDoc->GetData();
|
||||||
switch ($oDoc->GetMimeType())
|
switch ($oDoc->GetMimeType())
|
||||||
{
|
{
|
||||||
case 'text/html':
|
|
||||||
case 'text/xml':
|
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");
|
$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;
|
break;
|
||||||
@@ -3752,20 +3750,18 @@ EOF
|
|||||||
{
|
{
|
||||||
// Invoke extensions after the update (could be before)
|
// Invoke extensions after the update (could be before)
|
||||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
||||||
}
|
}
|
||||||
}
|
} catch (Exception $e)
|
||||||
catch (Exception $e)
|
|
||||||
{
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
{
|
||||||
unset($aUpdateReentrance[$sKey]);
|
unset($aUpdateReentrance[$sKey]);
|
||||||
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unset($aUpdateReentrance[$sKey]);
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3888,9 +3884,6 @@ EOF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
protected function DoCheckToDelete(&$oDeletionPlan)
|
protected function DoCheckToDelete(&$oDeletionPlan)
|
||||||
{
|
{
|
||||||
parent::DoCheckToDelete($oDeletionPlan);
|
parent::DoCheckToDelete($oDeletionPlan);
|
||||||
|
|||||||
@@ -29,13 +29,15 @@ require_once(APPROOT."/application/webpage.class.inc.php");
|
|||||||
|
|
||||||
class CSVPage extends WebPage
|
class CSVPage extends WebPage
|
||||||
{
|
{
|
||||||
function __construct($s_title)
|
function __construct($s_title) {
|
||||||
{
|
parent::__construct($s_title);
|
||||||
parent::__construct($s_title);
|
$this->add_header("Content-type: text/plain; charset=utf-8");
|
||||||
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
|
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
|
||||||
$this->add_header("Cache-control: no-cache");
|
$this->add_header('Pragma: no-cache');
|
||||||
|
$this->add_header('Expires: 0');
|
||||||
|
$this->add_header('X-Frame-Options: deny');
|
||||||
//$this->add_header("Content-Transfer-Encoding: binary");
|
//$this->add_header("Content-Transfer-Encoding: binary");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function output()
|
public function output()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -674,28 +674,29 @@ class RuntimeDashboard extends Dashboard
|
|||||||
{
|
{
|
||||||
$bCustomized = false;
|
$bCustomized = false;
|
||||||
|
|
||||||
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false))
|
$sDashboardFileSanitized = utils::RealPath($sDashboardFile, APPROOT);
|
||||||
{
|
if (false === $sDashboardFileSanitized) {
|
||||||
|
throw new SecurityException('Invalid dashboard file !');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false)) {
|
||||||
// Search for an eventual user defined dashboard
|
// Search for an eventual user defined dashboard
|
||||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||||
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
|
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
|
||||||
$oUDSet = new DBObjectSet($oUDSearch);
|
$oUDSet = new DBObjectSet($oUDSearch);
|
||||||
if ($oUDSet->Count() > 0)
|
if ($oUDSet->Count() > 0) {
|
||||||
{
|
|
||||||
// Assuming there is at most one couple {user, menu}!
|
// Assuming there is at most one couple {user, menu}!
|
||||||
$oUserDashboard = $oUDSet->Fetch();
|
$oUserDashboard = $oUDSet->Fetch();
|
||||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||||
$bCustomized = true;
|
$bCustomized = true;
|
||||||
}
|
} else {
|
||||||
else
|
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||||
{
|
|
||||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sDashboardDefinition !== false)
|
if ($sDashboardDefinition !== false)
|
||||||
@@ -703,7 +704,7 @@ class RuntimeDashboard extends Dashboard
|
|||||||
$oDashboard = new RuntimeDashboard($sDashBoardId);
|
$oDashboard = new RuntimeDashboard($sDashBoardId);
|
||||||
$oDashboard->FromXml($sDashboardDefinition);
|
$oDashboard->FromXml($sDashboardDefinition);
|
||||||
$oDashboard->SetCustomFlag($bCustomized);
|
$oDashboard->SetCustomFlag($bCustomized);
|
||||||
$oDashboard->SetDefinitionFile($sDashboardFile);
|
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -613,12 +613,12 @@ class DashletUnknown extends Dashlet
|
|||||||
{
|
{
|
||||||
$aInfos = static::GetInfo();
|
$aInfos = static::GetInfo();
|
||||||
|
|
||||||
$sIconUrl = utils::GetAbsoluteUrlAppRoot().$aInfos['icon'];
|
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().$aInfos['icon']);
|
||||||
$sExplainText = ($bEditMode) ? Dict::Format('UI:DashletUnknown:RenderText:Edit', $this->GetDashletType()) : Dict::S('UI:DashletUnknown:RenderText:View');
|
$sExplainText = ($bEditMode) ? Dict::Format('UI:DashletUnknown:RenderText:Edit', $this->GetDashletType()) : Dict::S('UI:DashletUnknown:RenderText:View');
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-content">');
|
$oPage->add('<div class="dashlet-content">');
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-ukn-image"><img src="'.utils::HtmlEntities($sIconUrl).'" /></div>');
|
$oPage->add('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div>');
|
||||||
$oPage->add('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
$oPage->add('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
||||||
|
|
||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
@@ -633,12 +633,12 @@ class DashletUnknown extends Dashlet
|
|||||||
{
|
{
|
||||||
$aInfos = static::GetInfo();
|
$aInfos = static::GetInfo();
|
||||||
|
|
||||||
$sIconUrl = utils::GetAbsoluteUrlAppRoot().$aInfos['icon'];
|
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().$aInfos['icon']);
|
||||||
$sExplainText = Dict::Format('UI:DashletUnknown:RenderNoDataText:Edit', $this->GetDashletType());
|
$sExplainText = Dict::Format('UI:DashletUnknown:RenderNoDataText:Edit', $this->GetDashletType());
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-content">');
|
$oPage->add('<div class="dashlet-content">');
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-ukn-image"><img src="'.utils::HtmlEntities($sIconUrl).'" /></div>');
|
$oPage->add('<div class="dashlet-ukn-image"><img src="'.$sIconUrl.'" /></div>');
|
||||||
$oPage->add('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
$oPage->add('<div class="dashlet-ukn-text">'.$sExplainText.'</div>');
|
||||||
|
|
||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
@@ -774,12 +774,12 @@ class DashletProxy extends DashletUnknown
|
|||||||
{
|
{
|
||||||
$aInfos = static::GetInfo();
|
$aInfos = static::GetInfo();
|
||||||
|
|
||||||
$sIconUrl = utils::GetAbsoluteUrlAppRoot().$aInfos['icon'];
|
$sIconUrl = utils::HtmlEntities(utils::GetAbsoluteUrlAppRoot().$aInfos['icon']);
|
||||||
$sExplainText = Dict::Format('UI:DashletProxy:RenderNoDataText:Edit', $this->GetDashletType());
|
$sExplainText = Dict::Format('UI:DashletProxy:RenderNoDataText:Edit', $this->GetDashletType());
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-content">');
|
$oPage->add('<div class="dashlet-content">');
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-pxy-image"><img src="'.utils::HtmlEntities($sIconUrl).'" /></div>');
|
$oPage->add('<div class="dashlet-pxy-image"><img src="'.$sIconUrl.'" /></div>');
|
||||||
$oPage->add('<div class="dashlet-pxy-text">'.$sExplainText.'</div>');
|
$oPage->add('<div class="dashlet-pxy-text">'.$sExplainText.'</div>');
|
||||||
|
|
||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
@@ -860,7 +860,7 @@ class DashletPlainText extends Dashlet
|
|||||||
*/
|
*/
|
||||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||||
{
|
{
|
||||||
$sText = htmlentities($this->aProperties['text'], ENT_QUOTES, 'UTF-8');
|
$sText = utils::HtmlEntities($this->aProperties['text']);
|
||||||
$sText = str_replace(array("\r\n", "\n", "\r"), "<br/>", $sText);
|
$sText = str_replace(array("\r\n", "\n", "\r"), "<br/>", $sText);
|
||||||
|
|
||||||
$sId = 'plaintext_'.($bEditMode? 'edit_' : '').$this->sId;
|
$sId = 'plaintext_'.($bEditMode? 'edit_' : '').$this->sId;
|
||||||
@@ -917,7 +917,7 @@ class DashletObjectList extends Dashlet
|
|||||||
$sShowMenu = $this->aProperties['menu'] ? '1' : '0';
|
$sShowMenu = $this->aProperties['menu'] ? '1' : '0';
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-content">');
|
$oPage->add('<div class="dashlet-content">');
|
||||||
$sHtmlTitle = htmlentities(Dict::S($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
|
$sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block
|
||||||
if ($sHtmlTitle != '')
|
if ($sHtmlTitle != '')
|
||||||
{
|
{
|
||||||
$oPage->add('<h1>'.$sHtmlTitle.'</h1>');
|
$oPage->add('<h1>'.$sHtmlTitle.'</h1>');
|
||||||
@@ -956,7 +956,7 @@ class DashletObjectList extends Dashlet
|
|||||||
$bShowMenu = $this->aProperties['menu'];
|
$bShowMenu = $this->aProperties['menu'];
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-content">');
|
$oPage->add('<div class="dashlet-content">');
|
||||||
$sHtmlTitle = htmlentities($this->oModelReflection->DictString($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
|
$sHtmlTitle = utils::HtmlEntities($this->oModelReflection->DictString($sTitle)); // done in the itop block
|
||||||
if ($sHtmlTitle != '')
|
if ($sHtmlTitle != '')
|
||||||
{
|
{
|
||||||
$oPage->add('<h1>'.$sHtmlTitle.'</h1>');
|
$oPage->add('<h1>'.$sHtmlTitle.'</h1>');
|
||||||
@@ -1249,7 +1249,7 @@ abstract class DashletGroupBy extends Dashlet
|
|||||||
|
|
||||||
case 'table':
|
case 'table':
|
||||||
default:
|
default:
|
||||||
$sHtmlTitle = htmlentities(Dict::S($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
|
$sHtmlTitle = utils::HtmlEntities(Dict::S($sTitle)); // done in the itop block
|
||||||
$sType = 'count';
|
$sType = 'count';
|
||||||
$aParams = array(
|
$aParams = array(
|
||||||
'group_by' => $this->sGroupByExpr,
|
'group_by' => $this->sGroupByExpr,
|
||||||
@@ -1686,7 +1686,7 @@ class DashletGroupByPie extends DashletGroupBy
|
|||||||
|
|
||||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||||
|
|
||||||
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.htmlentities($sTitle, ENT_QUOTES, 'UTF-8').'</h1>' : '';
|
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.utils::HtmlEntities($sTitle).'</h1>' : '';
|
||||||
$oPage->add("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
|
$oPage->add("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
|
||||||
|
|
||||||
$aDisplayValues = $this->MakeSimulatedData();
|
$aDisplayValues = $this->MakeSimulatedData();
|
||||||
@@ -1758,7 +1758,7 @@ class DashletGroupByBars extends DashletGroupBy
|
|||||||
|
|
||||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||||
|
|
||||||
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.htmlentities($sTitle, ENT_QUOTES, 'UTF-8').'</h1>' : '';
|
$HTMLsTitle = ($sTitle != '') ? '<h1 style="text-align:center">'.utils::HtmlEntities($sTitle).'</h1>' : '';
|
||||||
$oPage->add("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
|
$oPage->add("<div style=\"background-color:#fff;padding:0.25em;\">$HTMLsTitle<div id=\"$sBlockId\" style=\"background-color:#fff;\"></div></div>");
|
||||||
|
|
||||||
$aDisplayValues = $this->MakeSimulatedData();
|
$aDisplayValues = $this->MakeSimulatedData();
|
||||||
@@ -1907,16 +1907,16 @@ class DashletHeaderStatic extends Dashlet
|
|||||||
*/
|
*/
|
||||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||||
{
|
{
|
||||||
$sTitle = $this->aProperties['title'];
|
$sTitle = utils::HtmlEntities($this->aProperties['title']);
|
||||||
$sIcon = $this->aProperties['icon'];
|
$sIcon = $this->aProperties['icon'];
|
||||||
|
|
||||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||||
$sIconPath = $oIconSelect->MakeFileUrl($sIcon);
|
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-content">');
|
$oPage->add('<div class="dashlet-content">');
|
||||||
$oPage->add('<div class="main_header">');
|
$oPage->add('<div class="main_header">');
|
||||||
|
|
||||||
$oPage->add('<img src="'.utils::HtmlEntities($sIconPath).'">');
|
$oPage->add('<img src="'.$sIconPath.'">');
|
||||||
$oPage->add('<h1>'.$this->oModelReflection->DictString($sTitle).'</h1>');
|
$oPage->add('<h1>'.$this->oModelReflection->DictString($sTitle).'</h1>');
|
||||||
|
|
||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
@@ -2037,14 +2037,14 @@ class DashletHeaderDynamic extends Dashlet
|
|||||||
*/
|
*/
|
||||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||||
{
|
{
|
||||||
$sTitle = $this->aProperties['title'];
|
$sTitle = utils::HtmlEntities($this->aProperties['title']);
|
||||||
$sIcon = $this->aProperties['icon'];
|
$sIcon = $this->aProperties['icon'];
|
||||||
$sSubtitle = $this->aProperties['subtitle'];
|
$sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']);
|
||||||
$sQuery = $this->aProperties['query'];
|
$sQuery = $this->aProperties['query'];
|
||||||
$sGroupBy = $this->aProperties['group_by'];
|
$sGroupBy = $this->aProperties['group_by'];
|
||||||
|
|
||||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||||
$sIconPath = $oIconSelect->MakeFileUrl($sIcon);
|
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
|
||||||
|
|
||||||
$aValues = $this->GetValues();
|
$aValues = $this->GetValues();
|
||||||
if (count($aValues) > 0)
|
if (count($aValues) > 0)
|
||||||
@@ -2072,7 +2072,7 @@ class DashletHeaderDynamic extends Dashlet
|
|||||||
$oPage->add('<div class="dashlet-content">');
|
$oPage->add('<div class="dashlet-content">');
|
||||||
$oPage->add('<div class="main_header">');
|
$oPage->add('<div class="main_header">');
|
||||||
|
|
||||||
$oPage->add('<img src="'.utils::HtmlEntities($sIconPath).'">');
|
$oPage->add('<img src="'.$sIconPath.'">');
|
||||||
|
|
||||||
if (isset($aExtraParams['query_params']))
|
if (isset($aExtraParams['query_params']))
|
||||||
{
|
{
|
||||||
@@ -2101,9 +2101,9 @@ class DashletHeaderDynamic extends Dashlet
|
|||||||
*/
|
*/
|
||||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
|
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
|
||||||
{
|
{
|
||||||
$sTitle = $this->aProperties['title'];
|
$sTitle = utils::HtmlEntities($this->aProperties['title']);
|
||||||
$sIcon = $this->aProperties['icon'];
|
$sIcon = $this->aProperties['icon'];
|
||||||
$sSubtitle = $this->aProperties['subtitle'];
|
$sSubtitle = utils::HtmlEntities($this->aProperties['subtitle']);
|
||||||
$sQuery = $this->aProperties['query'];
|
$sQuery = $this->aProperties['query'];
|
||||||
$sGroupBy = $this->aProperties['group_by'];
|
$sGroupBy = $this->aProperties['group_by'];
|
||||||
|
|
||||||
@@ -2111,12 +2111,12 @@ class DashletHeaderDynamic extends Dashlet
|
|||||||
$sClass = $oQuery->GetClass();
|
$sClass = $oQuery->GetClass();
|
||||||
|
|
||||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||||
$sIconPath = $oIconSelect->MakeFileUrl($sIcon);
|
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
|
||||||
|
|
||||||
$oPage->add('<div class="dashlet-content">');
|
$oPage->add('<div class="dashlet-content">');
|
||||||
$oPage->add('<div class="main_header">');
|
$oPage->add('<div class="main_header">');
|
||||||
|
|
||||||
$oPage->add('<img src="'.utils::HtmlEntities($sIconPath).'">');
|
$oPage->add('<img src="'.$sIconPath.'">');
|
||||||
|
|
||||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||||
|
|
||||||
@@ -2147,8 +2147,8 @@ class DashletHeaderDynamic extends Dashlet
|
|||||||
$sTitle = $this->oModelReflection->DictString($sTitle);
|
$sTitle = $this->oModelReflection->DictString($sTitle);
|
||||||
$sSubtitle = $this->oModelReflection->DictFormat($sSubtitle, $iTotal);
|
$sSubtitle = $this->oModelReflection->DictFormat($sSubtitle, $iTotal);
|
||||||
|
|
||||||
$oPage->add('<h1>'.$sTitle.'</h1>');
|
$oPage->add('<h1>'.utils::HtmlEntities($sTitle).'</h1>');
|
||||||
$oPage->add('<a class="summary">'.$sSubtitle.'</a>');
|
$oPage->add('<a class="summary">'.utils::HtmlEntities($sSubtitle).'</a>');
|
||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
|
|
||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
|
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.6">
|
||||||
<portals>
|
<portals>
|
||||||
|
<portal id="legacy_portal" _delta="define">
|
||||||
|
<url>portal/index.php</url>
|
||||||
|
<rank>1.0</rank>
|
||||||
|
<handler/>
|
||||||
|
<allow>
|
||||||
|
</allow>
|
||||||
|
<deny/>
|
||||||
|
</portal>
|
||||||
<portal id="backoffice" _delta="define">
|
<portal id="backoffice" _delta="define">
|
||||||
<url>pages/UI.php</url>
|
<url>pages/UI.php</url>
|
||||||
<rank>2.0</rank>
|
<rank>2.0</rank>
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ class DisplayBlock
|
|||||||
$sHtml .= $this->GetRenderContent($oPage, $aExtraParams, $sId);
|
$sHtml .= $this->GetRenderContent($oPage, $aExtraParams, $sId);
|
||||||
} catch (Exception $e)
|
} catch (Exception $e)
|
||||||
{
|
{
|
||||||
IssueLog::Error('Exception during GetDisplay: ' . $e->getMessage());
|
|
||||||
}
|
}
|
||||||
$sHtml .= "</div>\n";
|
$sHtml .= "</div>\n";
|
||||||
}
|
}
|
||||||
@@ -1898,8 +1898,7 @@ class MenuBlock extends DisplayBlock
|
|||||||
if ($this->m_sStyle == 'details')
|
if ($this->m_sStyle == 'details')
|
||||||
{
|
{
|
||||||
$sSearchAction = "window.location=\"{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}\"";
|
$sSearchAction = "window.location=\"{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}\"";
|
||||||
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::Format('UI:SearchFor_Class',
|
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), ENT_QUOTES, 'UTF-8')."\"><span class=\"search-button fa fa-search\" onclick='$sSearchAction'></span></div>";
|
||||||
MetaModel::GetName($sClass)), ENT_QUOTES, 'UTF-8')."\"><span class=\"search-button fas fa-search\" onclick='$sSearchAction'></span></div>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1910,8 +1909,7 @@ class MenuBlock extends DisplayBlock
|
|||||||
}
|
}
|
||||||
if (!$oPage->IsPrintableVersion() && ($sRefreshAction!=''))
|
if (!$oPage->IsPrintableVersion() && ($sRefreshAction!=''))
|
||||||
{
|
{
|
||||||
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::S('UI:Button:Refresh'),
|
$sHtml .= "<div class=\"actions_button icon_actions_button\" title=\"".htmlentities(Dict::S('UI:Button:Refresh'), ENT_QUOTES, 'UTF-8')."\"><span class=\"refresh-button fa fa-refresh\" onclick=\"$sRefreshAction\"></span></div>";
|
||||||
ENT_QUOTES, 'UTF-8')."\"><span class=\"refresh-button fas fa-sync\" onclick=\"$sRefreshAction\"></span></div>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
// iTop is free software; you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
// 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
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
@@ -46,6 +46,8 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
|||||||
protected $sBreadCrumbEntryIcon;
|
protected $sBreadCrumbEntryIcon;
|
||||||
protected $oCtx;
|
protected $oCtx;
|
||||||
|
|
||||||
|
protected $bHasCollapsibleSection = false;
|
||||||
|
|
||||||
public function __construct($sTitle, $bPrintable = false)
|
public function __construct($sTitle, $bPrintable = false)
|
||||||
{
|
{
|
||||||
parent::__construct($sTitle, $bPrintable);
|
parent::__construct($sTitle, $bPrintable);
|
||||||
@@ -69,16 +71,18 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
|||||||
$this->m_sMenu = "";
|
$this->m_sMenu = "";
|
||||||
$this->m_aMessages = array();
|
$this->m_aMessages = array();
|
||||||
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
|
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
|
||||||
$this->add_header("Content-type: text/html; charset=".self::PAGES_CHARSET);
|
$this->add_header("Content-type: text/html; charset=utf-8");
|
||||||
$this->add_header("Cache-control: 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('X-Frame-Options: deny');
|
||||||
$this->add_linked_stylesheet("../css/jquery.treeview.css");
|
$this->add_linked_stylesheet("../css/jquery.treeview.css");
|
||||||
$this->add_linked_stylesheet("../css/jquery.autocomplete.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-ui-timepicker-addon.css");
|
||||||
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
|
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
|
||||||
$this->add_linked_stylesheet("../css/magnific-popup.css");
|
$this->add_linked_stylesheet("../css/magnific-popup.css");
|
||||||
$this->add_linked_stylesheet("../css/c3.min.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/font-awesome.min.css");
|
||||||
$this->add_linked_stylesheet("../css/font-awesome/css/v4-shims.min.css");
|
|
||||||
|
|
||||||
$this->add_linked_script('../js/jquery.layout.min.js');
|
$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.ba-bbq.min.js');
|
||||||
@@ -220,6 +224,7 @@ EOF;
|
|||||||
);
|
);
|
||||||
$sTimeFormat = AttributeDateTime::GetFormat()->ToTimeFormat();
|
$sTimeFormat = AttributeDateTime::GetFormat()->ToTimeFormat();
|
||||||
$oTimeFormat = new DateTimeFormat($sTimeFormat);
|
$oTimeFormat = new DateTimeFormat($sTimeFormat);
|
||||||
|
$sJSLangShort = json_encode(strtolower(substr(Dict::GetUserLanguage(), 0, 2)));
|
||||||
|
|
||||||
// Date picker options
|
// Date picker options
|
||||||
$aPickerOptions = array(
|
$aPickerOptions = array(
|
||||||
@@ -237,38 +242,29 @@ EOF;
|
|||||||
$sJSDatePickerOptions = json_encode($aPickerOptions);
|
$sJSDatePickerOptions = json_encode($aPickerOptions);
|
||||||
|
|
||||||
// Time picker additional options
|
// Time picker additional options
|
||||||
$sUserLang = Dict::GetUserLanguage();
|
|
||||||
$sUserLangShort = strtolower(
|
|
||||||
substr($sUserLang, 0, 2)
|
|
||||||
);
|
|
||||||
// PR #40 : we are picking correct values for specific cases in dict files
|
|
||||||
// some languages are using codes like zh-CN or pt-BR
|
|
||||||
$sTimePickerLang = json_encode(
|
|
||||||
Dict::S('INTERNAL:JQuery-DatePicker:LangCode', $sUserLangShort)
|
|
||||||
);
|
|
||||||
$aPickerOptions['showOn'] = '';
|
$aPickerOptions['showOn'] = '';
|
||||||
$aPickerOptions['buttonImage'] = null;
|
$aPickerOptions['buttonImage'] = null;
|
||||||
$aPickerOptions['timeFormat'] = $oTimeFormat->ToDatePicker();
|
$aPickerOptions['timeFormat'] = $oTimeFormat->ToDatePicker();
|
||||||
$aPickerOptions['controlType'] = 'select';
|
$aPickerOptions['controlType'] = 'select';
|
||||||
$aPickerOptions['closeText'] = Dict::S('UI:Button:Ok');
|
$aPickerOptions['closeText'] = Dict::S('UI:Button:Ok');
|
||||||
$sJSDateTimePickerOptions = json_encode($aPickerOptions);
|
$sJSDateTimePickerOptions = json_encode($aPickerOptions);
|
||||||
if ($sTimePickerLang != '"en"')
|
if ($sJSLangShort != '"en"')
|
||||||
{
|
{
|
||||||
// More options that cannot be passed via json_encode since they must be evaluated client-side
|
// More options that cannot be passed via json_encode since they must be evaluated client-side
|
||||||
$aMoreJSOptions = ",
|
$aMoreJSOptions = ",
|
||||||
'timeText': $.timepicker.regional[$sTimePickerLang].timeText,
|
'timeText': $.timepicker.regional[$sJSLangShort].timeText,
|
||||||
'hourText': $.timepicker.regional[$sTimePickerLang].hourText,
|
'hourText': $.timepicker.regional[$sJSLangShort].hourText,
|
||||||
'minuteText': $.timepicker.regional[$sTimePickerLang].minuteText,
|
'minuteText': $.timepicker.regional[$sJSLangShort].minuteText,
|
||||||
'secondText': $.timepicker.regional[$sTimePickerLang].secondText,
|
'secondText': $.timepicker.regional[$sJSLangShort].secondText,
|
||||||
'currentText': $.timepicker.regional[$sTimePickerLang].currentText
|
'currentText': $.timepicker.regional[$sJSLangShort].currentText
|
||||||
}";
|
}";
|
||||||
$sJSDateTimePickerOptions = substr($sJSDateTimePickerOptions, 0, -1).$aMoreJSOptions;
|
$sJSDateTimePickerOptions = substr($sJSDateTimePickerOptions, 0, -1).$aMoreJSOptions;
|
||||||
}
|
}
|
||||||
$this->add_script(
|
$this->add_script(
|
||||||
<<< JS
|
<<< EOF
|
||||||
function GetUserLanguage()
|
function GetUserLanguage()
|
||||||
{
|
{
|
||||||
return $sTimePickerLang;
|
return $sJSLangShort;
|
||||||
}
|
}
|
||||||
function PrepareWidgets()
|
function PrepareWidgets()
|
||||||
{
|
{
|
||||||
@@ -300,12 +296,12 @@ EOF;
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
JS
|
EOF
|
||||||
);
|
);
|
||||||
|
|
||||||
// Attribute set tooltip on items
|
// Attribute set tooltip on items
|
||||||
$this->add_ready_script(
|
$this->add_ready_script(
|
||||||
<<<JS
|
<<<EOF
|
||||||
$('.attribute-set-item').each(function(){
|
$('.attribute-set-item').each(function(){
|
||||||
// Encoding only title as the content is already sanitized by the HTML attribute.
|
// Encoding only title as the content is already sanitized by the HTML attribute.
|
||||||
var sLabel = $('<div/>').text($(this).attr('data-label')).html();
|
var sLabel = $('<div/>').text($(this).attr('data-label')).html();
|
||||||
@@ -332,24 +328,25 @@ JS
|
|||||||
position: { corner: { target: 'topMiddle', tooltip: 'bottomLeft' }}
|
position: { corner: { target: 'topMiddle', tooltip: 'bottomLeft' }}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
JS
|
EOF
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make image attributes zoomable
|
// Make image attributes zoomable
|
||||||
$this->add_ready_script(
|
$this->add_ready_script(
|
||||||
<<<JS
|
<<<EOF
|
||||||
$('.view-image img').each(function(){
|
$('.view-image img').each(function(){
|
||||||
$(this).attr('href', $(this).attr('src'))
|
$(this).attr('href', $(this).attr('src'))
|
||||||
})
|
})
|
||||||
.magnificPopup({type: 'image', closeOnContentClick: true });
|
.magnificPopup({type: 'image', closeOnContentClick: true });
|
||||||
JS
|
EOF
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->add_init_script(
|
$this->add_init_script(
|
||||||
<<< JS
|
<<< EOF
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var myLayout; // a var is required because this page utilizes: myLayout.allowOverflow() method
|
var myLayout; // a var is required because this page utilizes: myLayout.allowOverflow() method
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
paneSize = GetUserPreference('menu_size', 300);
|
paneSize = GetUserPreference('menu_size', 300);
|
||||||
if ($('body').length > 0)
|
if ($('body').length > 0)
|
||||||
@@ -455,11 +452,11 @@ JS
|
|||||||
// Do something with the error !
|
// Do something with the error !
|
||||||
alert(err);
|
alert(err);
|
||||||
}
|
}
|
||||||
JS
|
EOF
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->add_ready_script(
|
$this->add_ready_script(
|
||||||
<<< JS
|
<<< EOF
|
||||||
|
|
||||||
// Adjust initial size
|
// Adjust initial size
|
||||||
$('.v-resizable').each( function()
|
$('.v-resizable').each( function()
|
||||||
@@ -620,7 +617,7 @@ JS
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
JS
|
EOF
|
||||||
);
|
);
|
||||||
$this->add_ready_script(InlineImage::FixImagesWidth());
|
$this->add_ready_script(InlineImage::FixImagesWidth());
|
||||||
/*
|
/*
|
||||||
@@ -631,7 +628,7 @@ JS
|
|||||||
|
|
||||||
$sUserPrefs = appUserPreferences::GetAsJSON();
|
$sUserPrefs = appUserPreferences::GetAsJSON();
|
||||||
$this->add_script(
|
$this->add_script(
|
||||||
<<<JS
|
<<<EOF
|
||||||
// // for JQuery history
|
// // for JQuery history
|
||||||
// function history_callback(hash)
|
// function history_callback(hash)
|
||||||
// {
|
// {
|
||||||
@@ -701,7 +698,7 @@ JS
|
|||||||
{
|
{
|
||||||
$('.ui-layout-center, .ui-layout-north, .ui-layout-south').css({display: 'block'});
|
$('.ui-layout-center, .ui-layout-north, .ui-layout-south').css({display: 'block'});
|
||||||
}
|
}
|
||||||
JS
|
EOF
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -767,11 +764,17 @@ JS
|
|||||||
switch ($iCount)
|
switch ($iCount)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
// No such dimension/silo => nothing to select
|
||||||
// No such dimension/silo or only one possible choice => nothing to select
|
|
||||||
$sHtml = '<div id="SiloSelection"><!-- nothing to select --></div>';
|
$sHtml = '<div id="SiloSelection"><!-- nothing to select --></div>';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// Only one possible choice... no selection, but display the value
|
||||||
|
$oOrg = $oSet->Fetch();
|
||||||
|
$sHtml = '<div id="SiloSelection">'.$oOrg->GetName().'</div>';
|
||||||
|
$sHtml .= '';
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$sHtml = '';
|
$sHtml = '';
|
||||||
$oAppContext = new ApplicationContext();
|
$oAppContext = new ApplicationContext();
|
||||||
@@ -847,7 +850,7 @@ JS
|
|||||||
$aParams = array(
|
$aParams = array(
|
||||||
'image_url' => $sImageUrl,
|
'image_url' => $sImageUrl,
|
||||||
'placeholder_image_url' => $sPlaceholderImageUrl,
|
'placeholder_image_url' => $sPlaceholderImageUrl,
|
||||||
'cache_uuid' => 'itop-newsroom-'.UserRights::GetUserId().'-'.md5(APPROOT),
|
'cache_uuid' => 'itop-newsroom-'.md5(APPROOT),
|
||||||
'providers' => $aProviderParams,
|
'providers' => $aProviderParams,
|
||||||
'display_limit' => (int)appUserPreferences::GetPref('newsroom_display_size', 7),
|
'display_limit' => (int)appUserPreferences::GetPref('newsroom_display_size', 7),
|
||||||
'labels' => array(
|
'labels' => array(
|
||||||
@@ -954,8 +957,8 @@ EOF
|
|||||||
$sNewEntry = json_encode(array(
|
$sNewEntry = json_encode(array(
|
||||||
'id' => $this->sBreadCrumbEntryId,
|
'id' => $this->sBreadCrumbEntryId,
|
||||||
'url' => $this->sBreadCrumbEntryUrl,
|
'url' => $this->sBreadCrumbEntryUrl,
|
||||||
'label' => htmlentities($this->sBreadCrumbEntryLabel, ENT_QUOTES, self::PAGES_CHARSET),
|
'label' => htmlentities($this->sBreadCrumbEntryLabel, ENT_QUOTES, 'UTF-8'),
|
||||||
'description' => htmlentities($this->sBreadCrumbEntryDescription, ENT_QUOTES, self::PAGES_CHARSET),
|
'description' => htmlentities($this->sBreadCrumbEntryDescription, ENT_QUOTES, 'UTF-8'),
|
||||||
'icon' => $this->sBreadCrumbEntryIcon,
|
'icon' => $this->sBreadCrumbEntryIcon,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -988,9 +991,8 @@ EOF
|
|||||||
$sHtml .= "<head>\n";
|
$sHtml .= "<head>\n";
|
||||||
// Make sure that Internet Explorer renders the page using its latest/highest/greatest standards !
|
// Make sure that Internet Explorer renders the page using its latest/highest/greatest standards !
|
||||||
$sHtml .= "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n";
|
$sHtml .= "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n";
|
||||||
$sPageCharset = self::PAGES_CHARSET;
|
$sHtml .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
|
||||||
$sHtml .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=$sPageCharset\" />\n";
|
$sHtml .= "<title>".htmlentities($this->s_title, ENT_QUOTES, 'UTF-8')."</title>\n";
|
||||||
$sHtml .= "<title>".htmlentities($this->s_title, ENT_QUOTES, $sPageCharset)."</title>\n";
|
|
||||||
$sHtml .= $this->get_base_tag();
|
$sHtml .= $this->get_base_tag();
|
||||||
// Stylesheets MUST be loaded before any scripts otherwise
|
// Stylesheets MUST be loaded before any scripts otherwise
|
||||||
// jQuery scripts may face some spurious problems (like failing on a 'reload')
|
// jQuery scripts may face some spurious problems (like failing on a 'reload')
|
||||||
@@ -1116,11 +1118,9 @@ EOF
|
|||||||
$sHtml .= '<p>'.Dict::Format('UI:ExplainPrintable',
|
$sHtml .= '<p>'.Dict::Format('UI:ExplainPrintable',
|
||||||
'<img src="../images/eye-open-555.png" style="vertical-align:middle">').'</p>';
|
'<img src="../images/eye-open-555.png" style="vertical-align:middle">').'</p>';
|
||||||
$sHtml .= "<div id=\"hiddeable_chapters\"></div>";
|
$sHtml .= "<div id=\"hiddeable_chapters\"></div>";
|
||||||
$sHtml .= '<button onclick="window.print()">'.htmlentities(Dict::S('UI:Button:GoPrint'), ENT_QUOTES,
|
$sHtml .= '<button onclick="window.print()">'.htmlentities(Dict::S('UI:Button:GoPrint'), ENT_QUOTES, 'UTF-8').'</button>';
|
||||||
self::PAGES_CHARSET).'</button>';
|
|
||||||
$sHtml .= ' ';
|
$sHtml .= ' ';
|
||||||
$sHtml .= '<button onclick="window.close()">'.htmlentities(Dict::S('UI:Button:Cancel'), ENT_QUOTES,
|
$sHtml .= '<button onclick="window.close()">'.htmlentities(Dict::S('UI:Button:Cancel'), ENT_QUOTES, 'UTF-8').'</button>';
|
||||||
self::PAGES_CHARSET).'</button>';
|
|
||||||
$sHtml .= ' ';
|
$sHtml .= ' ';
|
||||||
|
|
||||||
$sDefaultResolution = '27.7cm';
|
$sDefaultResolution = '27.7cm';
|
||||||
@@ -1158,7 +1158,7 @@ EOF;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render the text of the global search form
|
// Render the text of the global search form
|
||||||
$sText = htmlentities(utils::ReadParam('text', '', false, 'raw_data'), ENT_QUOTES, self::PAGES_CHARSET);
|
$sText = htmlentities(utils::ReadParam('text', '', false, 'raw_data'), ENT_QUOTES, 'UTF-8');
|
||||||
$sOnClick = " onclick=\"if ($('#global-search-input').val() != '') { $('#global-search form').submit(); } \"";
|
$sOnClick = " onclick=\"if ($('#global-search-input').val() != '') { $('#global-search form').submit(); } \"";
|
||||||
$sDefaultPlaceHolder = Dict::S("UI:YourSearch");
|
$sDefaultPlaceHolder = Dict::S("UI:YourSearch");
|
||||||
|
|
||||||
@@ -1213,7 +1213,7 @@ EOF;
|
|||||||
$oExitArchive = new JSPopupMenuItem('UI:ArchiveModeOff', Dict::S('UI:ArchiveModeOff'), 'return ArchiveMode(false);');
|
$oExitArchive = new JSPopupMenuItem('UI:ArchiveModeOff', Dict::S('UI:ArchiveModeOff'), 'return ArchiveMode(false);');
|
||||||
$aActions[$oExitArchive->GetUID()] = $oExitArchive->GetMenuItem();
|
$aActions[$oExitArchive->GetUID()] = $oExitArchive->GetMenuItem();
|
||||||
|
|
||||||
$sIcon = '<span class="fas fa-lock fa-1x"></span>';
|
$sIcon = '<span class="fa fa-lock fa-1x"></span>';
|
||||||
$this->AddApplicationMessage(Dict::S('UI:ArchiveMode:Banner'), $sIcon, Dict::S('UI:ArchiveMode:Banner+'));
|
$this->AddApplicationMessage(Dict::S('UI:ArchiveMode:Banner'), $sIcon, Dict::S('UI:ArchiveMode:Banner+'));
|
||||||
}
|
}
|
||||||
elseif (UserRights::CanBrowseArchive())
|
elseif (UserRights::CanBrowseArchive())
|
||||||
@@ -1259,8 +1259,8 @@ EOF;
|
|||||||
$sIcon =
|
$sIcon =
|
||||||
<<<EOF
|
<<<EOF
|
||||||
<span class="fa-stack fa-sm">
|
<span class="fa-stack fa-sm">
|
||||||
<i class="fas fa-pencil-alt fa-flip-horizontal fa-stack-1x"></i>
|
<i class="fa fa-pencil fa-flip-horizontal fa-stack-1x"></i>
|
||||||
<i class="fas fa-ban fa-stack-2x text-danger"></i>
|
<i class="fa fa-ban fa-stack-2x text-danger"></i>
|
||||||
</span>
|
</span>
|
||||||
EOF;
|
EOF;
|
||||||
|
|
||||||
@@ -1277,7 +1277,7 @@ EOF;
|
|||||||
{
|
{
|
||||||
$sHtmlIcon = $aMessage['icon'] ? $aMessage['icon'] : '';
|
$sHtmlIcon = $aMessage['icon'] ? $aMessage['icon'] : '';
|
||||||
$sHtmlMessage = $aMessage['message'];
|
$sHtmlMessage = $aMessage['message'];
|
||||||
$sTitleAttr = $aMessage['tip'] ? 'title="'.htmlentities($aMessage['tip'], ENT_QUOTES, self::PAGES_CHARSET).'"' : '';
|
$sTitleAttr = $aMessage['tip'] ? 'title="'.htmlentities($aMessage['tip'], ENT_QUOTES, 'UTF-8').'"' : '';
|
||||||
$sApplicationMessages .= '<div class="app-message" '.$sTitleAttr.'><span class="app-message-icon">'.$sHtmlIcon.'</span><span class="app-message-body">'.$sHtmlMessage.'</div></span>';
|
$sApplicationMessages .= '<div class="app-message" '.$sTitleAttr.'><span class="app-message-icon">'.$sHtmlIcon.'</span><span class="app-message-body">'.$sHtmlMessage.'</div></span>';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1308,11 +1308,9 @@ EOF;
|
|||||||
$sHtml .= '<!-- Beginning of the left pane -->';
|
$sHtml .= '<!-- Beginning of the left pane -->';
|
||||||
$sHtml .= ' <div class="ui-layout-north">';
|
$sHtml .= ' <div class="ui-layout-north">';
|
||||||
$sHtml .= ' <div id="header-logo">';
|
$sHtml .= ' <div id="header-logo">';
|
||||||
$sHtml .= ' <div id="top-left"></div><div id="logo"><a href="'
|
$sHtml .= ' <div id="top-left"></div><div id="logo"><a href="'.htmlentities($sIconUrl, ENT_QUOTES,
|
||||||
.htmlentities($sIconUrl, ENT_QUOTES, self::PAGES_CHARSET)
|
'UTF-8').'"><img src="'.$sDisplayIcon.'" title="'.htmlentities($sVersionString, ENT_QUOTES,
|
||||||
.'"><img src="'.$sDisplayIcon.'" title="'
|
'UTF-8').'" style="border:0; margin-top:16px; margin-right:40px;"/></a></div>';
|
||||||
.htmlentities($sVersionString, ENT_QUOTES, self::PAGES_CHARSET)
|
|
||||||
.'" style="border:0; margin-top:16px; margin-right:40px;"/></a></div>';
|
|
||||||
$sHtml .= ' </div>';
|
$sHtml .= ' </div>';
|
||||||
$sHtml .= ' <div class="header-menu">';
|
$sHtml .= ' <div class="header-menu">';
|
||||||
if (!MetaModel::GetConfig()->Get('demo_mode'))
|
if (!MetaModel::GetConfig()->Get('demo_mode'))
|
||||||
@@ -1442,6 +1440,38 @@ EOF;
|
|||||||
ExecutionKPI::ReportStats();
|
ExecutionKPI::ReportStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds init scripts for the collapsible sections
|
||||||
|
*/
|
||||||
|
private function outputCollapsibleSectionInit()
|
||||||
|
{
|
||||||
|
if (!$this->bHasCollapsibleSection)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->add_script(<<<'EOD'
|
||||||
|
function initCollapsibleSection(iSectionId, bOpenedByDefault, sSectionStateStorageKey)
|
||||||
|
{
|
||||||
|
var bStoredSectionState = JSON.parse(localStorage.getItem(sSectionStateStorageKey));
|
||||||
|
var bIsSectionOpenedInitially = (bStoredSectionState == null) ? bOpenedByDefault : bStoredSectionState;
|
||||||
|
|
||||||
|
if (bIsSectionOpenedInitially) {
|
||||||
|
$("#LnkCollapse_"+iSectionId).toggleClass("open");
|
||||||
|
$("#Collapse_"+iSectionId).toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#LnkCollapse_"+iSectionId).click(function(e) {
|
||||||
|
localStorage.setItem(sSectionStateStorageKey, !($("#Collapse_"+iSectionId).is(":visible")));
|
||||||
|
$("#LnkCollapse_"+iSectionId).toggleClass("open");
|
||||||
|
$("#Collapse_"+iSectionId).slideToggle("normal");
|
||||||
|
e.preventDefault(); // we don't want to do anything more (see #1030 : a non wanted tab switching was triggered)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
EOD
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
||||||
{
|
{
|
||||||
$this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
|
$this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
|
||||||
@@ -1512,6 +1542,43 @@ EOF;
|
|||||||
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel));
|
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function StartCollapsibleSection(
|
||||||
|
$sSectionLabel, $bOpenedByDefault = false, $sSectionStateStorageBusinessKey = ''
|
||||||
|
) {
|
||||||
|
$this->add($this->GetStartCollapsibleSection($sSectionLabel, $bOpenedByDefault,
|
||||||
|
$sSectionStateStorageBusinessKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function GetStartCollapsibleSection(
|
||||||
|
$sSectionLabel, $bOpenedByDefault = false, $sSectionStateStorageBusinessKey = ''
|
||||||
|
) {
|
||||||
|
$this->bHasCollapsibleSection = true;
|
||||||
|
$sHtml = '';
|
||||||
|
static $iSectionId = 0;
|
||||||
|
$sHtml .= '<a id="LnkCollapse_'.$iSectionId.'" class="CollapsibleLabel" href="#">'.$sSectionLabel.'</a></br>'."\n";
|
||||||
|
$sHtml .= '<div id="Collapse_'.$iSectionId.'" style="display:none">'."\n";
|
||||||
|
|
||||||
|
$oConfig = MetaModel::GetConfig();
|
||||||
|
$sSectionStateStorageKey = $oConfig->GetItopInstanceid().'/'.$sSectionStateStorageBusinessKey.'/collapsible-'.$iSectionId;
|
||||||
|
$sSectionStateStorageKey = json_encode($sSectionStateStorageKey);
|
||||||
|
$sOpenedByDefault = ($bOpenedByDefault) ? 'true' : 'false';
|
||||||
|
$this->add_ready_script("initCollapsibleSection($iSectionId, $sOpenedByDefault, '$sSectionStateStorageKey');");
|
||||||
|
|
||||||
|
$iSectionId++;
|
||||||
|
|
||||||
|
return $sHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function EndCollapsibleSection()
|
||||||
|
{
|
||||||
|
$this->add($this->GetEndCollapsibleSection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetEndCollapsibleSection()
|
||||||
|
{
|
||||||
|
return "</div>";
|
||||||
|
}
|
||||||
|
|
||||||
public function add($sHtml)
|
public function add($sHtml)
|
||||||
{
|
{
|
||||||
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != ''))
|
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != ''))
|
||||||
@@ -1580,7 +1647,7 @@ EOF;
|
|||||||
*/
|
*/
|
||||||
public function SetMessage($sHtmlMessage)
|
public function SetMessage($sHtmlMessage)
|
||||||
{
|
{
|
||||||
$sHtmlIcon = '<span class="fas fa-comment fa-1x"></span>';
|
$sHtmlIcon = '<span class="fa fa-comment fa-1x"></span>';
|
||||||
$this->AddApplicationMessage($sHtmlMessage, $sHtmlIcon);
|
$this->AddApplicationMessage($sHtmlMessage, $sHtmlIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,124 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Class LoginBasic
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class LoginBasic extends AbstractLoginFSMExtension
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return the list of supported login modes for this plugin
|
|
||||||
*
|
|
||||||
* @return array of supported login modes
|
|
||||||
*/
|
|
||||||
public function ListSupportedLoginModes()
|
|
||||||
{
|
|
||||||
return array('basic');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnModeDetection(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if (!isset($_SESSION['login_mode']))
|
|
||||||
{
|
|
||||||
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
|
|
||||||
{
|
|
||||||
$_SESSION['login_mode'] = 'basic';
|
|
||||||
}
|
|
||||||
elseif (isset($_SERVER['PHP_AUTH_USER']))
|
|
||||||
{
|
|
||||||
$_SESSION['login_mode'] = 'basic';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnReadCredentials(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'basic')
|
|
||||||
{
|
|
||||||
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
|
|
||||||
$_SESSION['login_temp_auth_user'] = $sAuthUser;
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function OnCheckCredentials(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'basic')
|
|
||||||
{
|
|
||||||
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
|
|
||||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
|
||||||
{
|
|
||||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
|
||||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnCredentialsOK(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'basic')
|
|
||||||
{
|
|
||||||
list($sAuthUser) = $this->GetAuthUserAndPassword();
|
|
||||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnError(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'basic')
|
|
||||||
{
|
|
||||||
LoginWebPage::HTTP401Error();
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnConnected(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'basic')
|
|
||||||
{
|
|
||||||
$_SESSION['can_logoff'] = true;
|
|
||||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function GetAuthUserAndPassword()
|
|
||||||
{
|
|
||||||
$sAuthUser = '';
|
|
||||||
$sAuthPwd = null;
|
|
||||||
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
|
|
||||||
{
|
|
||||||
list($sAuthUser, $sAuthPwd) = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (isset($_SERVER['PHP_AUTH_USER']))
|
|
||||||
{
|
|
||||||
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
|
|
||||||
// Unfortunately, the RFC is not clear about the encoding...
|
|
||||||
// IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8
|
|
||||||
// So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base
|
|
||||||
if (!LoginWebPage::LooksLikeUTF8($sAuthUser))
|
|
||||||
{
|
|
||||||
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
|
|
||||||
// Supposed to be harmless in case of a plain ASCII string...
|
|
||||||
$sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser);
|
|
||||||
}
|
|
||||||
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
|
|
||||||
if (!LoginWebPage::LooksLikeUTF8($sAuthPwd))
|
|
||||||
{
|
|
||||||
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
|
|
||||||
// Supposed to be harmless in case of a plain ASCII string...
|
|
||||||
$sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array($sAuthUser, $sAuthPwd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class LoginDefaultBefore
|
|
||||||
*/
|
|
||||||
class LoginDefaultBefore extends AbstractLoginFSMExtension
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Must be executed before the other login plugins
|
|
||||||
*
|
|
||||||
* @return array of supported login modes
|
|
||||||
*/
|
|
||||||
public function ListSupportedLoginModes()
|
|
||||||
{
|
|
||||||
return array('before');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnStart(&$iErrorCode)
|
|
||||||
{
|
|
||||||
$iErrorCode = LoginWebPage::EXIT_CODE_OK;
|
|
||||||
|
|
||||||
unset($_SESSION['login_temp_auth_user']);
|
|
||||||
|
|
||||||
// Check if proposed login mode is present and allowed
|
|
||||||
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
|
|
||||||
$sProposedLoginMode = utils::ReadParam('login_mode', '');
|
|
||||||
$index = array_search($sProposedLoginMode, $aAllowedLoginTypes);
|
|
||||||
if ($index !== false)
|
|
||||||
{
|
|
||||||
// Force login mode
|
|
||||||
$_SESSION['login_mode'] = $sProposedLoginMode;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unset($_SESSION['login_mode']);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnReadCredentials(&$iErrorCode)
|
|
||||||
{
|
|
||||||
// Check if proposed login mode is present and allowed
|
|
||||||
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
|
|
||||||
$sProposedLoginMode = utils::ReadParam('login_mode', '');
|
|
||||||
$index = array_search($sProposedLoginMode, $aAllowedLoginTypes);
|
|
||||||
if ($index !== false)
|
|
||||||
{
|
|
||||||
// Force login mode
|
|
||||||
LoginWebPage::SetLoginModeAndReload($sProposedLoginMode);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class LoginDefaultAfter
|
|
||||||
*/
|
|
||||||
class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExtension
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Must be executed after the other login plugins
|
|
||||||
*
|
|
||||||
* @return array of supported login modes
|
|
||||||
*/
|
|
||||||
public function ListSupportedLoginModes()
|
|
||||||
{
|
|
||||||
return array('after');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnError(&$iErrorCode)
|
|
||||||
{
|
|
||||||
self::ResetLoginSession();
|
|
||||||
$iOnExit = LoginWebPage::getIOnExit();
|
|
||||||
if ($iOnExit == LoginWebPage::EXIT_RETURN)
|
|
||||||
{
|
|
||||||
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
|
||||||
}
|
|
||||||
elseif ($iOnExit == LoginWebPage::EXIT_HTTP_401)
|
|
||||||
{
|
|
||||||
LoginWebPage::HTTP401Error(); // Error, exit
|
|
||||||
}
|
|
||||||
// LoginWebPage::EXIT_PROMPT
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnCredentialsOk(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if (!isset($_SESSION['login_mode']))
|
|
||||||
{
|
|
||||||
// If no plugin validated the user, exit
|
|
||||||
self::ResetLoginSession();
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute all actions to log out properly
|
|
||||||
*/
|
|
||||||
public function LogoutAction()
|
|
||||||
{
|
|
||||||
self::ResetLoginSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnConnected(&$iErrorCode)
|
|
||||||
{
|
|
||||||
unset($_SESSION['login_temp_auth_user']);
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hard reset of the session
|
|
||||||
private static function ResetLoginSession()
|
|
||||||
{
|
|
||||||
LoginWebPage::ResetSession();
|
|
||||||
foreach (array_keys($_SESSION) as $sKey)
|
|
||||||
{
|
|
||||||
if (utils::StartsWith($sKey, 'login_'))
|
|
||||||
{
|
|
||||||
unset($_SESSION[$sKey]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class LoginExternal
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class LoginExternal extends AbstractLoginFSMExtension
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the list of supported login modes for this plugin
|
|
||||||
*
|
|
||||||
* @return array of supported login modes
|
|
||||||
*/
|
|
||||||
public function ListSupportedLoginModes()
|
|
||||||
{
|
|
||||||
return array('external');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnModeDetection(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if (!isset($_SESSION['login_mode']))
|
|
||||||
{
|
|
||||||
$sAuthUser = $this->GetAuthUser();
|
|
||||||
if ($sAuthUser && (strlen($sAuthUser) > 0))
|
|
||||||
{
|
|
||||||
$_SESSION['login_mode'] = 'external';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnCheckCredentials(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'external')
|
|
||||||
{
|
|
||||||
$sAuthUser = $this->GetAuthUser();
|
|
||||||
if (!UserRights::CheckCredentials($sAuthUser, '', $_SESSION['login_mode'], 'external'))
|
|
||||||
{
|
|
||||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
|
||||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnCredentialsOK(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'external')
|
|
||||||
{
|
|
||||||
$sAuthUser = $this->GetAuthUser();
|
|
||||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'external', $_SESSION['login_mode']);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnConnected(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'external')
|
|
||||||
{
|
|
||||||
$_SESSION['can_logoff'] = false;
|
|
||||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function GetAuthUser()
|
|
||||||
{
|
|
||||||
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
|
|
||||||
eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value
|
|
||||||
/** @var string $sAuthUser */
|
|
||||||
return $sAuthUser; // Retrieve the value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class LoginForm
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class LoginForm extends AbstractLoginFSMExtension implements iLoginDataExtension
|
|
||||||
{
|
|
||||||
private $bForceFormOnError = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the list of supported login modes for this plugin
|
|
||||||
*
|
|
||||||
* @return array of supported login modes
|
|
||||||
*/
|
|
||||||
public function ListSupportedLoginModes()
|
|
||||||
{
|
|
||||||
return array('form');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnReadCredentials(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if (!isset($_SESSION['login_mode']) || ($_SESSION['login_mode'] == 'form'))
|
|
||||||
{
|
|
||||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
|
|
||||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
|
|
||||||
if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd))
|
|
||||||
{
|
|
||||||
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
|
|
||||||
{
|
|
||||||
// X-Combodo-Ajax is a special header automatically added to all ajax requests
|
|
||||||
// Let's reply that we're currently logged-out
|
|
||||||
header('HTTP/1.0 401 Unauthorized');
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No credentials yet, display the form
|
|
||||||
$oPage = LoginWebPage::NewLoginWebPage();
|
|
||||||
$oPage->DisplayLoginForm($this->bForceFormOnError);
|
|
||||||
$oPage->output();
|
|
||||||
$this->bForceFormOnError = false;
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$_SESSION['login_temp_auth_user'] = $sAuthUser;
|
|
||||||
$_SESSION['login_mode'] = 'form';
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnCheckCredentials(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'form')
|
|
||||||
{
|
|
||||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
|
|
||||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
|
|
||||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
|
||||||
{
|
|
||||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
|
||||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnCredentialsOK(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'form')
|
|
||||||
{
|
|
||||||
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']);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnError(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'form')
|
|
||||||
{
|
|
||||||
$this->bForceFormOnError = true;
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnConnected(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'form')
|
|
||||||
{
|
|
||||||
$_SESSION['can_logoff'] = true;
|
|
||||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return LoginTwigData
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function GetLoginData()
|
|
||||||
{
|
|
||||||
|
|
||||||
$aPostedVars = array('auth_user', 'auth_pwd');
|
|
||||||
$oLoginData = new LoginTwigData($aPostedVars);
|
|
||||||
|
|
||||||
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
|
|
||||||
$sAuthPwd = utils::ReadParam('suggest_pwd', '', true, 'raw_data');
|
|
||||||
|
|
||||||
$aData = array(
|
|
||||||
'sAuthUser' => $sAuthUser,
|
|
||||||
'sAuthPwd' => $sAuthPwd,
|
|
||||||
);
|
|
||||||
$oLoginData->AddBlockData('login_input', new LoginBlockData('loginforminput.html.twig', $aData));
|
|
||||||
$oLoginData->AddBlockData('login_submit', new LoginBlockData('loginformsubmit.html.twig'));
|
|
||||||
$oLoginData->AddBlockData('login_form_footer', new LoginBlockData('loginformfooter.html.twig'));
|
|
||||||
|
|
||||||
$bEnableResetPassword = empty(MetaModel::GetConfig()->Get('forgot_password')) ? true : MetaModel::GetConfig()->Get('forgot_password');
|
|
||||||
$sResetPasswordUrl = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?loginop=forgot_pwd';
|
|
||||||
|
|
||||||
$aData = array(
|
|
||||||
'bEnableResetPassword' => $bEnableResetPassword,
|
|
||||||
'sResetPasswordUrl' => $sResetPasswordUrl,
|
|
||||||
);
|
|
||||||
$oLoginData->AddBlockData('login_links', new LoginBlockData('loginformlinks.html.twig', $aData));
|
|
||||||
|
|
||||||
return $oLoginData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
use Combodo\iTop\TwigExtension;
|
|
||||||
|
|
||||||
class LoginTwigData
|
|
||||||
{
|
|
||||||
private $aBlockData;
|
|
||||||
private $aPostedVars;
|
|
||||||
private $sTwigLoaderPath;
|
|
||||||
private $sCSSFile;
|
|
||||||
/** @var array */
|
|
||||||
private $aJsFiles;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LoginTwigData constructor.
|
|
||||||
*
|
|
||||||
* @param array $aPostedVars
|
|
||||||
* @param string $sLoaderPath
|
|
||||||
* @param string $sCSSFile
|
|
||||||
* @param array $aJsFiles
|
|
||||||
*/
|
|
||||||
public function __construct($aPostedVars = array(), $sLoaderPath = null, $sCSSFile = null, $aJsFiles = array())
|
|
||||||
{
|
|
||||||
$this->aBlockData = array();
|
|
||||||
$this->aPostedVars = $aPostedVars;
|
|
||||||
$this->sTwigLoaderPath = $sLoaderPath;
|
|
||||||
$this->sCSSFile = $sCSSFile;
|
|
||||||
$this->aJsFiles = $aJsFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $sBlockName
|
|
||||||
* @param LoginBlockData $oBlockData
|
|
||||||
*/
|
|
||||||
public final function AddBlockData($sBlockName, $oBlockData)
|
|
||||||
{
|
|
||||||
$this->aBlockData[$sBlockName] = $oBlockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final function GetBlockData($sBlockName)
|
|
||||||
{
|
|
||||||
/** @var LoginBlockData $oBlockData */
|
|
||||||
$oBlockData = isset($this->aBlockData[$sBlockName]) ? $this->aBlockData[$sBlockName] : null;
|
|
||||||
return $oBlockData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final function GetPostedVars()
|
|
||||||
{
|
|
||||||
return $this->aPostedVars;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final function GetTwigLoaderPath()
|
|
||||||
{
|
|
||||||
return $this->sTwigLoaderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final function GetCSSFile()
|
|
||||||
{
|
|
||||||
return $this->sCSSFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function GetJsFiles()
|
|
||||||
{
|
|
||||||
return $this->aJsFiles;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginBlockData
|
|
||||||
{
|
|
||||||
private $sTwig;
|
|
||||||
private $aData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LoginBlockData constructor.
|
|
||||||
*
|
|
||||||
* @param string $sTwig
|
|
||||||
* @param array $aData
|
|
||||||
*/
|
|
||||||
public function __construct($sTwig, $aData = array())
|
|
||||||
{
|
|
||||||
$this->sTwig = $sTwig;
|
|
||||||
$this->aData = $aData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final function GetTwig()
|
|
||||||
{
|
|
||||||
return $this->sTwig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final function GetData()
|
|
||||||
{
|
|
||||||
return $this->aData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoginTwigContext
|
|
||||||
{
|
|
||||||
private $aLoginPluginList;
|
|
||||||
private $aPluginFormData;
|
|
||||||
private $aPostedVars;
|
|
||||||
private $oTwig;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->aLoginPluginList = LoginWebPage::GetLoginPluginList('iLoginDataExtension', false);
|
|
||||||
$this->aPluginFormData = array();
|
|
||||||
$aTwigLoaders = array();
|
|
||||||
$this->aPostedVars = array();
|
|
||||||
foreach ($this->aLoginPluginList as $oLoginPlugin)
|
|
||||||
{
|
|
||||||
/** @var \iLoginDataExtension $oLoginPlugin */
|
|
||||||
$oLoginData = $oLoginPlugin->GetLoginData();
|
|
||||||
$this->aPluginFormData[] = $oLoginData;
|
|
||||||
$sTwigLoaderPath = $oLoginData->GetTwigLoaderPath();
|
|
||||||
if ($sTwigLoaderPath != null)
|
|
||||||
{
|
|
||||||
$aTwigLoaders[] = new Twig_Loader_Filesystem($sTwigLoaderPath);
|
|
||||||
}
|
|
||||||
$this->aPostedVars = array_merge($this->aPostedVars, $oLoginData->GetPostedVars());
|
|
||||||
}
|
|
||||||
|
|
||||||
$oCoreLoader = new Twig_Loader_Filesystem(array(), APPROOT.'templates');
|
|
||||||
$aCoreTemplatesPaths = array('login', 'login/password');
|
|
||||||
// Having this path declared after the plugins let the plugins replace the core templates
|
|
||||||
$oCoreLoader->setPaths($aCoreTemplatesPaths);
|
|
||||||
// Having the core templates accessible within a different namespace offer the possibility to extend them while replacing them
|
|
||||||
$oCoreLoader->setPaths($aCoreTemplatesPaths, 'ItopCore');
|
|
||||||
$aTwigLoaders[] = $oCoreLoader;
|
|
||||||
|
|
||||||
$oLoader = new Twig_Loader_Chain($aTwigLoaders);
|
|
||||||
$this->oTwig = new Twig_Environment($oLoader);
|
|
||||||
TwigExtension::RegisterTwigExtensions($this->oTwig);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetDefaultVars()
|
|
||||||
{
|
|
||||||
$sLogo = 'itop-logo-external.png';
|
|
||||||
$sBrandingLogo = 'login-logo.png';
|
|
||||||
|
|
||||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
|
|
||||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
|
||||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?t='.utils::GetCacheBusterTimestamp();
|
|
||||||
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
|
||||||
{
|
|
||||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?t='.utils::GetCacheBusterTimestamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
$aVars = array(
|
|
||||||
'sAppRootUrl' => utils::GetAbsoluteUrlAppRoot(),
|
|
||||||
'aPluginFormData' => $this->GetPluginFormData(),
|
|
||||||
'sItopVersion' => ITOP_VERSION,
|
|
||||||
'sVersionShort' => $sVersionShort,
|
|
||||||
'sIconUrl' => $sIconUrl,
|
|
||||||
'sDisplayIcon' => $sDisplayIcon,
|
|
||||||
);
|
|
||||||
|
|
||||||
return $aVars;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Render(NiceWebPage $oPage, $sTwigFile, $aVars = array())
|
|
||||||
{
|
|
||||||
$oTemplate = $this->GetTwig()->load($sTwigFile);
|
|
||||||
$oPage->add($oTemplate->renderBlock('body', $aVars));
|
|
||||||
$oPage->add_script($oTemplate->renderBlock('script', $aVars));
|
|
||||||
$oPage->add_ready_script($oTemplate->renderBlock('ready_script', $aVars));
|
|
||||||
$oPage->add_style($oTemplate->renderBlock('css', $aVars));
|
|
||||||
|
|
||||||
// Render CSS links
|
|
||||||
foreach ($this->aPluginFormData as $oFormData)
|
|
||||||
{
|
|
||||||
/** @var \LoginTwigData $oFormData */
|
|
||||||
$sCSSFile = $oFormData->GetCSSFile();
|
|
||||||
if (!empty($sCSSFile))
|
|
||||||
{
|
|
||||||
$oPage->add_linked_stylesheet($sCSSFile);
|
|
||||||
}
|
|
||||||
$aJsFiles = $oFormData->GetJsFiles();
|
|
||||||
foreach ($aJsFiles as $sJsFile)
|
|
||||||
{
|
|
||||||
$oPage->add_linked_script($sJsFile);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function GetLoginPluginList()
|
|
||||||
{
|
|
||||||
return $this->aLoginPluginList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function GetPluginFormData()
|
|
||||||
{
|
|
||||||
return $this->aPluginFormData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function GetPostedVars()
|
|
||||||
{
|
|
||||||
return $this->aPostedVars;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \Twig_Environment
|
|
||||||
*/
|
|
||||||
public function GetTwig()
|
|
||||||
{
|
|
||||||
return $this->oTwig;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class LoginURL
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class LoginURL extends AbstractLoginFSMExtension
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $bErrorOccurred = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the list of supported login modes for this plugin
|
|
||||||
*
|
|
||||||
* @return array of supported login modes
|
|
||||||
*/
|
|
||||||
public function ListSupportedLoginModes()
|
|
||||||
{
|
|
||||||
return array('url');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnModeDetection(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if (!isset($_SESSION['login_mode']) && !$this->bErrorOccurred)
|
|
||||||
{
|
|
||||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
|
||||||
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
|
||||||
if (!empty($sAuthUser) && !empty($sAuthPwd))
|
|
||||||
{
|
|
||||||
$_SESSION['login_mode'] = 'url';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnReadCredentials(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'url')
|
|
||||||
{
|
|
||||||
$_SESSION['login_temp_auth_user'] = utils::ReadParam('auth_user', '', false, 'raw_data');
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnCheckCredentials(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'url')
|
|
||||||
{
|
|
||||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
|
||||||
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
|
||||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
|
||||||
{
|
|
||||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
|
||||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnCredentialsOK(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'url')
|
|
||||||
{
|
|
||||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
|
||||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnError(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'url')
|
|
||||||
{
|
|
||||||
$this->bErrorOccurred = true;
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function OnConnected(&$iErrorCode)
|
|
||||||
{
|
|
||||||
if ($_SESSION['login_mode'] == 'url')
|
|
||||||
{
|
|
||||||
$_SESSION['can_logoff'] = true;
|
|
||||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
|
||||||
}
|
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,75 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Maintenance message display functions
|
|
||||||
// Only included by approot.inc.php
|
|
||||||
//
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use a setup page to display the maintenance message
|
|
||||||
* @param $sTitle
|
|
||||||
* @param $sMessage
|
|
||||||
*/
|
|
||||||
function _MaintenanceSetupPageMessage($sTitle, $sMessage)
|
|
||||||
{
|
|
||||||
// Web Page
|
|
||||||
@include_once(APPROOT.'bootstrap.inc.php');
|
|
||||||
@include_once(APPROOT.'setup/setuppage.class.inc.php');
|
|
||||||
if (class_exists('SetupPage'))
|
|
||||||
{
|
|
||||||
$oP = new SetupPage($sTitle);
|
|
||||||
$oP->p("<h2>$sMessage</h2>");
|
|
||||||
$oP->output();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_MaintenanceTextMessage($sMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use simple text to display the maintenance message
|
|
||||||
* @param $sMessage
|
|
||||||
*/
|
|
||||||
function _MaintenanceTextMessage($sMessage)
|
|
||||||
{
|
|
||||||
echo $sMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use a simple HTML to display the maintenance message
|
|
||||||
* @param $sMessage
|
|
||||||
*/
|
|
||||||
function _MaintenanceHtmlMessage($sMessage)
|
|
||||||
{
|
|
||||||
echo '<html><body><div>'.$sMessage.'</div></body></html>';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use a simple JSON to display the maintenance message
|
|
||||||
*
|
|
||||||
* @param $sTitle
|
|
||||||
* @param $sMessage
|
|
||||||
*/
|
|
||||||
function _MaintenanceJsonMessage($sTitle, $sMessage)
|
|
||||||
{
|
|
||||||
@include_once(APPROOT.'bootstrap.inc.php');
|
|
||||||
@include_once(APPROOT."/application/ajaxwebpage.class.inc.php");
|
|
||||||
if (class_exists('ajax_page'))
|
|
||||||
{
|
|
||||||
$oP = new ajax_page($sTitle);
|
|
||||||
$oP->add_header('Access-Control-Allow-Origin: *');
|
|
||||||
$oP->SetContentType('application/json');
|
|
||||||
$oP->add('{"code":100, "message":"'.$sMessage.'"}');
|
|
||||||
$oP->Output();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_MaintenanceTextMessage($sMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
// iTop is free software; you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
// 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
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
@@ -291,22 +291,15 @@ EOF
|
|||||||
$aChildren = self::GetChildren($index);
|
$aChildren = self::GetChildren($index);
|
||||||
$sCSSClass = (count($aChildren) > 0) ? ' class="submenu"' : '';
|
$sCSSClass = (count($aChildren) > 0) ? ' class="submenu"' : '';
|
||||||
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
|
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
|
||||||
$sItemHtml = '<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'>';
|
|
||||||
if ($sHyperlink != '')
|
if ($sHyperlink != '')
|
||||||
{
|
{
|
||||||
$sLinkTarget = '';
|
$sTitle = utils::HtmlEntities($oMenu->GetTitle());
|
||||||
if ($oMenu->IsHyperLinkInNewWindow())
|
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$sTitle.'</a></li>');
|
||||||
{
|
|
||||||
$sLinkTarget .= ' target="_blank"';
|
|
||||||
}
|
|
||||||
$sItemHtml .= '<a href="'.$oMenu->GetHyperlink($aExtraParams).'"'.$sLinkTarget.'>'.$oMenu->GetTitle().'</a>';
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sItemHtml .= $oMenu->GetTitle();
|
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'>'.$oMenu->GetTitle().'</li>');
|
||||||
}
|
}
|
||||||
$sItemHtml .= '</li>';
|
|
||||||
$oPage->AddToMenu($sItemHtml);
|
|
||||||
if ($iActiveMenu == $index)
|
if ($iActiveMenu == $index)
|
||||||
{
|
{
|
||||||
$bActive = true;
|
$bActive = true;
|
||||||
@@ -614,15 +607,6 @@ abstract class MenuNode
|
|||||||
$aExtraParams['c[menu]'] = $this->GetMenuId();
|
$aExtraParams['c[menu]'] = $this->GetMenuId();
|
||||||
return $this->AddParams(utils::GetAbsoluteUrlAppRoot().'pages/UI.php', $aExtraParams);
|
return $this->AddParams(utils::GetAbsoluteUrlAppRoot().'pages/UI.php', $aExtraParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool true if the link should be opened in a new window
|
|
||||||
* @since 2.7.0 N°1283
|
|
||||||
*/
|
|
||||||
public function IsHyperLinkInNewWindow()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a limiting display condition for the same menu node. The conditions will be combined with a AND
|
* Add a limiting display condition for the same menu node. The conditions will be combined with a AND
|
||||||
@@ -922,7 +906,7 @@ class OQLMenuNode extends MenuNode
|
|||||||
$oBlock->Display($oPage, 0);
|
$oBlock->Display($oPage, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
$oPage->add("<p class=\"page-header\">$sIcon ".Dict::S($sTitle)."</p>");
|
$oPage->add("<p class=\"page-header\">$sIcon ".utils::HtmlEntities(Dict::S($sTitle))."</p>");
|
||||||
|
|
||||||
$aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
|
$aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
|
||||||
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);
|
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);
|
||||||
@@ -1006,12 +990,8 @@ class WebPageMenuNode extends MenuNode
|
|||||||
*/
|
*/
|
||||||
protected $sHyperlink;
|
protected $sHyperlink;
|
||||||
|
|
||||||
/** @var bool */
|
|
||||||
protected $bIsLinkInNewWindow;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a menu item that points to any web page (not only UI.php)
|
* Create a menu item that points to any web page (not only UI.php)
|
||||||
*
|
|
||||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||||
* @param string $sHyperlink URL to the page to load. Use relative URL if you want to keep the application portable !
|
* @param string $sHyperlink URL to the page to load. Use relative URL if you want to keep the application portable !
|
||||||
* @param integer $iParentIndex ID of the parent menu
|
* @param integer $iParentIndex ID of the parent menu
|
||||||
@@ -1020,17 +1000,12 @@ class WebPageMenuNode extends MenuNode
|
|||||||
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
|
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
|
||||||
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
|
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
|
||||||
* @param string $sEnableStimulus
|
* @param string $sEnableStimulus
|
||||||
* @param bool $bIsLinkInNewWindow for the {@link WebPageMenuNode::IsHyperLinkInNewWindow} method
|
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||||
$sMenuId, $sHyperlink, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null,
|
|
||||||
$iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null, $bIsLinkInNewWindow = false
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||||
$this->sHyperlink = $sHyperlink;
|
$this->sHyperlink = $sHyperlink;
|
||||||
$this->aReflectionProperties['url'] = $sHyperlink;
|
$this->aReflectionProperties['url'] = $sHyperlink;
|
||||||
$this->bIsLinkInNewWindow = $bIsLinkInNewWindow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1043,11 +1018,6 @@ class WebPageMenuNode extends MenuNode
|
|||||||
return $this->AddParams( $this->sHyperlink, $aExtraParams);
|
return $this->AddParams( $this->sHyperlink, $aExtraParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsHyperLinkInNewWindow()
|
|
||||||
{
|
|
||||||
return $this->bIsLinkInNewWindow;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param WebPage $oPage
|
* @param WebPage $oPage
|
||||||
* @param array $aExtraParams
|
* @param array $aExtraParams
|
||||||
|
|||||||
@@ -37,14 +37,14 @@ class NiceWebPage extends WebPage
|
|||||||
{
|
{
|
||||||
parent::__construct($s_title, $bPrintable);
|
parent::__construct($s_title, $bPrintable);
|
||||||
$this->m_aReadyScripts = array();
|
$this->m_aReadyScripts = array();
|
||||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.min.js');
|
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-3.3.1.min.js');
|
||||||
if(utils::IsDevelopmentEnvironment()) // Needed since many other plugins still rely on oldies like $.browser
|
if(utils::IsDevelopmentEnvironment()) // Needed since many other plugins still rely on oldies like $.browser
|
||||||
{
|
{
|
||||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate.dev.js');
|
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-3.0.1.dev.js');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate.prod.min.js');
|
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-3.0.1.prod.min.js');
|
||||||
}
|
}
|
||||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.11.4.custom.css');
|
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.11.4.custom.css');
|
||||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.11.4.custom.min.js');
|
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.11.4.custom.min.js');
|
||||||
@@ -75,8 +75,6 @@ class NiceWebPage extends WebPage
|
|||||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');
|
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');
|
||||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date.js');
|
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date.js');
|
||||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_time.js');
|
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_time.js');
|
||||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/clipboard.min.js');
|
|
||||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/clipboardwidget.js');
|
|
||||||
|
|
||||||
$this->add_dict_entries('UI:Combo');
|
$this->add_dict_entries('UI:Combo');
|
||||||
|
|
||||||
@@ -234,8 +232,7 @@ EOF
|
|||||||
foreach($aChoices as $sKey => $sValue)
|
foreach($aChoices as $sKey => $sValue)
|
||||||
{
|
{
|
||||||
$sSelected = ($sKey == $sDefaultValue) ? " SELECTED" : "";
|
$sSelected = ($sKey == $sDefaultValue) ? " SELECTED" : "";
|
||||||
$this->add("<option style=\"width: ".$iWidthPx." px;\" value=\"".htmlspecialchars($sKey)."\"$sSelected>".htmlentities($sValue,
|
$this->add("<option style=\"width: ".$iWidthPx." px;\" value=\"".htmlspecialchars($sKey)."\"$sSelected>".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."</option>");
|
||||||
ENT_QUOTES, self::PAGES_CHARSET)."</option>");
|
|
||||||
}
|
}
|
||||||
$this->add("</select>");
|
$this->add("</select>");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,15 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
require_once(APPROOT.'lib/tcpdf/tcpdf.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
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once(APPROOT.'application/utils.inc.php');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom class derived from TCPDF for providing custom headers and footers
|
* Custom class derived from TCPDF for providing custom headers and footers
|
||||||
*
|
|
||||||
* @author denis
|
* @author denis
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class iTopPDF extends TCPDF
|
class iTopPDF extends TCPDF
|
||||||
{
|
{
|
||||||
protected $sDocumentTitle;
|
protected $sDocumentTitle;
|
||||||
|
|
||||||
/**
|
|
||||||
* Shortcut for {@link TCPDF::SetFont}, to use the font configured
|
|
||||||
*
|
|
||||||
* @param string $style
|
|
||||||
* @param int $size
|
|
||||||
* @param string $fontfile
|
|
||||||
* @param string $subset
|
|
||||||
* @param bool $out
|
|
||||||
*
|
|
||||||
* @uses \TCPDF::SetFont()
|
|
||||||
* @uses \iTopPDF::GetPdfFont()
|
|
||||||
* @since 2.7
|
|
||||||
*/
|
|
||||||
public function SetFontParams($style, $size, $fontfile='', $subset='default', $out=true)
|
|
||||||
{
|
|
||||||
$siTopFont = self::GetPdfFont();
|
|
||||||
$this->SetFont($siTopFont, $style, $size, $fontfile, $subset, $out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SetDocumentTitle($sDocumentTitle)
|
public function SetDocumentTitle($sDocumentTitle)
|
||||||
{
|
{
|
||||||
$this->sDocumentTitle = $sDocumentTitle;
|
$this->sDocumentTitle = $sDocumentTitle;
|
||||||
@@ -55,29 +17,26 @@ class iTopPDF extends TCPDF
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds the custom header. Called for each new page.
|
* Builds the custom header. Called for each new page.
|
||||||
*
|
|
||||||
* @see TCPDF::Header()
|
* @see TCPDF::Header()
|
||||||
*/
|
*/
|
||||||
public function Header()
|
public function Header()
|
||||||
{
|
{
|
||||||
// Title
|
// Title
|
||||||
// Set font
|
// Set font
|
||||||
$this->SetFontParams('B', 10);
|
$this->SetFont('dejavusans', 'B', 10);
|
||||||
|
|
||||||
$iPageNumberWidth = 25;
|
$iPageNumberWidth = 25;
|
||||||
$aMargins = $this->getMargins();
|
$aMargins = $this->getMargins();
|
||||||
|
|
||||||
// Display the title (centered)
|
// Display the title (centered)
|
||||||
$this->SetXY($aMargins['left'] + $iPageNumberWidth, 0);
|
$this->SetXY($aMargins['left'] + $iPageNumberWidth, 0);
|
||||||
$this->MultiCell($this->getPageWidth() - $aMargins['left'] - $aMargins['right'] - 2 * $iPageNumberWidth, 15, $this->sDocumentTitle,
|
$this->MultiCell($this->getPageWidth() - $aMargins['left'] - $aMargins['right'] - 2*$iPageNumberWidth, 15, $this->sDocumentTitle, 0, 'C', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
|
||||||
0, 'C', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
|
$this->SetFont('dejavusans', '', 10);
|
||||||
$this->SetFontParams('', 10);
|
|
||||||
|
|
||||||
// Display the page number (right aligned)
|
// Display the page number (right aligned)
|
||||||
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
|
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
|
||||||
$this->MultiCell($iPageNumberWidth, 15, Dict::Format('Core:BulkExport:PDF:PageNumber', $this->page), 0, 'R', false, 0 /* $ln */, '',
|
$this->MultiCell($iPageNumberWidth, 15, Dict::Format('Core:BulkExport:PDF:PageNumber' ,$this->page), 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
|
||||||
'', true, 0, false, true, 15, 'M' /* $valign */);
|
|
||||||
|
|
||||||
// Branding logo
|
// Branding logo
|
||||||
$sBrandingIcon = APPROOT.'images/itop-logo.png';
|
$sBrandingIcon = APPROOT.'images/itop-logo.png';
|
||||||
if (file_exists(MODULESROOT.'branding/main-logo.png'))
|
if (file_exists(MODULESROOT.'branding/main-logo.png'))
|
||||||
@@ -92,18 +51,6 @@ class iTopPDF extends TCPDF
|
|||||||
{
|
{
|
||||||
// No footer
|
// No footer
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
|
|
||||||
* @return string font in the config file (export_pdf_font)
|
|
||||||
*/
|
|
||||||
public static function GetPdfFont()
|
|
||||||
{
|
|
||||||
$oConfig = utils::GetConfig();
|
|
||||||
$sPdfFont = $oConfig->Get('export_pdf_font');
|
|
||||||
|
|
||||||
return $sPdfFont;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -111,45 +58,49 @@ class iTopPDF extends TCPDF
|
|||||||
*/
|
*/
|
||||||
class PDFPage extends WebPage
|
class PDFPage extends WebPage
|
||||||
{
|
{
|
||||||
/** @var \iTopPDF Instance of the TCPDF object for creating the PDF */
|
/**
|
||||||
|
* Instance of the TCPDF object for creating the PDF
|
||||||
|
* @var TCPDF
|
||||||
|
*/
|
||||||
protected $oPdf;
|
protected $oPdf;
|
||||||
|
|
||||||
public function __construct($s_title, $sPageFormat = 'A4', $sPageOrientation = 'L')
|
public function __construct($s_title, $sPageFormat = 'A4', $sPageOrientation = 'L')
|
||||||
{
|
{
|
||||||
parent::__construct($s_title);
|
parent::__construct($s_title);
|
||||||
define(K_PATH_FONTS, APPROOT.'lib/combodo/tcpdf/fonts');
|
define(K_PATH_FONTS, APPROOT.'lib/tcpdf/fonts');
|
||||||
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, self::PAGES_CHARSET, false);
|
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, 'UTF-8', false);
|
||||||
|
|
||||||
// set document information
|
// set document information
|
||||||
$this->oPdf->SetCreator(PDF_CREATOR);
|
$this->oPdf->SetCreator(PDF_CREATOR);
|
||||||
$this->oPdf->SetAuthor('iTop');
|
$this->oPdf->SetAuthor('iTop');
|
||||||
$this->oPdf->SetTitle($s_title);
|
$this->oPdf->SetTitle($s_title);
|
||||||
$this->oPdf->SetDocumentTitle($s_title);
|
$this->oPdf->SetDocumentTitle($s_title);
|
||||||
|
|
||||||
$this->oPdf->setFontSubsetting(true);
|
$this->oPdf->setFontSubsetting(true);
|
||||||
|
|
||||||
|
// Set font
|
||||||
// dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
|
// dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
|
||||||
$this->oPdf->SetFontParams('', 10, '', true);
|
$this->oPdf->SetFont('dejavusans', '', 10, '', true);
|
||||||
|
|
||||||
// set auto page breaks
|
// set auto page breaks
|
||||||
$this->oPdf->SetAutoPageBreak(true, 15); // 15 mm break margin at the bottom
|
$this->oPdf->SetAutoPageBreak(true, 15); // 15 mm break margin at the bottom
|
||||||
$this->oPdf->SetTopMargin(15);
|
$this->oPdf->SetTopMargin(15);
|
||||||
|
|
||||||
// Add a page, we're ready to start
|
// Add a page, we're ready to start
|
||||||
$this->oPdf->AddPage();
|
$this->oPdf->AddPage();
|
||||||
|
|
||||||
$this->SetContentDisposition('inline', $s_title.'.pdf');
|
$this->SetContentDisposition('inline', $s_title.'.pdf');
|
||||||
$this->SetDefaultStyle();
|
$this->SetDefaultStyle();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a default style (suitable for printing) to be included each time $this->oPdf->writeHTML() is called
|
* Sets a default style (suitable for printing) to be included each time $this->oPdf->writeHTML() is called
|
||||||
*/
|
*/
|
||||||
protected function SetDefaultStyle()
|
protected function SetDefaultStyle()
|
||||||
{
|
{
|
||||||
$this->add_style(
|
$this->add_style(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
table {
|
table {
|
||||||
padding: 2pt;
|
padding: 2pt;
|
||||||
}
|
}
|
||||||
@@ -173,21 +124,19 @@ td.icon {
|
|||||||
width: 30px;
|
width: 30px;
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get access to the underlying TCPDF object
|
* Get access to the underlying TCPDF object
|
||||||
*
|
* @return TCPDF
|
||||||
* @return \iTopPDF
|
|
||||||
*/
|
*/
|
||||||
public function get_tcpdf()
|
public function get_tcpdf()
|
||||||
{
|
{
|
||||||
$this->flush();
|
$this->flush();
|
||||||
|
|
||||||
return $this->oPdf;
|
return $this->oPdf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the currently buffered HTML content into the PDF. This can be useful:
|
* Writes the currently buffered HTML content into the PDF. This can be useful:
|
||||||
* - to sync the flow in case you want to access the underlying TCPDF object for some specific/graphic output
|
* - to sync the flow in case you want to access the underlying TCPDF object for some specific/graphic output
|
||||||
@@ -207,42 +156,39 @@ EOF
|
|||||||
$this->s_content = '';
|
$this->s_content = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not the page is a PDF page
|
* Whether or not the page is a PDF page
|
||||||
*
|
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function is_pdf()
|
public function is_pdf()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the PDF document and returns the PDF content as a string
|
* Generates the PDF document and returns the PDF content as a string
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
* @see WebPage::output()
|
* @see WebPage::output()
|
||||||
*/
|
*/
|
||||||
public function output()
|
public function output()
|
||||||
{
|
{
|
||||||
$this->add_header('Content-type: application/x-pdf');
|
$this->add_header('Content-type: application/x-pdf');
|
||||||
if (!empty($this->sContentDisposition))
|
if (!empty($this->sContentDisposition))
|
||||||
{
|
{
|
||||||
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
|
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
|
||||||
}
|
}
|
||||||
foreach ($this->a_headers as $s_header)
|
foreach($this->a_headers as $s_header)
|
||||||
{
|
{
|
||||||
header($s_header);
|
header($s_header);
|
||||||
}
|
}
|
||||||
$this->flush();
|
$this->flush();
|
||||||
echo $this->oPdf->Output($this->s_title.'.pdf', 'S');
|
echo $this->oPdf->Output($this->s_title.'.pdf', 'S');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_pdf()
|
public function get_pdf()
|
||||||
{
|
{
|
||||||
$this->flush();
|
$this->flush();
|
||||||
|
|
||||||
return $this->oPdf->Output($this->s_title.'.pdf', 'S');
|
return $this->oPdf->Output($this->s_title.'.pdf', 'S');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1031
application/portalwebpage.class.inc.php
Normal file
1031
application/portalwebpage.class.inc.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
// iTop is free software; you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
// 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
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
@@ -15,15 +15,12 @@
|
|||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
|
||||||
require_once(APPROOT.'/application/utils.inc.php');
|
|
||||||
require_once(APPROOT.'/core/contexttag.class.inc.php');
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File to include to initialize the datamodel in memory
|
* File to include to initialize the datamodel in memory
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -35,16 +32,10 @@ register_shutdown_function(function()
|
|||||||
$sReservedMemory = null;
|
$sReservedMemory = null;
|
||||||
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
|
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
|
||||||
{
|
{
|
||||||
IssueLog::error($err['message']);
|
|
||||||
if (strpos($err['message'], 'Allowed memory size of') !== false)
|
if (strpos($err['message'], 'Allowed memory size of') !== false)
|
||||||
{
|
{
|
||||||
$sLimit = ini_get('memory_limit');
|
$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";
|
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)
|
|
||||||
{
|
|
||||||
$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
|
||||||
{
|
{
|
||||||
@@ -53,6 +44,9 @@ register_shutdown_function(function()
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||||
|
require_once(APPROOT.'/application/utils.inc.php');
|
||||||
|
require_once(APPROOT.'/core/contexttag.class.inc.php');
|
||||||
session_name('itop-'.md5(APPROOT));
|
session_name('itop-'.md5(APPROOT));
|
||||||
session_start();
|
session_start();
|
||||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
||||||
@@ -85,4 +79,4 @@ else
|
|||||||
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
||||||
}
|
}
|
||||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||||
@@ -26,8 +26,6 @@
|
|||||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
class privUITransaction
|
class privUITransaction
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@@ -99,9 +97,10 @@ class privUITransaction
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The original (and by default) mechanism for storing transaction information
|
* The original mechanism for storing transaction information as an array in the $_SESSION variable
|
||||||
* 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
|
class privUITransactionSession
|
||||||
{
|
{
|
||||||
@@ -194,9 +193,35 @@ class privUITransactionSession
|
|||||||
*/
|
*/
|
||||||
class privUITransactionFile
|
class privUITransactionFile
|
||||||
{
|
{
|
||||||
|
/** @var int Value to use when no user logged */
|
||||||
|
const UNAUTHENTICATED_USER_ID = -666;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int The new transaction identifier
|
* @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 identifier of the new transaction
|
||||||
|
*
|
||||||
|
* @throws \SecurityException
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
|
*
|
||||||
|
* @since 2.6.5 2.7.6 3.0.0 security hardening + throws SecurityException if no user logged
|
||||||
*/
|
*/
|
||||||
public static function GetNewTransactionId()
|
public static function GetNewTransactionId()
|
||||||
{
|
{
|
||||||
@@ -206,84 +231,102 @@ class privUITransactionFile
|
|||||||
{
|
{
|
||||||
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
|
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
|
||||||
}
|
}
|
||||||
// condition avoids race condition N°2345
|
/** @noinspection MkdirRaceConditionInspection */
|
||||||
// See https://github.com/kalessil/phpinspectionsea/blob/master/docs/probable-bugs.md#mkdir-race-condition
|
if (!@mkdir(APPROOT.'data/transactions'))
|
||||||
if (!mkdir($concurrentDirectory = APPROOT.'data/transactions') && !is_dir($concurrentDirectory))
|
|
||||||
{
|
{
|
||||||
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.');
|
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'))
|
if (!is_writable(APPROOT.'data/transactions'))
|
||||||
{
|
{
|
||||||
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
|
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
|
||||||
}
|
}
|
||||||
self::CleanupOldTransactions();
|
|
||||||
$id = basename(tempnam(APPROOT.'data/transactions', static::GetUserPrefix()));
|
|
||||||
self::Info('GetNewTransactionId: Created transaction: '.$id);
|
|
||||||
|
|
||||||
return (string)$id;
|
$iCurrentUserId = static::GetCurrentUserId();
|
||||||
|
|
||||||
|
self::CleanupOldTransactions();
|
||||||
|
|
||||||
|
$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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
|
* 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
|
* the session so that another call to IsTransactionValid for the same transaction id
|
||||||
* will return false
|
* will return false
|
||||||
|
*
|
||||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
||||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
* @param bool $bRemoveTransaction True if the transaction must be removed
|
||||||
|
*
|
||||||
* @return bool True if the transaction is valid, false otherwise
|
* @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)
|
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||||
{
|
{
|
||||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
clearstatcache(true, $sFilepath);
|
clearstatcache(true, $sFilepath);
|
||||||
$bResult = file_exists($sFilepath);
|
$bResult = file_exists($sFilepath);
|
||||||
if ($bResult)
|
|
||||||
|
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 ($bRemoveTransaction)
|
$bResult = @unlink($sFilepath);
|
||||||
|
if (!$bResult)
|
||||||
{
|
{
|
||||||
$bResult = @unlink($sFilepath);
|
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
||||||
if (!$bResult)
|
}
|
||||||
{
|
else
|
||||||
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
{
|
||||||
}
|
self::Info('IsTransactionValid: OK. Removed 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;
|
return $bResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the transaction specified by its id
|
* Removes the transaction specified by its id
|
||||||
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
|
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
|
||||||
* @return void
|
* @return bool true if the token can be removed
|
||||||
|
*
|
||||||
|
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
|
||||||
*/
|
*/
|
||||||
public static function RemoveTransaction($id)
|
public static function RemoveTransaction($id)
|
||||||
{
|
{
|
||||||
$bSuccess = true;
|
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
$bResult = static::IsTransactionValid($id, true);
|
||||||
clearstatcache(true, $sFilepath);
|
if (false === $bResult) {
|
||||||
if(!file_exists($sFilepath))
|
self::Error("RemoveTransaction: Transaction '$id' is invalid. Pending transactions:\n"
|
||||||
{
|
.implode("\n", self::GetPendingTransactions()));
|
||||||
$bSuccess = false;
|
return false;
|
||||||
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
|
||||||
}
|
}
|
||||||
$bSuccess = @unlink($sFilepath);
|
|
||||||
if (!$bSuccess)
|
return true;
|
||||||
{
|
|
||||||
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self::Info('RemoveTransaction: OK '.$id);
|
|
||||||
}
|
|
||||||
return $bSuccess;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -348,22 +391,35 @@ class privUITransactionFile
|
|||||||
{
|
{
|
||||||
self::Write('Error | '.$sText);
|
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)
|
protected static function Write($sText)
|
||||||
{
|
{
|
||||||
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
|
if (false === static::IsLogEnabled()) {
|
||||||
if ($bLogEnabled)
|
return;
|
||||||
{
|
}
|
||||||
|
|
||||||
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
|
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
|
||||||
if ($hLogFile !== false)
|
if ($hLogFile !== false) {
|
||||||
{
|
|
||||||
flock($hLogFile, LOCK_EX);
|
flock($hLogFile, LOCK_EX);
|
||||||
$sDate = date('Y-m-d H:i:s');
|
$sDate = date('Y-m-d H:i:s');
|
||||||
fwrite($hLogFile, "$sDate | $sText\n");
|
fwrite($hLogFile, "$sDate | $sText\n");
|
||||||
fflush($hLogFile);
|
fflush($hLogFile);
|
||||||
flock($hLogFile, LOCK_UN);
|
flock($hLogFile, LOCK_UN);
|
||||||
fclose($hLogFile);
|
fclose($hLogFile);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Combodo\iTop;
|
|
||||||
|
|
||||||
use AttributeDateTime;
|
|
||||||
use Dict;
|
|
||||||
use Exception;
|
|
||||||
use MetaModel;
|
|
||||||
use Twig_Environment;
|
|
||||||
use Twig_SimpleFilter;
|
|
||||||
use Twig_SimpleFunction;
|
|
||||||
use utils;
|
|
||||||
|
|
||||||
class TwigExtension
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Registers Twig extensions such as filters or functions.
|
|
||||||
* It allows us to access some stuff directly in twig.
|
|
||||||
*
|
|
||||||
* @param \Twig_Environment $oTwigEnv
|
|
||||||
*/
|
|
||||||
public static function RegisterTwigExtensions(Twig_Environment &$oTwigEnv)
|
|
||||||
{
|
|
||||||
// Filter to translate a string via the Dict::S function
|
|
||||||
// Usage in twig: {{ 'String:ToTranslate'|dict_s }}
|
|
||||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_s',
|
|
||||||
function ($sStringCode, $sDefault = null, $bUserLanguageOnly = false) {
|
|
||||||
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Filter to format a string via the Dict::Format function
|
|
||||||
// Usage in twig: {{ 'String:ToTranslate'|dict_format() }}
|
|
||||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_format',
|
|
||||||
function ($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null) {
|
|
||||||
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Filter to format output
|
|
||||||
// example a DateTime is converted to user format
|
|
||||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
|
||||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('date_format',
|
|
||||||
function ($sDate) {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate)))
|
|
||||||
{
|
|
||||||
return AttributeDateTime::GetFormat()->Format($sDate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception $e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return $sDate;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// Filter to format output
|
|
||||||
// example a DateTime is converted to user format
|
|
||||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
|
||||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('size_format',
|
|
||||||
function ($sSize) {
|
|
||||||
return utils::BytesToFriendlyFormat($sSize);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Filter to enable base64 encode/decode
|
|
||||||
// Usage in twig: {{ 'String to encode'|base64_encode }}
|
|
||||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_encode', 'base64_encode'));
|
|
||||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_decode', 'base64_decode'));
|
|
||||||
|
|
||||||
// Filter to enable json decode (encode already exists)
|
|
||||||
// Usage in twig: {{ aSomeArray|json_decode }}
|
|
||||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('json_decode', function ($sJsonString, $bAssoc = false) {
|
|
||||||
return json_decode($sJsonString, $bAssoc);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
// Filter to add itopversion to an url
|
|
||||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_itop_version', function ($sUrl) {
|
|
||||||
if (strpos($sUrl, '?') === false)
|
|
||||||
{
|
|
||||||
$sUrl = $sUrl."?itopversion=".ITOP_VERSION;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sUrl = $sUrl."&itopversion=".ITOP_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sUrl;
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Filter to add a module's version to an url
|
|
||||||
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_module_version', function ($sUrl, $sModuleName) {
|
|
||||||
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
|
|
||||||
|
|
||||||
if (strpos($sUrl, '?') === false)
|
|
||||||
{
|
|
||||||
$sUrl = $sUrl."?moduleversion=".$sModuleVersion;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sUrl = $sUrl."&moduleversion=".$sModuleVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sUrl;
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Function to check our current environment
|
|
||||||
// Usage in twig: {% if is_development_environment() %}
|
|
||||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('is_development_environment', function()
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -373,10 +373,10 @@ EOF
|
|||||||
$sHTML .= "</form>\n";
|
$sHTML .= "</form>\n";
|
||||||
$sHTML .= '</div></div>';
|
$sHTML .= '</div></div>';
|
||||||
|
|
||||||
$sDialogTitle = addslashes($sTitle);
|
$sDialogTitleSanitized = addslashes(utils::HtmlToText($sTitle));
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
$('#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 });
|
$('#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 });
|
||||||
$('#fs_{$this->iId}').bind('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);
|
$('#fs_{$this->iId}').bind('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);
|
||||||
$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);
|
$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);
|
||||||
EOF
|
EOF
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class UIHTMLEditorWidget
|
|||||||
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
|
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
|
||||||
$aConfig['language'] = $sLanguage;
|
$aConfig['language'] = $sLanguage;
|
||||||
$aConfig['contentsLanguage'] = $sLanguage;
|
$aConfig['contentsLanguage'] = $sLanguage;
|
||||||
$aConfig['extraPlugins'] = 'disabler,codesnippet';
|
$aConfig['extraPlugins'] = 'disabler';
|
||||||
$sWidthSpec = addslashes(trim($this->m_oAttDef->GetWidth()));
|
$sWidthSpec = addslashes(trim($this->m_oAttDef->GetWidth()));
|
||||||
if ($sWidthSpec != '')
|
if ($sWidthSpec != '')
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
<?php
|
<?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
|
|
||||||
*/
|
|
||||||
|
|
||||||
use ScssPhp\ScssPhp\Compiler;
|
use Leafo\ScssPhp\Compiler;
|
||||||
|
|
||||||
|
// Copyright (C) 2010-2017 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/>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,6 +26,13 @@ use ScssPhp\ScssPhp\Compiler;
|
|||||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
require_once(APPROOT.'core/metamodel.class.php');
|
||||||
|
require_once(APPROOT.'core/config.class.inc.php');
|
||||||
|
require_once(APPROOT.'application/transaction.class.inc.php');
|
||||||
|
require_once(APPROOT.'application/Html2Text.php');
|
||||||
|
require_once(APPROOT.'application/Html2TextException.php');
|
||||||
|
|
||||||
define('ITOP_CONFIG_FILE', 'config-itop.php');
|
define('ITOP_CONFIG_FILE', 'config-itop.php');
|
||||||
define('ITOP_DEFAULT_CONFIG_FILE', APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE);
|
define('ITOP_DEFAULT_CONFIG_FILE', APPCONF.ITOP_DEFAULT_ENV.'/'.ITOP_CONFIG_FILE);
|
||||||
|
|
||||||
@@ -44,13 +51,8 @@ class FileUploadException extends Exception
|
|||||||
*/
|
*/
|
||||||
class utils
|
class utils
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* Cache when getting config from disk or set externally (using {@link SetConfig})
|
|
||||||
* @internal
|
|
||||||
* @var Config $oConfig
|
|
||||||
* @see GetConfig
|
|
||||||
*/
|
|
||||||
private static $oConfig = null;
|
private static $oConfig = null;
|
||||||
|
private static $m_bCASClient = false;
|
||||||
|
|
||||||
// Parameters loaded from a file, parameters of the page/command line still have precedence
|
// Parameters loaded from a file, parameters of the page/command line still have precedence
|
||||||
private static $m_aParamsFromFile = null;
|
private static $m_aParamsFromFile = null;
|
||||||
@@ -111,10 +113,10 @@ class utils
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the source file from which the parameter has been found,
|
* Return the source file from which the parameter has been found,
|
||||||
* useful when it comes to pass user credential to a process executed
|
* usefull when it comes to pass user credential to a process executed
|
||||||
* in the background
|
* in the background
|
||||||
* @param string $sName Parameter name
|
* @param $sName Parameter name
|
||||||
* @return string|null The file name if any, or null
|
* @return The file name if any, or null
|
||||||
*/
|
*/
|
||||||
public static function GetParamSourceFile($sName)
|
public static function GetParamSourceFile($sName)
|
||||||
{
|
{
|
||||||
@@ -306,6 +308,7 @@ class utils
|
|||||||
case 'context_param':
|
case 'context_param':
|
||||||
case 'parameter':
|
case 'parameter':
|
||||||
case 'field_name':
|
case 'field_name':
|
||||||
|
case 'transaction_id':
|
||||||
if (is_array($value))
|
if (is_array($value))
|
||||||
{
|
{
|
||||||
$retValue = array();
|
$retValue = array();
|
||||||
@@ -349,6 +352,11 @@ class utils
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// For XML / HTML node identifiers
|
||||||
|
case 'element_identifier':
|
||||||
|
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -359,16 +367,13 @@ class utils
|
|||||||
|
|
||||||
return $retValue;
|
return $retValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads an uploaded file and turns it into an ormDocument object - Triggers an exception in case of error
|
* Reads an uploaded file and turns it into an ormDocument object - Triggers an exception in case of error
|
||||||
*
|
* @param string $sName Name of the input used from uploading the file
|
||||||
* @param string $sName Name of the input used from uploading the file
|
* @param string $sIndex If Name is an array of posted files, then the index must be used to point out the file
|
||||||
* @param string $sIndex If Name is an array of posted files, then the index must be used to point out the file
|
|
||||||
*
|
|
||||||
* @return ormDocument The uploaded file (can be 'empty' if nothing was uploaded)
|
* @return ormDocument The uploaded file (can be 'empty' if nothing was uploaded)
|
||||||
* @throws \FileUploadException
|
*/
|
||||||
*/
|
|
||||||
public static function ReadPostedDocument($sName, $sIndex = null)
|
public static function ReadPostedDocument($sName, $sIndex = null)
|
||||||
{
|
{
|
||||||
$oDocument = new ormDocument(); // an empty document
|
$oDocument = new ormDocument(); // an empty document
|
||||||
@@ -385,8 +390,24 @@ class utils
|
|||||||
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
||||||
|
|
||||||
$doc_content = file_get_contents($sTmpName);
|
$doc_content = file_get_contents($sTmpName);
|
||||||
$sMimeType = self::GetFileMimeType($sTmpName);
|
if (function_exists('finfo_file'))
|
||||||
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
|
{
|
||||||
|
// as of PHP 5.3 the fileinfo extension is bundled within PHP
|
||||||
|
// in which case we don't trust the mime type provided by the browser
|
||||||
|
$rInfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
|
||||||
|
if ($rInfo !== false)
|
||||||
|
{
|
||||||
|
$sType = @finfo_file($rInfo, $sTmpName);
|
||||||
|
if ( ($sType !== false)
|
||||||
|
&& is_string($sType)
|
||||||
|
&& (strlen($sType)>0))
|
||||||
|
{
|
||||||
|
$sMimeType = $sType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@finfo_close($rInfo);
|
||||||
|
}
|
||||||
|
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UPLOAD_ERR_NO_FILE:
|
case UPLOAD_ERR_NO_FILE:
|
||||||
@@ -423,17 +444,14 @@ class utils
|
|||||||
}
|
}
|
||||||
return $oDocument;
|
return $oDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interprets the results posted by a normal or paginated list (in multiple selection mode)
|
* Interprets the results posted by a normal or paginated list (in multiple selection mode)
|
||||||
*
|
*
|
||||||
* @param DBSearch $oFullSetFilter The criteria defining the whole sets of objects being selected
|
* @param $oFullSetFilter DBSearch The criteria defining the whole sets of objects being selected
|
||||||
*
|
*
|
||||||
* @return array An array of object IDs corresponding to the objects selected in the set
|
* @return Array An array of object IDs corresponding to the objects selected in the set
|
||||||
* @throws \CoreException
|
*/
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \MySQLException
|
|
||||||
*/
|
|
||||||
public static function ReadMultipleSelection($oFullSetFilter)
|
public static function ReadMultipleSelection($oFullSetFilter)
|
||||||
{
|
{
|
||||||
$aSelectedObj = utils::ReadParam('selectObject', array());
|
$aSelectedObj = utils::ReadParam('selectObject', array());
|
||||||
@@ -527,11 +545,11 @@ class utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns a unique tmp id for the current upload based on the transaction system (db).
|
||||||
|
*
|
||||||
* Build as static::GetNewTransactionId()
|
* Build as static::GetNewTransactionId()
|
||||||
*
|
*
|
||||||
* @param string $sTransactionId
|
* @return string
|
||||||
*
|
|
||||||
* @return string unique tmp id for the current upload based on the transaction system (db). Build as static::GetNewTransactionId()
|
|
||||||
*/
|
*/
|
||||||
public static function GetUploadTempId($sTransactionId = null)
|
public static function GetUploadTempId($sTransactionId = null)
|
||||||
{
|
{
|
||||||
@@ -553,7 +571,7 @@ class utils
|
|||||||
* as in php.ini, e.g. 256k, 2M, 1G etc. Into a number of bytes
|
* 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
|
* @param mixed $value The value as read from php.ini
|
||||||
* @return number
|
* @return number
|
||||||
*/
|
*/
|
||||||
public static function ConvertToBytes( $value )
|
public static function ConvertToBytes( $value )
|
||||||
{
|
{
|
||||||
$iReturn = $value;
|
$iReturn = $value;
|
||||||
@@ -590,7 +608,7 @@ class utils
|
|||||||
/**
|
/**
|
||||||
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
|
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
|
||||||
*
|
*
|
||||||
* @param float $value
|
* @param type $value
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function BytesToFriendlyFormat($value)
|
public static function BytesToFriendlyFormat($value)
|
||||||
@@ -631,8 +649,8 @@ class utils
|
|||||||
* Example: StringToTime('01/05/11 12:03:45', '%d/%m/%y %H:%i:%s')
|
* Example: StringToTime('01/05/11 12:03:45', '%d/%m/%y %H:%i:%s')
|
||||||
* @param string $sDate
|
* @param string $sDate
|
||||||
* @param string $sFormat
|
* @param string $sFormat
|
||||||
* @return string|false false if the input format is not correct, timestamp otherwise
|
* @return timestamp or false if the input format is not correct
|
||||||
*/
|
*/
|
||||||
public static function StringToTime($sDate, $sFormat)
|
public static function StringToTime($sDate, $sFormat)
|
||||||
{
|
{
|
||||||
// Source: http://php.net/manual/fr/function.strftime.php
|
// Source: http://php.net/manual/fr/function.strftime.php
|
||||||
@@ -674,12 +692,12 @@ class utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert an old date/time format specification (using % placeholders)
|
* Convert an old date/time format specifciation (using % placeholders)
|
||||||
* to a format compatible with DateTime::createFromFormat
|
* to a format compatible with DateTime::createFromFormat
|
||||||
* @param string $sOldDateTimeFormat
|
* @param string $sOldDateTimeFormat
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function DateTimeFormatToPHP($sOldDateTimeFormat)
|
static public function DateTimeFormatToPHP($sOldDateTimeFormat)
|
||||||
{
|
{
|
||||||
$aSearch = array('%d', '%m', '%y', '%Y', '%H', '%i', '%s');
|
$aSearch = array('%d', '%m', '%y', '%Y', '%H', '%i', '%s');
|
||||||
$aReplacement = array('d', 'm', 'y', 'Y', 'H', 'i', 's');
|
$aReplacement = array('d', 'm', 'y', 'Y', 'H', 'i', 's');
|
||||||
@@ -687,58 +705,31 @@ class utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allow to set cached config. Useful when running with {@link Parameters} for example.
|
* @return \Config from the current environement, or if not existing from the production env, else new Config made from scratch
|
||||||
* @param \Config $oConfig
|
* @uses \MetaModel::GetConfig() don't forget to add the needed <code>require_once(APPROOT.'core/metamodel.class.php');</code>
|
||||||
*/
|
*/
|
||||||
public static function SetConfig(Config $oConfig)
|
static public function GetConfig()
|
||||||
{
|
{
|
||||||
self::$oConfig = $oConfig;
|
if (self::$oConfig == null)
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \Config Get object in the following order :
|
|
||||||
* <ol>
|
|
||||||
* <li>from {@link MetaModel::GetConfig} if loaded
|
|
||||||
* <li>{@link oConfig} attribute if set
|
|
||||||
* <li>from disk (current env, using {@link GetConfigFilePath}) => if loaded this will be stored in {@link oConfig} attribute
|
|
||||||
* <li>from disk, env production => if loaded this will be stored in {@link oConfig} attribute
|
|
||||||
* <li>default Config object
|
|
||||||
* </ol>
|
|
||||||
* @throws \ConfigException
|
|
||||||
* @throws \CoreException
|
|
||||||
*
|
|
||||||
* @since 2.7.0 N°2478 always call {@link MetaModel::GetConfig} first, cache is only set when loading from disk
|
|
||||||
*/
|
|
||||||
public static function GetConfig()
|
|
||||||
{
|
|
||||||
$oMetaModelConfig = MetaModel::GetConfig();
|
|
||||||
if ($oMetaModelConfig !== null)
|
|
||||||
{
|
{
|
||||||
return $oMetaModelConfig;
|
self::$oConfig = MetaModel::GetConfig();
|
||||||
}
|
|
||||||
|
|
||||||
if (self::$oConfig !== null)
|
if (self::$oConfig == null)
|
||||||
{
|
{
|
||||||
return self::$oConfig;
|
$sConfigFile = self::GetConfigFilePath();
|
||||||
}
|
if (!file_exists($sConfigFile))
|
||||||
|
{
|
||||||
|
$sConfigFile = self::GetConfigFilePath('production');
|
||||||
|
if (!file_exists($sConfigFile))
|
||||||
|
{
|
||||||
|
$sConfigFile = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$sCurrentEnvConfigPath = self::GetConfigFilePath();
|
self::$oConfig = new Config($sConfigFile);
|
||||||
if (file_exists($sCurrentEnvConfigPath))
|
}
|
||||||
{
|
|
||||||
$oCurrentEnvDiskConfig = new Config($sCurrentEnvConfigPath);
|
|
||||||
self::SetConfig($oCurrentEnvDiskConfig);
|
|
||||||
return self::$oConfig;
|
|
||||||
}
|
}
|
||||||
|
return self::$oConfig;
|
||||||
$sProductionEnvConfigPath = self::GetConfigFilePath('production');
|
|
||||||
if (file_exists($sProductionEnvConfigPath))
|
|
||||||
{
|
|
||||||
$oProductionEnvDiskConfig = new Config($sProductionEnvConfigPath);
|
|
||||||
self::SetConfig($oProductionEnvDiskConfig);
|
|
||||||
return self::$oConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Config();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function InitTimeZone() {
|
public static function InitTimeZone() {
|
||||||
@@ -763,7 +754,7 @@ class utils
|
|||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function GetAbsoluteUrlAppRoot()
|
static public function GetAbsoluteUrlAppRoot()
|
||||||
{
|
{
|
||||||
static $sUrl = null;
|
static $sUrl = null;
|
||||||
if ($sUrl === null)
|
if ($sUrl === null)
|
||||||
@@ -798,7 +789,7 @@ class utils
|
|||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public static function GetDefaultUrlAppRoot()
|
static public function GetDefaultUrlAppRoot()
|
||||||
{
|
{
|
||||||
// Build an absolute URL to this page on this server/port
|
// Build an absolute URL to this page on this server/port
|
||||||
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
||||||
@@ -873,7 +864,7 @@ class utils
|
|||||||
* nginx set it to an empty string
|
* nginx set it to an empty string
|
||||||
* Others might leave it unset (no array entry)
|
* Others might leave it unset (no array entry)
|
||||||
*/
|
*/
|
||||||
public static function IsConnectionSecure()
|
static public function IsConnectionSecure()
|
||||||
{
|
{
|
||||||
$bSecured = false;
|
$bSecured = false;
|
||||||
|
|
||||||
@@ -893,23 +884,67 @@ class utils
|
|||||||
*/
|
*/
|
||||||
static function CanLogOff()
|
static function CanLogOff()
|
||||||
{
|
{
|
||||||
return (isset($_SESSION['can_logoff']) ? $_SESSION['can_logoff'] : false);
|
$bResult = false;
|
||||||
|
if(isset($_SESSION['login_mode']))
|
||||||
|
{
|
||||||
|
$sLoginMode = $_SESSION['login_mode'];
|
||||||
|
switch($sLoginMode)
|
||||||
|
{
|
||||||
|
case 'external':
|
||||||
|
$bResult = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'form':
|
||||||
|
case 'basic':
|
||||||
|
case 'url':
|
||||||
|
case 'cas':
|
||||||
|
default:
|
||||||
|
$bResult = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $bResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the _SESSION variable for logging purpose
|
* Initializes the CAS client
|
||||||
* @return false|string
|
|
||||||
*/
|
*/
|
||||||
public static function GetSessionLog()
|
static function InitCASClient()
|
||||||
{
|
{
|
||||||
ob_start();
|
$sCASIncludePath = self::GetConfig()->Get('cas_include_path');
|
||||||
print_r($_SESSION);
|
include_once($sCASIncludePath.'/CAS.php');
|
||||||
$sSessionLog = ob_get_contents();
|
|
||||||
ob_end_clean();
|
$bCASDebug = self::GetConfig()->Get('cas_debug');
|
||||||
|
if ($bCASDebug)
|
||||||
return $sSessionLog;
|
{
|
||||||
}
|
phpCAS::setDebug(APPROOT.'log/error.log');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self::$m_bCASClient)
|
||||||
|
{
|
||||||
|
// Initialize phpCAS
|
||||||
|
$sCASVersion = self::GetConfig()->Get('cas_version');
|
||||||
|
$sCASHost = self::GetConfig()->Get('cas_host');
|
||||||
|
$iCASPort = self::GetConfig()->Get('cas_port');
|
||||||
|
$sCASContext = self::GetConfig()->Get('cas_context');
|
||||||
|
phpCAS::client($sCASVersion, $sCASHost, $iCASPort, $sCASContext, false /* session already started */);
|
||||||
|
self::$m_bCASClient = true;
|
||||||
|
$sCASCACertPath = self::GetConfig()->Get('cas_server_ca_cert_path');
|
||||||
|
if (empty($sCASCACertPath))
|
||||||
|
{
|
||||||
|
// If no certificate authority is provided, do not attempt to validate
|
||||||
|
// the server's certificate
|
||||||
|
// THIS SETTING IS NOT RECOMMENDED FOR PRODUCTION.
|
||||||
|
// VALIDATING THE CAS SERVER IS CRUCIAL TO THE SECURITY OF THE CAS PROTOCOL!
|
||||||
|
phpCAS::setNoCasServerValidation();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
phpCAS::setCasServerCACert($sCASCACertPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static function DebugBacktrace($iLimit = 5)
|
static function DebugBacktrace($iLimit = 5)
|
||||||
{
|
{
|
||||||
$aFullTrace = debug_backtrace();
|
$aFullTrace = debug_backtrace();
|
||||||
@@ -928,16 +963,10 @@ class utils
|
|||||||
* @param string $sScript Name and relative path to the file (relative to the iTop root dir)
|
* @param string $sScript Name and relative path to the file (relative to the iTop root dir)
|
||||||
* @param hash $aArguments Associative array of 'arg' => 'value'
|
* @param hash $aArguments Associative array of 'arg' => 'value'
|
||||||
* @return array(iCode, array(output lines))
|
* @return array(iCode, array(output lines))
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @param string $sScriptName
|
|
||||||
* @param array $aArguments
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
* @throws \ConfigException
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
*/
|
||||||
public static function ExecITopScript($sScriptName, $aArguments)
|
static function ExecITopScript($sScriptName, $aArguments)
|
||||||
{
|
{
|
||||||
$aDisabled = explode(', ', ini_get('disable_functions'));
|
$aDisabled = explode(', ', ini_get('disable_functions'));
|
||||||
if (in_array('exec', $aDisabled))
|
if (in_array('exec', $aDisabled))
|
||||||
@@ -1022,33 +1051,16 @@ class utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string A path to a folder into which any module can store cache data
|
* Returns a path to a folder into which any module can store cache data
|
||||||
* The corresponding folder is created or cleaned upon code compilation
|
* The corresponding folder is created or cleaned upon code compilation
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function GetCachePath()
|
public static function GetCachePath()
|
||||||
{
|
{
|
||||||
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
|
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* @return string A path to a folder into which any module can store log
|
|
||||||
* @since 2.7.0
|
|
||||||
*/
|
|
||||||
public static function GetLogPath()
|
|
||||||
{
|
|
||||||
return APPROOT.'log/';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge standard menu items with plugin provided menus items
|
* Merge standard menu items with plugin provided menus items
|
||||||
*
|
|
||||||
* @param \WebPage $oPage
|
|
||||||
* @param int $iMenuId
|
|
||||||
* @param \DBObjectSet $param
|
|
||||||
* @param array $aActions
|
|
||||||
* @param string $sTableId
|
|
||||||
* @param string $sDataTableId
|
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions, $sTableId = null, $sDataTableId = null)
|
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions, $sTableId = null, $sDataTableId = null)
|
||||||
{
|
{
|
||||||
@@ -1178,10 +1190,7 @@ class utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sEnvironment
|
|
||||||
*
|
|
||||||
* @return string target configuration file name (including full path)
|
* @return string target configuration file name (including full path)
|
||||||
*/
|
*/
|
||||||
public static function GetConfigFilePath($sEnvironment = null)
|
public static function GetConfigFilePath($sEnvironment = null)
|
||||||
@@ -1192,10 +1201,7 @@ class utils
|
|||||||
}
|
}
|
||||||
return APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
return APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sEnvironment
|
|
||||||
*
|
|
||||||
* @return string target configuration file name (including relative path)
|
* @return string target configuration file name (including relative path)
|
||||||
*/
|
*/
|
||||||
public static function GetConfigFilePathRelative($sEnvironment = null)
|
public static function GetConfigFilePathRelative($sEnvironment = null)
|
||||||
@@ -1207,11 +1213,10 @@ class utils
|
|||||||
return "conf/".$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
return "conf/".$sEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string the absolute URL to the modules root path
|
* @return string the absolute URL to the modules root path
|
||||||
* @throws \Exception
|
*/
|
||||||
*/
|
static public function GetAbsoluteUrlModulesRoot()
|
||||||
public static function GetAbsoluteUrlModulesRoot()
|
|
||||||
{
|
{
|
||||||
$sUrl = self::GetAbsoluteUrlAppRoot().'env-'.self::GetCurrentEnvironment().'/';
|
$sUrl = self::GetAbsoluteUrlAppRoot().'env-'.self::GetCurrentEnvironment().'/';
|
||||||
return $sUrl;
|
return $sUrl;
|
||||||
@@ -1226,20 +1231,17 @@ class utils
|
|||||||
* require_once(__DIR__.'/../../approot.inc.php');
|
* require_once(__DIR__.'/../../approot.inc.php');
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @see GetExecPageArguments can be used to submit using the GET method (see bug in N.1108)
|
* @param string $sModule
|
||||||
* @see GetAbsoluteUrlExecPage
|
* @param string $sPage
|
||||||
*
|
|
||||||
* @param string[] $aArguments
|
* @param string[] $aArguments
|
||||||
* @param string $sEnvironment
|
* @param string $sEnvironment
|
||||||
*
|
*
|
||||||
* @param string $sModule
|
|
||||||
* @param string $sPage
|
|
||||||
*
|
|
||||||
* @return string the URL to a page that will execute the requested module page, with query string values url encoded
|
* @return string the URL to a page that will execute the requested module page, with query string values url encoded
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @see GetExecPageArguments can be used to submit using the GET method (see bug in N.1108)
|
||||||
|
* @see GetAbsoluteUrlExecPage
|
||||||
*/
|
*/
|
||||||
public static function GetAbsoluteUrlModulePage($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
|
static public function GetAbsoluteUrlModulePage($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
|
||||||
{
|
{
|
||||||
$aArgs = self::GetExecPageArguments($sModule, $sPage, $aArguments, $sEnvironment);
|
$aArgs = self::GetExecPageArguments($sModule, $sPage, $aArguments, $sEnvironment);
|
||||||
$sArgs = http_build_query($aArgs);
|
$sArgs = http_build_query($aArgs);
|
||||||
@@ -1256,7 +1258,7 @@ class utils
|
|||||||
* @return string[] key/value pair for the exec page query string. <b>Warning</b> : values are not url encoded !
|
* @return string[] key/value pair for the exec page query string. <b>Warning</b> : values are not url encoded !
|
||||||
* @throws \Exception if one of the argument has a reserved name
|
* @throws \Exception if one of the argument has a reserved name
|
||||||
*/
|
*/
|
||||||
public static function GetExecPageArguments($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
|
static public function GetExecPageArguments($sModule, $sPage, $aArguments = array(), $sEnvironment = null)
|
||||||
{
|
{
|
||||||
$sEnvironment = is_null($sEnvironment) ? self::GetCurrentEnvironment() : $sEnvironment;
|
$sEnvironment = is_null($sEnvironment) ? self::GetCurrentEnvironment() : $sEnvironment;
|
||||||
$aArgs = array();
|
$aArgs = array();
|
||||||
@@ -1277,20 +1279,18 @@ class utils
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public static function GetAbsoluteUrlExecPage()
|
static public function GetAbsoluteUrlExecPage()
|
||||||
{
|
{
|
||||||
return self::GetAbsoluteUrlAppRoot().'pages/exec.php';
|
return self::GetAbsoluteUrlAppRoot().'pages/exec.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns a name unique amongst the given list
|
||||||
* @param string $sProposed The default value
|
* @param string $sProposed The default value
|
||||||
* @param array $aExisting An array of existing values (strings)
|
* @param array $aExisting An array of existing values (strings)
|
||||||
*
|
|
||||||
* @return string a unique name amongst the given list
|
|
||||||
*/
|
*/
|
||||||
public static function MakeUniqueName($sProposed, $aExisting)
|
static public function MakeUniqueName($sProposed, $aExisting)
|
||||||
{
|
{
|
||||||
if (in_array($sProposed, $aExisting))
|
if (in_array($sProposed, $aExisting))
|
||||||
{
|
{
|
||||||
@@ -1312,7 +1312,7 @@ class utils
|
|||||||
* @param string $sId The ID to sanitize
|
* @param string $sId The ID to sanitize
|
||||||
* @return string The sanitized ID
|
* @return string The sanitized ID
|
||||||
*/
|
*/
|
||||||
public static function GetSafeId($sId)
|
static public function GetSafeId($sId)
|
||||||
{
|
{
|
||||||
return str_replace(array(':', '[', ']', '+', '-'), '_', $sId);
|
return str_replace(array(':', '[', ']', '+', '-'), '_', $sId);
|
||||||
}
|
}
|
||||||
@@ -1324,17 +1324,14 @@ class utils
|
|||||||
* Does not require cUrl but requires openssl for performing https POSTs.
|
* Does not require cUrl but requires openssl for performing https POSTs.
|
||||||
*
|
*
|
||||||
* @param string $sUrl The URL to POST the data to
|
* @param string $sUrl The URL to POST the data to
|
||||||
* @param array $aData The data to POST as an array('param_name' => value)
|
* @param hash $aData The data to POST as an array('param_name' => value)
|
||||||
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
|
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
|
||||||
* @param array $aResponseHeaders An array to be filled with reponse headers: WARNING: the actual content of the array depends on the
|
* @param hash $aResponseHeaders An array to be filled with reponse headers: WARNING: the actual content of the array depends on the library used: cURL or fopen, test with both !! See: http://fr.php.net/manual/en/function.curl-getinfo.php
|
||||||
* library used: cURL or fopen, test with both !! See: http://fr.php.net/manual/en/function.curl-getinfo.php
|
* @param hash $aCurlOptions An (optional) array of options to pass to curl_init. The format is 'option_code' => 'value'. These values have precedence over the default ones. Example: CURLOPT_SSLVERSION => CURL_SSLVERSION_SSLv3
|
||||||
* @param array $aCurlOptions An (optional) array of options to pass to curl_init. The format is 'option_code' => 'value'. These values
|
|
||||||
* have precedence over the default ones. Example: CURLOPT_SSLVERSION => CURL_SSLVERSION_SSLv3
|
|
||||||
*
|
|
||||||
* @return string The result of the POST request
|
* @return string The result of the POST request
|
||||||
* @throws Exception with a specific error message depending on the cause
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
|
static public function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
|
||||||
{
|
{
|
||||||
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
||||||
|
|
||||||
@@ -1477,11 +1474,22 @@ class utils
|
|||||||
* @param string $sValue
|
* @param string $sValue
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function HtmlEntities($sValue)
|
static public function HtmlEntities($sValue)
|
||||||
{
|
{
|
||||||
return htmlentities($sValue, ENT_QUOTES, 'UTF-8');
|
return htmlentities($sValue, ENT_QUOTES, 'UTF-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to encapsulation iTop's html_entity_decode
|
||||||
|
* @param string $sValue
|
||||||
|
* @return string
|
||||||
|
* @since 2.7.0
|
||||||
|
*/
|
||||||
|
public static function HtmlEntityDecode($sValue)
|
||||||
|
{
|
||||||
|
return html_entity_decode($sValue, ENT_QUOTES, 'UTF-8');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a string containing some (valid) HTML markup to plain text
|
* Convert a string containing some (valid) HTML markup to plain text
|
||||||
* @param string $sHtml
|
* @param string $sHtml
|
||||||
@@ -1520,7 +1528,7 @@ class utils
|
|||||||
* @param array $aImportPaths Array of absolute paths to load imports from
|
* @param array $aImportPaths Array of absolute paths to load imports from
|
||||||
* @return string Relative path to the CSS file (<name>.css)
|
* @return string Relative path to the CSS file (<name>.css)
|
||||||
*/
|
*/
|
||||||
public static function GetCSSFromSASS($sSassRelPath, $aImportPaths = null)
|
static public function GetCSSFromSASS($sSassRelPath, $aImportPaths = null)
|
||||||
{
|
{
|
||||||
// Avoiding compilation if file is already a css file.
|
// Avoiding compilation if file is already a css file.
|
||||||
if (preg_match('/\.css(\?.*)?$/', $sSassRelPath))
|
if (preg_match('/\.css(\?.*)?$/', $sSassRelPath))
|
||||||
@@ -1541,9 +1549,10 @@ class utils
|
|||||||
clearstatcache();
|
clearstatcache();
|
||||||
if (!file_exists($sCssPath) || (is_writable($sCssPath) && (filemtime($sCssPath) < filemtime($sSassPath))))
|
if (!file_exists($sCssPath) || (is_writable($sCssPath) && (filemtime($sCssPath) < filemtime($sSassPath))))
|
||||||
{
|
{
|
||||||
|
require_once(APPROOT.'lib/scssphp/scss.inc.php');
|
||||||
$oScss = new Compiler();
|
$oScss = new Compiler();
|
||||||
$oScss->setImportPaths($aImportPaths);
|
$oScss->setImportPaths($aImportPaths);
|
||||||
$oScss->setFormatter('ScssPhp\\ScssPhp\\Formatter\\Expanded');
|
$oScss->setFormatter('Leafo\\ScssPhp\\Formatter\\Expanded');
|
||||||
// Temporary disabling max exec time while compiling
|
// Temporary disabling max exec time while compiling
|
||||||
$iCurrentMaxExecTime = (int) ini_get('max_execution_time');
|
$iCurrentMaxExecTime = (int) ini_get('max_execution_time');
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
@@ -1554,7 +1563,7 @@ class utils
|
|||||||
return $sCssRelPath;
|
return $sCssRelPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function GetImageSize($sImageData)
|
static public function GetImageSize($sImageData)
|
||||||
{
|
{
|
||||||
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
|
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
|
||||||
{
|
{
|
||||||
@@ -1677,7 +1686,7 @@ class utils
|
|||||||
* @param string $sPrefix
|
* @param string $sPrefix
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function CreateUUID($sPrefix = '')
|
static public function CreateUUID($sPrefix = '')
|
||||||
{
|
{
|
||||||
$uid = uniqid("", true);
|
$uid = uniqid("", true);
|
||||||
$data = $sPrefix;
|
$data = $sPrefix;
|
||||||
@@ -1701,10 +1710,10 @@ class utils
|
|||||||
/**
|
/**
|
||||||
* Returns the name of the module containing the file where the call to this function is made
|
* Returns the name 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)
|
* or an empty string if no such module is found (or not called within a module file)
|
||||||
* @param int $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
|
* @param number $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function GetCurrentModuleName($iCallDepth = 0)
|
static public function GetCurrentModuleName($iCallDepth = 0)
|
||||||
{
|
{
|
||||||
$sCurrentModuleName = '';
|
$sCurrentModuleName = '';
|
||||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||||
@@ -1727,13 +1736,12 @@ class utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the relative (to MODULESROOT) path of the root directory of the module containing the file where the call to
|
* Returns the relative (to APPROOT) path of the root directory of the module containing the file where the call to this function is made
|
||||||
* this function is made
|
|
||||||
* or an empty string if no such module is found (or not called within a module file)
|
* 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
|
* @param number $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function GetCurrentModuleDir($iCallDepth)
|
static public function GetCurrentModuleDir($iCallDepth)
|
||||||
{
|
{
|
||||||
$sCurrentModuleDir = '';
|
$sCurrentModuleDir = '';
|
||||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||||
@@ -1754,13 +1762,13 @@ class utils
|
|||||||
}
|
}
|
||||||
return $sCurrentModuleDir;
|
return $sCurrentModuleDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string the base URL for all files in the current module from which this method is called
|
* Returns 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)
|
* or an empty string if no such module is found (or not called within a module file)
|
||||||
* @throws \Exception
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function GetCurrentModuleUrl()
|
static public function GetCurrentModuleUrl()
|
||||||
{
|
{
|
||||||
$sDir = static::GetCurrentModuleDir(1);
|
$sDir = static::GetCurrentModuleDir(1);
|
||||||
if ( $sDir !== '')
|
if ( $sDir !== '')
|
||||||
@@ -1771,21 +1779,23 @@ class utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get the value of a given setting for the current module
|
||||||
* @param string $sProperty The name of the property to retrieve
|
* @param string $sProperty The name of the property to retrieve
|
||||||
* @param mixed $defaultvalue
|
* @param mixed $defaultvalue
|
||||||
* @return mixed the value of a given setting for the current module
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
static public function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
||||||
{
|
{
|
||||||
$sModuleName = static::GetCurrentModuleName(1);
|
$sModuleName = static::GetCurrentModuleName(1);
|
||||||
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
|
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Get the compiled version of a given module, as it was seen by the compiler
|
||||||
* @param string $sModuleName
|
* @param string $sModuleName
|
||||||
* @return string|NULL compiled version of a given module, as it was seen by the compiler
|
* @return string|NULL
|
||||||
*/
|
*/
|
||||||
public static function GetCompiledModuleVersion($sModuleName)
|
static public function GetCompiledModuleVersion($sModuleName)
|
||||||
{
|
{
|
||||||
$aModulesInfo = GetModulesInfo();
|
$aModulesInfo = GetModulesInfo();
|
||||||
if (array_key_exists($sModuleName, $aModulesInfo))
|
if (array_key_exists($sModuleName, $aModulesInfo))
|
||||||
@@ -1999,9 +2009,10 @@ class utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string a string based on compilation time or (if not available because the datamodel has not been loaded)
|
* Return a string based on compilation time or (if not available because the datamodel has not been loaded)
|
||||||
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
|
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
|
||||||
* (re)installation of iTop (especially during development).
|
* (re)installation of iTop (especially during development).
|
||||||
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function GetCacheBusterTimestamp()
|
public static function GetCacheBusterTimestamp()
|
||||||
{
|
{
|
||||||
@@ -2018,8 +2029,6 @@ class utils
|
|||||||
* @param $sClass
|
* @param $sClass
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
* @throws \ConfigException
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
*/
|
||||||
public static function IsHighCardinality($sClass)
|
public static function IsHighCardinality($sClass)
|
||||||
{
|
{
|
||||||
@@ -2041,40 +2050,6 @@ class utils
|
|||||||
return ITOP_REVISION === 'svn';
|
return ITOP_REVISION === 'svn';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see https://php.net/manual/en/function.finfo-file.php
|
|
||||||
*
|
|
||||||
* @param string $sFilePath file full path
|
|
||||||
* @param string $sDefaultMimeType
|
|
||||||
*
|
|
||||||
* @return string mime type, defaults to <code>application/octet-stream</code>
|
|
||||||
* @uses finfo_file in FileInfo extension (bundled in PHP since version 5.3)
|
|
||||||
* @since 2.7.0 N°2366
|
|
||||||
*/
|
|
||||||
public static function GetFileMimeType($sFilePath, $sDefaultMimeType = 'application/octet-stream')
|
|
||||||
{
|
|
||||||
if (!function_exists('finfo_file'))
|
|
||||||
{
|
|
||||||
return $sDefaultMimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sMimeType = $sDefaultMimeType;
|
|
||||||
$rInfo = @finfo_open(FILEINFO_MIME_TYPE);
|
|
||||||
if ($rInfo !== false)
|
|
||||||
{
|
|
||||||
$sType = @finfo_file($rInfo, $sFilePath);
|
|
||||||
if (($sType !== false)
|
|
||||||
&& is_string($sType)
|
|
||||||
&& ($sType !== ''))
|
|
||||||
{
|
|
||||||
$sMimeType = $sType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@finfo_close($rInfo);
|
|
||||||
|
|
||||||
return $sMimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* helper to test if a string starts with another
|
* helper to test if a string starts with another
|
||||||
* @param $haystack
|
* @param $haystack
|
||||||
@@ -2084,6 +2059,11 @@ class utils
|
|||||||
*/
|
*/
|
||||||
final public static function StartsWith($haystack, $needle)
|
final public static function StartsWith($haystack, $needle)
|
||||||
{
|
{
|
||||||
|
if (strlen($needle) > strlen($haystack))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
|
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2095,6 +2075,11 @@ class utils
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
final public static function EndsWith($haystack, $needle) {
|
final public static function EndsWith($haystack, $needle) {
|
||||||
|
if (strlen($needle) > strlen($haystack))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
|
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2102,13 +2087,13 @@ class utils
|
|||||||
* @param string $sPath for example '/var/www/html/itop/data/backups/manual/itop_27-2019-10-03_15_35.tar.gz'
|
* @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/'
|
* @param string $sBasePath for example '/var/www/html/itop/data/'
|
||||||
*
|
*
|
||||||
* @return bool false if path :
|
* @return bool|string false if path :
|
||||||
* * invalid
|
* * invalid
|
||||||
* * not allowed
|
* * not allowed
|
||||||
* * not contained in base path
|
* * not contained in base path
|
||||||
* Otherwise return the real path (see realpath())
|
* Otherwise return the real path (see realpath())
|
||||||
*
|
*
|
||||||
* @since 2.7.0 N°2538
|
* @since 2.6.5 2.7.0 N°2538
|
||||||
*/
|
*/
|
||||||
final public static function RealPath($sPath, $sBasePath)
|
final public static function RealPath($sPath, $sBasePath)
|
||||||
{
|
{
|
||||||
@@ -2126,43 +2111,4 @@ class utils
|
|||||||
|
|
||||||
return $sFileRealPath;
|
return $sFileRealPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the local path relative to the iTop installation of an existing file
|
|
||||||
* Dir separator is changed to '/' for consistency among the different OS
|
|
||||||
*
|
|
||||||
* @param string $sPath absolute path
|
|
||||||
*
|
|
||||||
* @return false|string
|
|
||||||
*/
|
|
||||||
final public static function LocalPath($sPath)
|
|
||||||
{
|
|
||||||
$sRootPath = realpath(APPROOT);
|
|
||||||
$sFullPath = realpath($sPath);
|
|
||||||
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$sLocalPath = substr($sFullPath, strlen($sRootPath.DIRECTORY_SEPARATOR));
|
|
||||||
$sLocalPath = str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
|
|
||||||
return $sLocalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return absolute path of an existing file located in iTop
|
|
||||||
*
|
|
||||||
* @param string $sPath relative iTop path
|
|
||||||
*
|
|
||||||
* @return string|false absolute path
|
|
||||||
*/
|
|
||||||
public static function AbsolutePath($sPath)
|
|
||||||
{
|
|
||||||
$sRootPath = realpath(APPROOT);
|
|
||||||
$sFullPath = realpath($sRootPath.DIRECTORY_SEPARATOR.$sPath);
|
|
||||||
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return $sFullPath;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,10 +60,6 @@ Interface Page
|
|||||||
*/
|
*/
|
||||||
class WebPage implements Page
|
class WebPage implements Page
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @since 2.7.0 N°2529
|
|
||||||
*/
|
|
||||||
const PAGES_CHARSET = 'utf-8';
|
|
||||||
protected $s_title;
|
protected $s_title;
|
||||||
protected $s_content;
|
protected $s_content;
|
||||||
protected $s_deferred_content;
|
protected $s_deferred_content;
|
||||||
@@ -84,8 +80,6 @@ class WebPage implements Page
|
|||||||
protected $s_sOutputFormat;
|
protected $s_sOutputFormat;
|
||||||
protected $a_OutputOptions;
|
protected $a_OutputOptions;
|
||||||
protected $bPrintable;
|
protected $bPrintable;
|
||||||
protected $bHasCollapsibleSection;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct($s_title, $bPrintable = false)
|
public function __construct($s_title, $bPrintable = false)
|
||||||
{
|
{
|
||||||
@@ -108,7 +102,6 @@ class WebPage implements Page
|
|||||||
$this->bTrashUnexpectedOutput = false;
|
$this->bTrashUnexpectedOutput = false;
|
||||||
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
|
$this->s_OutputFormat = utils::ReadParam('output_format', 'html');
|
||||||
$this->a_OutputOptions = array();
|
$this->a_OutputOptions = array();
|
||||||
$this->bHasCollapsibleSection = false;
|
|
||||||
$this->bPrintable = $bPrintable;
|
$this->bPrintable = $bPrintable;
|
||||||
ob_start(); // Start capturing the output
|
ob_start(); // Start capturing the output
|
||||||
}
|
}
|
||||||
@@ -362,8 +355,10 @@ class WebPage implements Page
|
|||||||
*/
|
*/
|
||||||
public function no_cache()
|
public function no_cache()
|
||||||
{
|
{
|
||||||
$this->add_header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
|
$this->add_header('Cache-control: no-cache, no-store, must-revalidate');
|
||||||
$this->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past
|
$this->add_header('Pragma: no-cache');
|
||||||
|
$this->add_header('Expires: 0');
|
||||||
|
$this->add_header('X-Frame-Options: deny');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -894,73 +889,6 @@ class WebPage implements Page
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds init scripts for the collapsible sections
|
|
||||||
*/
|
|
||||||
protected function outputCollapsibleSectionInit()
|
|
||||||
{
|
|
||||||
if (!$this->bHasCollapsibleSection)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->add_script(<<<'EOD'
|
|
||||||
function initCollapsibleSection(iSectionId, bOpenedByDefault, sSectionStateStorageKey)
|
|
||||||
{
|
|
||||||
var bStoredSectionState = JSON.parse(localStorage.getItem(sSectionStateStorageKey));
|
|
||||||
var bIsSectionOpenedInitially = (bStoredSectionState == null) ? bOpenedByDefault : bStoredSectionState;
|
|
||||||
|
|
||||||
if (bIsSectionOpenedInitially) {
|
|
||||||
$("#LnkCollapse_"+iSectionId).toggleClass("open");
|
|
||||||
$("#Collapse_"+iSectionId).toggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#LnkCollapse_"+iSectionId).click(function(e) {
|
|
||||||
localStorage.setItem(sSectionStateStorageKey, !($("#Collapse_"+iSectionId).is(":visible")));
|
|
||||||
$("#LnkCollapse_"+iSectionId).toggleClass("open");
|
|
||||||
$("#Collapse_"+iSectionId).slideToggle("normal");
|
|
||||||
e.preventDefault(); // we don't want to do anything more (see #1030 : a non wanted tab switching was triggered)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
EOD
|
|
||||||
);
|
|
||||||
}
|
|
||||||
public function StartCollapsibleSection($sSectionLabel, $bOpenedByDefault = false, $sSectionStateStorageBusinessKey = '')
|
|
||||||
{
|
|
||||||
$this->add($this->GetStartCollapsibleSection($sSectionLabel, $bOpenedByDefault, $sSectionStateStorageBusinessKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetStartCollapsibleSection($sSectionLabel, $bOpenedByDefault = false, $sSectionStateStorageBusinessKey = '')
|
|
||||||
{
|
|
||||||
$this->bHasCollapsibleSection = true;
|
|
||||||
$sHtml = '';
|
|
||||||
static $iSectionId = 0;
|
|
||||||
$sHtml .= '<a id="LnkCollapse_'.$iSectionId.'" class="CollapsibleLabel" href="#">'.$sSectionLabel.'</a></br>'."\n";
|
|
||||||
$sHtml .= '<div id="Collapse_'.$iSectionId.'" style="display:none">'."\n";
|
|
||||||
|
|
||||||
$oConfig = MetaModel::GetConfig();
|
|
||||||
$sSectionStateStorageKey = $oConfig->GetItopInstanceid().'/'.$sSectionStateStorageBusinessKey.'/collapsible-'.$iSectionId;
|
|
||||||
$sSectionStateStorageKey = json_encode($sSectionStateStorageKey);
|
|
||||||
$sOpenedByDefault = ($bOpenedByDefault) ? 'true' : 'false';
|
|
||||||
$this->add_ready_script("initCollapsibleSection($iSectionId, $sOpenedByDefault, '$sSectionStateStorageKey');");
|
|
||||||
|
|
||||||
$iSectionId++;
|
|
||||||
|
|
||||||
return $sHtml;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function EndCollapsibleSection()
|
|
||||||
{
|
|
||||||
$this->add($this->GetEndCollapsibleSection());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetEndCollapsibleSection()
|
|
||||||
{
|
|
||||||
return "</div>";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1114,11 +1042,9 @@ class TabManager
|
|||||||
*
|
*
|
||||||
* @param string $sTabLabel The (localised) label of the tab
|
* @param string $sTabLabel The (localised) label of the tab
|
||||||
* @param string $sUrl The URL to load (on the same server)
|
* @param string $sUrl The URL to load (on the same server)
|
||||||
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. false will cause
|
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause
|
||||||
* the tab to be reloaded upon each activation.
|
* the tab to be reloaded upon each activation.
|
||||||
*
|
*
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @since 2.0.3
|
* @since 2.0.3
|
||||||
*/
|
*/
|
||||||
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)
|
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ Class XLSXWriter
|
|||||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
|
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
|
||||||
} else if ($value==''){
|
} else if ($value==''){
|
||||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'"/>');
|
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'"/>');
|
||||||
} else if ($value{0}=='='){
|
} else if ($value[0]=='='){
|
||||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
|
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
|
||||||
} else if ($value!==''){
|
} else if ($value!==''){
|
||||||
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');
|
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');
|
||||||
|
|||||||
@@ -42,10 +42,13 @@ class XMLPage extends WebPage
|
|||||||
parent::__construct($s_title);
|
parent::__construct($s_title);
|
||||||
$this->m_bPassThrough = $bPassThrough;
|
$this->m_bPassThrough = $bPassThrough;
|
||||||
$this->m_bHeaderSent = false;
|
$this->m_bHeaderSent = false;
|
||||||
$this->add_header("Content-type: text/xml; charset=".self::PAGES_CHARSET);
|
$this->add_header("Content-type: text/xml; charset=utf-8");
|
||||||
$this->add_header("Cache-control: 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('X-Frame-Options: deny');
|
||||||
$this->add_header("Content-location: export.xml");
|
$this->add_header("Content-location: export.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function output()
|
public function output()
|
||||||
{
|
{
|
||||||
@@ -54,8 +57,7 @@ class XMLPage extends WebPage
|
|||||||
// Get the unexpected output but do nothing with it
|
// Get the unexpected output but do nothing with it
|
||||||
$sTrash = $this->ob_get_clean_safe();
|
$sTrash = $this->ob_get_clean_safe();
|
||||||
|
|
||||||
$sCharset = self::PAGES_CHARSET;
|
$this->s_content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n".trim($this->s_content);
|
||||||
$this->s_content = "<?xml version=\"1.0\" encoding=\"$sCharset\"?".">\n".trim($this->s_content);
|
|
||||||
$this->add_header("Content-Length: ".strlen($this->s_content));
|
$this->add_header("Content-Length: ".strlen($this->s_content));
|
||||||
foreach($this->a_headers as $s_header)
|
foreach($this->a_headers as $s_header)
|
||||||
{
|
{
|
||||||
@@ -88,8 +90,7 @@ class XMLPage extends WebPage
|
|||||||
{
|
{
|
||||||
header($s_header);
|
header($s_header);
|
||||||
}
|
}
|
||||||
$sCharset = self::PAGES_CHARSET;
|
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n";
|
||||||
echo "<?xml version=\"1.0\" encoding=\"$sCharset\"?".">\n";
|
|
||||||
echo trim($s_captured_output);
|
echo trim($s_captured_output);
|
||||||
echo trim($this->s_content);
|
echo trim($this->s_content);
|
||||||
echo $sText;
|
echo $sText;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
define('APPROOT', dirname(__FILE__).'/');
|
define('APPROOT', dirname(__FILE__).'/');
|
||||||
define('APPCONF', APPROOT.'conf/');
|
define('APPCONF', APPROOT.'conf/');
|
||||||
define('ITOP_DEFAULT_ENV', 'production');
|
define('ITOP_DEFAULT_ENV', 'production');
|
||||||
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
|
|
||||||
|
|
||||||
if (function_exists('microtime'))
|
if (function_exists('microtime'))
|
||||||
{
|
{
|
||||||
@@ -13,56 +12,4 @@ else
|
|||||||
{
|
{
|
||||||
$fItopStarted = 1000 * time();
|
$fItopStarted = 1000 * time();
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
//
|
|
||||||
// Maintenance mode
|
|
||||||
//
|
|
||||||
|
|
||||||
// Use 'maintenance' parameter to bypass maintenance mode
|
|
||||||
if (!isset($bBypassMaintenance))
|
|
||||||
{
|
|
||||||
$bBypassMaintenance = isset($_REQUEST['maintenance']) ? boolval($_REQUEST['maintenance']) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
|
|
||||||
{
|
|
||||||
$sMessage = 'This application is currently under maintenance.';
|
|
||||||
$sTitle = 'Maintenance';
|
|
||||||
|
|
||||||
http_response_code(503);
|
|
||||||
// Display message depending on the request
|
|
||||||
include(APPROOT.'application/maintenancemsg.php');
|
|
||||||
|
|
||||||
switch (true)
|
|
||||||
{
|
|
||||||
case isset($_SERVER['REQUEST_URI']) && EndsWith($_SERVER['REQUEST_URI'], '/pages/ajax.searchform.php'):
|
|
||||||
_MaintenanceHtmlMessage($sMessage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER):
|
|
||||||
case isset($_SERVER['REQUEST_URI']) && EndsWith($_SERVER['REQUEST_URI'], '/webservices/soapserver.php'):
|
|
||||||
case isset($_SERVER['REQUEST_URI']) && EndsWith($_SERVER['REQUEST_URI'], '/webservices/rest.php'):
|
|
||||||
_MaintenanceTextMessage($sMessage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case isset($_SERVER['CONTENT_TYPE']) && ($_SERVER['CONTENT_TYPE'] == 'application/json'):
|
|
||||||
_MaintenanceJsonMessage($sTitle, $sMessage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
_MaintenanceSetupPageMessage($sTitle, $sMessage);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* helper to test if a string ends with another
|
|
||||||
* @param $haystack
|
|
||||||
* @param $needle
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
function EndsWith($haystack, $needle) {
|
|
||||||
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
<?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
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once __DIR__.'/approot.inc.php';
|
|
||||||
require_once APPROOT.'/lib/autoload.php';
|
|
||||||
|
|
||||||
// Require here files containing PHP instructions
|
|
||||||
@@ -1,32 +1,13 @@
|
|||||||
{
|
{
|
||||||
"type": "project",
|
|
||||||
"license": "AGPLv3",
|
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=5.6.0",
|
"php": ">=5.6.0",
|
||||||
"ext-soap": "*",
|
"ext-soap": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
|
"ext-zip": "*",
|
||||||
"ext-mysqli": "*",
|
"ext-mysqli": "*",
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"ext-gd": "*",
|
"ext-gd": "*"
|
||||||
"ext-ctype": "*",
|
|
||||||
|
|
||||||
"scssphp/scssphp": "1.0.0",
|
|
||||||
"swiftmailer/swiftmailer": "5.4.9",
|
|
||||||
"pelago/emogrifier": "2.1.0",
|
|
||||||
"combodo/tcpdf": "6.3.0",
|
|
||||||
"pear/archive_tar": "1.4.7",
|
|
||||||
|
|
||||||
"symfony/console": "3.4.*",
|
|
||||||
"symfony/dotenv": "3.4.*",
|
|
||||||
"symfony/framework-bundle": "3.4.*",
|
|
||||||
"symfony/twig-bundle": "3.4.*",
|
|
||||||
"symfony/yaml": "3.4.*",
|
|
||||||
"symfony/polyfill-php70": "1.*"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"symfony/stopwatch": "3.4.*",
|
|
||||||
"symfony/web-profiler-bundle": "3.4.*"
|
|
||||||
},
|
},
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-libsodium": "Required to use the AttributeEncryptedString.",
|
"ext-libsodium": "Required to use the AttributeEncryptedString.",
|
||||||
@@ -39,39 +20,6 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"platform": {
|
"platform": {
|
||||||
"php": "5.6.0"
|
"php": "5.6.0"
|
||||||
},
|
|
||||||
"vendor-dir": "lib",
|
|
||||||
"preferred-install": {
|
|
||||||
"*": "dist"
|
|
||||||
},
|
|
||||||
"sort-packages": true
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"classmap": [
|
|
||||||
"core",
|
|
||||||
"application"
|
|
||||||
],
|
|
||||||
"exclude-from-classmap": [
|
|
||||||
"core/dbobjectsearch.class.php",
|
|
||||||
"core/legacy/dbobjectsearchlegacy.class.php",
|
|
||||||
"core/querybuildercontext.class.inc.php",
|
|
||||||
"core/legacy/querybuildercontextlegacy.class.inc.php",
|
|
||||||
"core/querybuilderexpressions.class.inc.php",
|
|
||||||
"core/legacy/querybuilderexpressionslegacy.class.inc.php",
|
|
||||||
"application/loginform.class.inc.php",
|
|
||||||
"application/loginbasic.class.inc.php",
|
|
||||||
"application/logindefault.class.inc.php",
|
|
||||||
"application/loginexternal.class.inc.php",
|
|
||||||
"application/loginurl.class.inc.php"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"symfony/symfony": "*"
|
|
||||||
},
|
|
||||||
"extra": {
|
|
||||||
"symfony": {
|
|
||||||
"allow-contrib": false,
|
|
||||||
"require": "3.4.*"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3840
composer.lock
generated
3840
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
|||||||
<?php
|
|
||||||
echo 'Access denied';
|
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8" ?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<system.web>
|
<system.webServer>
|
||||||
<authorization>
|
<security>
|
||||||
<deny users="*" /> <!-- Denies all users -->
|
<requestFiltering>
|
||||||
</authorization>
|
<fileExtensions applyToWebDAV="false" allowUnlisted="false"></fileExtensions>
|
||||||
</system.web>
|
</requestFiltering>
|
||||||
|
<authorization>
|
||||||
|
<deny users="*" /> <!-- Denies all users -->
|
||||||
|
</authorization>
|
||||||
|
</security>
|
||||||
|
</system.webServer>
|
||||||
</configuration>
|
</configuration>
|
||||||
105
contributing.md
Normal file
105
contributing.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Contributing to iTop
|
||||||
|
|
||||||
|
You want to contribute to iTop? Many thanks to you! 🎉 👍
|
||||||
|
|
||||||
|
Here are some guidelines that will help us integrate your work!
|
||||||
|
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
### Subjects
|
||||||
|
You are welcome to create pull requests on any of those subjects:
|
||||||
|
|
||||||
|
* 🐛 `:bug:` bug fix
|
||||||
|
* 🔒 `:lock:` security
|
||||||
|
* 🌐 `:globe_with_meridians:` translation / i18n / l10n
|
||||||
|
|
||||||
|
If you want to implement a **new feature**, please [create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) for review.
|
||||||
|
If you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the ticket.
|
||||||
|
|
||||||
|
### License
|
||||||
|
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file),
|
||||||
|
your code must comply with this license.
|
||||||
|
|
||||||
|
If you want to use another license, you may [create an extension][wiki new ext].
|
||||||
|
|
||||||
|
[license.txt]: https://github.com/Combodo/iTop/blob/develop/license.txt
|
||||||
|
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
|
||||||
|
|
||||||
|
|
||||||
|
## Branch model
|
||||||
|
|
||||||
|
TL;DR:
|
||||||
|
> **create a fork from iTop main repository,
|
||||||
|
> create a branch based on either release branch if present, or develop otherwise**
|
||||||
|
|
||||||
|
We are using the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model. That means we have in our repo those
|
||||||
|
main branches:
|
||||||
|
|
||||||
|
- develop: ongoing development version
|
||||||
|
- release/\*: if present, that means we are working on a beta version
|
||||||
|
- master: previous stable version
|
||||||
|
|
||||||
|
For example, if no beta version is currently ongoing we could have:
|
||||||
|
|
||||||
|
- develop containing future 2.8.0 version
|
||||||
|
- master containing 2.7.x maintenance version
|
||||||
|
|
||||||
|
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
|
||||||
|
- master: 2.7.x maintenance version
|
||||||
|
|
||||||
|
And when 2.8.0 final will be out:
|
||||||
|
|
||||||
|
- develop: future 2.9.0 version
|
||||||
|
- master: 2.8.x maintenance version
|
||||||
|
- support/2.7 : 2.7.x maintenance version
|
||||||
|
|
||||||
|
|
||||||
|
## Coding
|
||||||
|
|
||||||
|
### PHP styleguide
|
||||||
|
|
||||||
|
Please follow [our guidelines](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Acoding_standards).
|
||||||
|
|
||||||
|
### 🌐 Translations
|
||||||
|
|
||||||
|
A [dedicated page](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Atranslation) is available in the official wiki.
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
Please create tests that covers as much as possible the code you're submitting.
|
||||||
|
|
||||||
|
Our tests are located in the `test/` directory, containing a PHPUnit config file : `phpunit.xml.dist`.
|
||||||
|
|
||||||
|
### Git Commit Messages
|
||||||
|
|
||||||
|
* Describe the functional change instead of the technical modifications
|
||||||
|
* 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.carloscuesta.me/)). For example :
|
||||||
|
* 🌐 `:globe_with_meridians:` for translations
|
||||||
|
* 🎨 `:art:` when improving the format/structure of the code
|
||||||
|
* ⚡️ `:zap:` when improving performance
|
||||||
|
* 🐛 `:bug:` when fixing a bug
|
||||||
|
* 🔥 `:fire:` when removing code or files
|
||||||
|
* 💚 `:green_heart:` when fixing the CI build
|
||||||
|
* ✅ `:white_check_mark:` when adding tests
|
||||||
|
* 🔒 `:lock:` when dealing with security
|
||||||
|
* ⬆️ `:arrow_up:` when upgrading dependencies
|
||||||
|
* ⬇️ `:arrow_down:` when downgrading dependencies
|
||||||
|
* ♻️ `:recycle:` code refactoring
|
||||||
|
* 💄 `:lipstick:` Updating the UI and style files.
|
||||||
|
|
||||||
|
## Pull request
|
||||||
|
|
||||||
|
When your code is working, please:
|
||||||
|
|
||||||
|
* stash as much as possible your commits,
|
||||||
|
* rebase your branch on our repo last commit,
|
||||||
|
* create a pull request.
|
||||||
|
|
||||||
|
Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
||||||
@@ -314,42 +314,54 @@ class ActionEmail extends ActionNotification
|
|||||||
{
|
{
|
||||||
$this->m_iRecipients = 0;
|
$this->m_iRecipients = 0;
|
||||||
$this->m_aMailErrors = array();
|
$this->m_aMailErrors = array();
|
||||||
$bRes = false; // until we do succeed in sending the email
|
|
||||||
|
|
||||||
// Determine recicipients
|
// Determine recicipients
|
||||||
//
|
//
|
||||||
$sTo = $this->FindRecipients('to', $aContextArgs);
|
$sTo = $this->FindRecipients('to', $aContextArgs);
|
||||||
$sCC = $this->FindRecipients('cc', $aContextArgs);
|
$sCC = $this->FindRecipients('cc', $aContextArgs);
|
||||||
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
|
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
|
||||||
|
|
||||||
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
|
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
|
||||||
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
|
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
|
||||||
|
|
||||||
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
||||||
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
|
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
|
||||||
|
|
||||||
$oObj = $aContextArgs['this->object()'];
|
$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.'>';
|
$sReference = '<'.$sMessageId.'>';
|
||||||
}
|
}
|
||||||
catch(Exception $e)
|
catch (Exception $e) {
|
||||||
{
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
throw $e;
|
||||||
throw $e;
|
}
|
||||||
}
|
finally {
|
||||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||||
|
}
|
||||||
if (!is_null($oLog))
|
|
||||||
{
|
if (!is_null($oLog)) {
|
||||||
// Note: we have to secure this because those values are calculated
|
// Note: we have to secure this because those values are calculated
|
||||||
// inside the try statement, and we would like to keep track of as
|
// 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
|
// many data as we could while some variables may still be undefined
|
||||||
if (isset($sTo)) $oLog->Set('to', $sTo);
|
if (isset($sTo)) {
|
||||||
if (isset($sCC)) $oLog->Set('cc', $sCC);
|
$oLog->Set('to', $sTo);
|
||||||
if (isset($sBCC)) $oLog->Set('bcc', $sBCC);
|
}
|
||||||
if (isset($sFrom)) $oLog->Set('from', $sFrom);
|
if (isset($sCC)) {
|
||||||
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
|
$oLog->Set('cc', $sCC);
|
||||||
if (isset($sBody)) $oLog->Set('body', $sBody);
|
}
|
||||||
|
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 = file_get_contents(APPROOT.'css/email.css');
|
||||||
$sStyles .= MetaModel::GetConfig()->Get('email_css');
|
$sStyles .= MetaModel::GetConfig()->Get('email_css');
|
||||||
@@ -439,4 +451,3 @@ class ActionEmail extends ActionNotification
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
|
|||||||
@@ -166,14 +166,7 @@ class apcFile
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (is_file($sCache))
|
if (!@unlink($sCache))
|
||||||
{
|
|
||||||
if (!@unlink($sCache))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -216,14 +209,8 @@ class apcFile
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_file(self::GetCacheFileName($sKey)))
|
@unlink(self::GetCacheFileName($sKey));
|
||||||
{
|
@unlink(self::GetCacheFileName('-'.$sKey));
|
||||||
@unlink(self::GetCacheFileName($sKey));
|
|
||||||
}
|
|
||||||
if (is_file(self::GetCacheFileName('-'.$sKey)))
|
|
||||||
{
|
|
||||||
@unlink(self::GetCacheFileName('-'.$sKey));
|
|
||||||
}
|
|
||||||
if ($iTTL > 0)
|
if ($iTTL > 0)
|
||||||
{
|
{
|
||||||
// hint for ttl management
|
// hint for ttl management
|
||||||
@@ -325,10 +312,6 @@ class apcFile
|
|||||||
*/
|
*/
|
||||||
static protected function ReadCacheLocked($sFilename)
|
static protected function ReadCacheLocked($sFilename)
|
||||||
{
|
{
|
||||||
if (!is_file($sFilename))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$file = @fopen($sFilename, 'r');
|
$file = @fopen($sFilename, 'r');
|
||||||
if ($file === false)
|
if ($file === false)
|
||||||
{
|
{
|
||||||
|
|||||||
335
core/archive.class.inc.php
Normal file
335
core/archive.class.inc.php
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
<?php
|
||||||
|
// Copyright (C) 2010-2012 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/>
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility to import/export the DB from/to a ZIP file
|
||||||
|
*
|
||||||
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iTopArchive a class to manipulate (read/write) iTop archives with their catalog
|
||||||
|
* Each iTop archive is a zip file that contains (at the root of the archive)
|
||||||
|
* a file called catalog.xml holding the description of the archive
|
||||||
|
*/
|
||||||
|
class iTopArchive
|
||||||
|
{
|
||||||
|
const read = 0;
|
||||||
|
const create = ZipArchive::CREATE;
|
||||||
|
|
||||||
|
protected $m_sZipPath;
|
||||||
|
protected $m_oZip;
|
||||||
|
protected $m_sVersion;
|
||||||
|
protected $m_sTitle;
|
||||||
|
protected $m_sDescription;
|
||||||
|
protected $m_aPackages;
|
||||||
|
protected $m_aErrorMessages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an iTopArchive object
|
||||||
|
* @param $sArchivePath string The full path the archive file
|
||||||
|
* @param $iMode integrer Either iTopArchive::read for reading an existing archive or iTopArchive::create for creating a new one. Updating is not supported (yet)
|
||||||
|
*/
|
||||||
|
public function __construct($sArchivePath, $iMode = iTopArchive::read)
|
||||||
|
{
|
||||||
|
$this->m_sZipPath = $sArchivePath;
|
||||||
|
$this->m_oZip = new ZipArchive();
|
||||||
|
$this->m_oZip->open($this->m_sZipPath, $iMode);
|
||||||
|
$this->m_aErrorMessages = array();
|
||||||
|
$this->m_sVersion = '1.0';
|
||||||
|
$this->m_sTitle = '';
|
||||||
|
$this->m_sDescription = '';
|
||||||
|
$this->m_aPackages = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetTitle($sTitle)
|
||||||
|
{
|
||||||
|
$this->m_sTitle = $sTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function SetDescription($sDescription)
|
||||||
|
{
|
||||||
|
$this->m_sDescription = $sDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetTitle()
|
||||||
|
{
|
||||||
|
return $this->m_sTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetDescription()
|
||||||
|
{
|
||||||
|
return $this->m_sDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetPackages()
|
||||||
|
{
|
||||||
|
return $this->m_aPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __destruct()
|
||||||
|
{
|
||||||
|
$this->m_oZip->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the error message explaining the latest error encountered
|
||||||
|
* @return array All the error messages encountered during the validation
|
||||||
|
*/
|
||||||
|
public function GetErrors()
|
||||||
|
{
|
||||||
|
return $this->m_aErrorMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the catalog from the archive (zip) file
|
||||||
|
* @param sPath string Path the the zip file
|
||||||
|
* @return boolean True in case of success, false otherwise
|
||||||
|
*/
|
||||||
|
public function ReadCatalog()
|
||||||
|
{
|
||||||
|
if ($this->IsValid())
|
||||||
|
{
|
||||||
|
$sXmlCatalog = $this->m_oZip->getFromName('catalog.xml');
|
||||||
|
$oParser = xml_parser_create();
|
||||||
|
xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes);
|
||||||
|
xml_parser_free($oParser);
|
||||||
|
|
||||||
|
$iIndex = $aIndexes['ARCHIVE'][0];
|
||||||
|
$this->m_sVersion = $aValues[$iIndex]['attributes']['VERSION'];
|
||||||
|
$iIndex = $aIndexes['TITLE'][0];
|
||||||
|
$this->m_sTitle = $aValues[$iIndex]['value'];
|
||||||
|
$iIndex = $aIndexes['DESCRIPTION'][0];
|
||||||
|
if (array_key_exists('value', $aValues[$iIndex]))
|
||||||
|
{
|
||||||
|
// #@# implement a get_array_value(array, key, default) ?
|
||||||
|
$this->m_sDescription = $aValues[$iIndex]['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($aIndexes['PACKAGE'] as $iIndex)
|
||||||
|
{
|
||||||
|
$this->m_aPackages[$aValues[$iIndex]['attributes']['HREF']] = array( 'type' => $aValues[$iIndex]['attributes']['TYPE'], 'title'=> $aValues[$iIndex]['attributes']['TITLE'], 'description' => $aValues[$iIndex]['value']);
|
||||||
|
}
|
||||||
|
|
||||||
|
//echo "Archive path: {$this->m_sZipPath}<br/>\n";
|
||||||
|
//echo "Archive format version: {$this->m_sVersion}<br/>\n";
|
||||||
|
//echo "Title: {$this->m_sTitle}<br/>\n";
|
||||||
|
//echo "Description: {$this->m_sDescription}<br/>\n";
|
||||||
|
//foreach($this->m_aPackages as $aFile)
|
||||||
|
//{
|
||||||
|
// echo "{$aFile['title']} ({$aFile['type']}): {$aFile['description']}<br/>\n";
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function WriteCatalog()
|
||||||
|
{
|
||||||
|
$sXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?".">\n"; // split the XML closing tag that disturbs PSPad's syntax coloring
|
||||||
|
$sXml .= "<archive version=\"1.0\">\n";
|
||||||
|
$sXml .= "<title>{$this->m_sTitle}</title>\n";
|
||||||
|
$sXml .= "<description>{$this->m_sDescription}</description>\n";
|
||||||
|
foreach($this->m_aPackages as $sFileName => $aFile)
|
||||||
|
{
|
||||||
|
$sXml .= "<package title=\"{$aFile['title']}\" type=\"{$aFile['type']}\" href=\"$sFileName\">{$aFile['description']}</package>\n";
|
||||||
|
}
|
||||||
|
$sXml .= "</archive>";
|
||||||
|
$this->m_oZip->addFromString('catalog.xml', $sXml);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a package to the archive
|
||||||
|
* @param string $sExternalFilePath The path to the file to be added to the archive as a package (directories are not yet implemented)
|
||||||
|
* @param string $sFilePath The name of the file inside the archive
|
||||||
|
* @param string $sTitle A short title for this package
|
||||||
|
* @param string $sType Type of the package. SQL scripts must be of type 'text/sql'
|
||||||
|
* @param string $sDescription A longer description of the purpose of this package
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
public function AddPackage($sExternalFilePath, $sFilePath, $sTitle, $sType, $sDescription)
|
||||||
|
{
|
||||||
|
$this->m_aPackages[$sFilePath] = array('title' => $sTitle, 'type' => $sType, 'description' => $sDescription);
|
||||||
|
$this->m_oZip->addFile($sExternalFilePath, $sFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the contents of the given file from the archive
|
||||||
|
* @param string $sFileName The path to the file inside the archive
|
||||||
|
* @return string The content of the file read from the archive
|
||||||
|
*/
|
||||||
|
public function GetFileContents($sFileName)
|
||||||
|
{
|
||||||
|
return $this->m_oZip->getFromName($sFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the contents of the given file from the archive
|
||||||
|
* @param string $sFileName The path to the file inside the archive
|
||||||
|
* @param string $sDestinationFileName The path of the file to write
|
||||||
|
* @return none
|
||||||
|
*/
|
||||||
|
public function ExtractToFile($sFileName, $sDestinationFileName)
|
||||||
|
{
|
||||||
|
$iBufferSize = 64 * 1024; // Read 64K at a time
|
||||||
|
$oZipStream = $this->m_oZip->getStream($sFileName);
|
||||||
|
$oDestinationStream = fopen($sDestinationFileName, 'wb');
|
||||||
|
while (!feof($oZipStream)) {
|
||||||
|
$sContents = fread($oZipStream, $iBufferSize);
|
||||||
|
fwrite($oDestinationStream, $sContents);
|
||||||
|
}
|
||||||
|
fclose($oZipStream);
|
||||||
|
fclose($oDestinationStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a SQL script taken from the archive. The package must be listed in the catalog and of type text/sql
|
||||||
|
* @param string $sFileName The path to the SQL package inside the archive
|
||||||
|
* @return boolean false in case of error, true otherwise
|
||||||
|
*/
|
||||||
|
public function ImportSql($sFileName, $sDatabase = 'itop')
|
||||||
|
{
|
||||||
|
if ( ($this->m_oZip->locateName($sFileName) == false) || (!isset($this->m_aPackages[$sFileName])) || ($this->m_aPackages[$sFileName]['type'] != 'text/sql'))
|
||||||
|
{
|
||||||
|
// invalid type or not listed in the catalog
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$sTempName = tempnam("../tmp/", "sql");
|
||||||
|
//echo "Extracting to: '$sTempName'<br/>\n";
|
||||||
|
$this->ExtractToFile($sFileName, $sTempName);
|
||||||
|
// Note: the command line below works on Windows with the right path to mysql !!!
|
||||||
|
$sCommandLine = 'type "'.$sTempName.'" | "/iTop/MySQL Server 5.0/bin/mysql.exe" -u root '.$sDatabase;
|
||||||
|
//echo "Executing: '$sCommandLine'<br/>\n";
|
||||||
|
exec($sCommandLine, $aOutput, $iRet);
|
||||||
|
//echo "Return code: $iRet<br/>\n";
|
||||||
|
//echo "Output:<br/><pre>\n";
|
||||||
|
//print_r($aOutput);
|
||||||
|
//echo "</pre><br/>\n";
|
||||||
|
unlink($sTempName);
|
||||||
|
return ($iRet == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumps some part of the specified MySQL database into the archive as a text/sql package
|
||||||
|
* @param $sTitle string A short title for this SQL script
|
||||||
|
* @param $sDescription string A longer description of the purpose of this SQL script
|
||||||
|
* @param $sFileName string The name of the package inside the archive
|
||||||
|
* @param $sDatabase string name of the database
|
||||||
|
* @param $aTables array array or table names. If empty, all tables are dumped
|
||||||
|
* @param $bStructureOnly boolean Whether or not to dump the data or just the schema
|
||||||
|
* @return boolean False in case of error, true otherwise
|
||||||
|
*/
|
||||||
|
public function AddDatabaseDump($sTitle, $sDescription, $sFileName, $sDatabase = 'itop', $aTables = array(), $bStructureOnly = true)
|
||||||
|
{
|
||||||
|
$sTempName = tempnam("../tmp/", "sql");
|
||||||
|
$sNoData = $bStructureOnly ? "--no-data" : "";
|
||||||
|
$sCommandLine = "\"/iTop/MySQL Server 5.0/bin/mysqldump.exe\" --user=root --opt $sNoData --result-file=$sTempName $sDatabase ".implode(" ", $aTables);
|
||||||
|
//echo "Executing command: '$sCommandLine'<br/>\n";
|
||||||
|
exec($sCommandLine, $aOutput, $iRet);
|
||||||
|
//echo "Return code: $iRet<br/>\n";
|
||||||
|
//echo "Output:<br/><pre>\n";
|
||||||
|
//print_r($aOutput);
|
||||||
|
//echo "</pre><br/>\n";
|
||||||
|
if ($iRet == 0)
|
||||||
|
{
|
||||||
|
$this->AddPackage($sTempName, $sFileName, $sTitle, 'text/sql', $sDescription);
|
||||||
|
}
|
||||||
|
//unlink($sTempName);
|
||||||
|
return ($iRet == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the consistency of the archive
|
||||||
|
* @return boolean True if the archive file is consistent
|
||||||
|
*/
|
||||||
|
public function IsValid()
|
||||||
|
{
|
||||||
|
// TO DO: use a DTD to validate the XML instead of this hand-made validation
|
||||||
|
$bResult = true;
|
||||||
|
$aMandatoryTags = array('ARCHIVE' => array('VERSION'),
|
||||||
|
'TITLE' => array(),
|
||||||
|
'DESCRIPTION' => array(),
|
||||||
|
'PACKAGE' => array('TYPE', 'HREF', 'TITLE'));
|
||||||
|
|
||||||
|
$sXmlCatalog = $this->m_oZip->getFromName('catalog.xml');
|
||||||
|
$oParser = xml_parser_create();
|
||||||
|
xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes);
|
||||||
|
xml_parser_free($oParser);
|
||||||
|
|
||||||
|
foreach($aMandatoryTags as $sTag => $aAttributes)
|
||||||
|
{
|
||||||
|
// Check that all the required tags are present
|
||||||
|
if (!isset($aIndexes[$sTag]))
|
||||||
|
{
|
||||||
|
$this->m_aErrorMessages[] = "The XML catalog does not contain the mandatory tag $sTag.";
|
||||||
|
$bResult = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach($aIndexes[$sTag] as $iIndex)
|
||||||
|
{
|
||||||
|
switch($aValues[$iIndex]['type'])
|
||||||
|
{
|
||||||
|
case 'complete':
|
||||||
|
case 'open':
|
||||||
|
// Check that all the required attributes are present
|
||||||
|
foreach($aAttributes as $sAttribute)
|
||||||
|
{
|
||||||
|
if (!isset($aValues[$iIndex]['attributes'][$sAttribute]))
|
||||||
|
{
|
||||||
|
$this->m_aErrorMessages[] = "The tag $sTag ($iIndex) does not contain the required attribute $sAttribute.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// ignore other type of tags: close or cdata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $bResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// Unit test - reading an archive
|
||||||
|
$sArchivePath = '../tmp/archive.zip';
|
||||||
|
$oArchive = new iTopArchive($sArchivePath, iTopArchive::read);
|
||||||
|
$oArchive->ReadCatalog();
|
||||||
|
$oArchive->ImportSql('full_backup.sql');
|
||||||
|
|
||||||
|
// Writing an archive --
|
||||||
|
|
||||||
|
$sArchivePath = '../tmp/archive2.zip';
|
||||||
|
$oArchive = new iTopArchive($sArchivePath, iTopArchive::create);
|
||||||
|
$oArchive->SetTitle('First Archive !');
|
||||||
|
$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.');
|
||||||
|
$oArchive->AddPackage('../tmp/schema.sql', 'test.sql', 'this is just a test', 'text/sql', 'My first attempt at creating an archive from PHP...');
|
||||||
|
$oArchive->WriteCatalog();
|
||||||
|
|
||||||
|
|
||||||
|
$sArchivePath = '../tmp/archive2.zip';
|
||||||
|
$oArchive = new iTopArchive($sArchivePath, iTopArchive::create);
|
||||||
|
$oArchive->SetTitle('First Archive !');
|
||||||
|
$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.');
|
||||||
|
$oArchive->AddDatabaseDump('Test', 'This is my first automatic dump', 'schema.sql', 'itop', array('objects'));
|
||||||
|
$oArchive->WriteCatalog();
|
||||||
|
*/
|
||||||
|
?>
|
||||||
@@ -73,7 +73,7 @@ define('EXTKEY_ABSOLUTE', 2);
|
|||||||
define('DEL_MANUAL', 1);
|
define('DEL_MANUAL', 1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Propagation of the deletion through an external key - remove linked objects if ext key has is_null_allowed=false
|
* Propagation of the deletion through an external key - ask the user to delete the referencing object
|
||||||
*
|
*
|
||||||
* @package iTopORM
|
* @package iTopORM
|
||||||
*/
|
*/
|
||||||
@@ -1299,7 +1299,7 @@ class AttributeDashboard extends AttributeDefinition
|
|||||||
{
|
{
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
@@ -3555,22 +3555,6 @@ class AttributeFinalClass extends AttributeString
|
|||||||
|
|
||||||
return $aLocalizedValues;
|
return $aLocalizedValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
* @throws \CoreException
|
|
||||||
* @since 2.7
|
|
||||||
*/
|
|
||||||
public function CopyOnAllTables()
|
|
||||||
{
|
|
||||||
$sClass = self::GetHostClass();
|
|
||||||
if (MetaModel::IsLeafClass($sClass))
|
|
||||||
{
|
|
||||||
// Leaf class, no finalclass
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -6913,16 +6897,7 @@ class AttributeExternalField extends AttributeDefinition
|
|||||||
{
|
{
|
||||||
$sFormFieldClass = $oRemoteAttDef::GetFormFieldClass();
|
$sFormFieldClass = $oRemoteAttDef::GetFormFieldClass();
|
||||||
}
|
}
|
||||||
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
|
|
||||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||||
switch ($sFormFieldClass)
|
|
||||||
{
|
|
||||||
case '\Combodo\iTop\Form\Field\SelectField':
|
|
||||||
$oFormField->SetChoices($oRemoteAttDef->GetAllowedValues($oObject->ToArgsForQuery()));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
parent::MakeFormField($oObject, $oFormField);
|
parent::MakeFormField($oObject, $oFormField);
|
||||||
|
|
||||||
@@ -7362,6 +7337,13 @@ class AttributeImage extends AttributeBlob
|
|||||||
{
|
{
|
||||||
$oDoc = parent::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
|
// The validation of the MIME Type is done by CheckFormat below
|
||||||
return $oDoc;
|
return $oDoc;
|
||||||
}
|
}
|
||||||
@@ -7416,17 +7398,7 @@ class AttributeImage extends AttributeBlob
|
|||||||
return '<div class="'.$sCssClasses.'" style="width: '.$iMaxWidthPx.'; height: '.$iMaxHeightPx.';"><span class="helper-middle"></span>'.$sRet.'</div>';
|
return '<div class="'.$sCssClasses.'" style="width: '.$iMaxWidthPx.'; height: '.$iMaxHeightPx.';"><span class="helper-middle"></span>'.$sRet.'</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function GetHtmlForImageUrl($sUrl, $iMaxWidthPx, $iMaxHeightPx) {
|
||||||
* @param string $sUrl
|
|
||||||
* @param int $iMaxWidthPx
|
|
||||||
* @param int $iMaxHeightPx
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @since 2.6.0 new private method
|
|
||||||
* @since 2.7.0 change visibility to protected
|
|
||||||
*/
|
|
||||||
protected function GetHtmlForImageUrl($sUrl, $iMaxWidthPx, $iMaxHeightPx) {
|
|
||||||
return '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'; max-height: '.$iMaxHeightPx.'">';
|
return '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'; max-height: '.$iMaxHeightPx.'">';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7435,11 +7407,8 @@ class AttributeImage extends AttributeBlob
|
|||||||
* @param \DBObject $oHostObject
|
* @param \DBObject $oHostObject
|
||||||
*
|
*
|
||||||
* @return null|string
|
* @return null|string
|
||||||
*
|
|
||||||
* @since 2.6.0 new private method
|
|
||||||
* @since 2.7.0 change visibility to protected
|
|
||||||
*/
|
*/
|
||||||
protected function GetAttributeImageFileUrl($value, $oHostObject) {
|
private function GetAttributeImageFileUrl($value, $oHostObject) {
|
||||||
if (!is_object($value)) {
|
if (!is_object($value)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class BackgroundTask
|
* Class BackgroundTask
|
||||||
* A class to record information about the execution of background processes ({@link iProcess} impl)
|
* A class to record information about the execution of background processes
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
@@ -55,7 +55,6 @@ class BackgroundTask extends DBObject
|
|||||||
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeBoolean("running", array("allowed_values"=>null, "sql"=>"running", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeBoolean("running", array("allowed_values"=>null, "sql"=>"running", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('active,paused'), "sql"=>"status", "default_value"=>'active', "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('active,paused'), "sql"=>"status", "default_value"=>'active', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("system_user", array("allowed_values"=>null, "sql"=>"system_user", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ComputeDurations($fLatestDuration)
|
public function ComputeDurations($fLatestDuration)
|
||||||
@@ -74,4 +73,4 @@ class BackgroundTask extends DBObject
|
|||||||
}
|
}
|
||||||
$this->Set('latest_run_duration', sprintf('%.3f',$fLatestDuration));
|
$this->Set('latest_run_duration', sprintf('%.3f',$fLatestDuration));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,6 +59,7 @@ require_once('sqlobjectquery.class.inc.php');
|
|||||||
require_once('sqlunionquery.class.inc.php');
|
require_once('sqlunionquery.class.inc.php');
|
||||||
|
|
||||||
require_once('dbobject.class.php');
|
require_once('dbobject.class.php');
|
||||||
|
require_once('dbsearch.class.php');
|
||||||
require_once('dbobjectset.class.php');
|
require_once('dbobjectset.class.php');
|
||||||
|
|
||||||
require_once('backgroundprocess.inc.php');
|
require_once('backgroundprocess.inc.php');
|
||||||
|
|||||||
@@ -99,14 +99,6 @@ class MySQLHasGoneAwayException extends MySQLException
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
class MySQLNoTransactionException extends MySQLException
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CMDBSource
|
* CMDBSource
|
||||||
@@ -138,12 +130,6 @@ class CMDBSource
|
|||||||
/** @var mysqli $m_oMysqli */
|
/** @var mysqli $m_oMysqli */
|
||||||
protected static $m_oMysqli;
|
protected static $m_oMysqli;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var int number of level for nested transactions : 0 if no transaction was ever opened, +1 for each 'START TRANSACTION' sent
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
protected static $m_iTransactionLevel = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SQL charset & collation declaration for text columns
|
* SQL charset & collation declaration for text columns
|
||||||
*
|
*
|
||||||
@@ -151,7 +137,7 @@ class CMDBSource
|
|||||||
* use expression as value)
|
* use expression as value)
|
||||||
*
|
*
|
||||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-column.html
|
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-column.html
|
||||||
* @since 2.5.1 N°1001 switch to utf8mb4
|
* @since 2.5 N°1001 switch to utf8mb4
|
||||||
*/
|
*/
|
||||||
public static function GetSqlStringColumnDefinition()
|
public static function GetSqlStringColumnDefinition()
|
||||||
{
|
{
|
||||||
@@ -303,11 +289,11 @@ class CMDBSource
|
|||||||
$iConnectInfoCount = count($aConnectInfo);
|
$iConnectInfoCount = count($aConnectInfo);
|
||||||
if ($bUsePersistentConnection && ($iConnectInfoCount == 3))
|
if ($bUsePersistentConnection && ($iConnectInfoCount == 3))
|
||||||
{
|
{
|
||||||
$iPort = (int)($aConnectInfo[2]);
|
$iPort = $aConnectInfo[2];
|
||||||
}
|
}
|
||||||
else if (!$bUsePersistentConnection && ($iConnectInfoCount == 2))
|
else if (!$bUsePersistentConnection && ($iConnectInfoCount == 2))
|
||||||
{
|
{
|
||||||
$iPort = (int)($aConnectInfo[1]);
|
$iPort = $aConnectInfo[1];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -424,14 +410,6 @@ class CMDBSource
|
|||||||
return $aVersions[0];
|
return $aVersions[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function GetServerInfo()
|
|
||||||
{
|
|
||||||
return mysqli_get_server_info ( self::$m_oMysqli );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the DB vendor between MySQL and its main forks
|
* Get the DB vendor between MySQL and its main forks
|
||||||
* @return string
|
* @return string
|
||||||
@@ -592,68 +570,25 @@ class CMDBSource
|
|||||||
/**
|
/**
|
||||||
* @param string $sSQLQuery
|
* @param string $sSQLQuery
|
||||||
*
|
*
|
||||||
* @return \mysqli_result|null
|
* @return \mysqli_result
|
||||||
* @throws \MySQLException
|
* @throws \MySQLException
|
||||||
* @throws \MySQLHasGoneAwayException
|
* @throws \MySQLHasGoneAwayException
|
||||||
* @throws \CoreException
|
|
||||||
*
|
|
||||||
* @since 2.7.0 N°679 handles nested transactions
|
|
||||||
*/
|
*/
|
||||||
public static function Query($sSQLQuery)
|
public static function Query($sSQLQuery)
|
||||||
{
|
|
||||||
if (preg_match('/^START TRANSACTION;?$/i', $sSQLQuery))
|
|
||||||
{
|
|
||||||
self::StartTransaction();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (preg_match('/^COMMIT;?$/i', $sSQLQuery))
|
|
||||||
{
|
|
||||||
self::Commit();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (preg_match('/^ROLLBACK;?$/i', $sSQLQuery))
|
|
||||||
{
|
|
||||||
self::Rollback();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return self::DBQuery($sSQLQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send the query directly to the DB. **Be extra cautious with this !**
|
|
||||||
*
|
|
||||||
* Use {@link Query} if you're not sure.
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*
|
|
||||||
* @param string $sSql
|
|
||||||
*
|
|
||||||
* @return bool|\mysqli_result
|
|
||||||
* @throws \MySQLHasGoneAwayException
|
|
||||||
* @throws \MySQLException
|
|
||||||
*
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
private static function DBQuery($sSql)
|
|
||||||
{
|
{
|
||||||
$oKPI = new ExecutionKPI();
|
$oKPI = new ExecutionKPI();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$oResult = self::$m_oMysqli->query($sSql);
|
$oResult = self::$m_oMysqli->query($sSQLQuery);
|
||||||
}
|
}
|
||||||
catch (mysqli_sql_exception $e)
|
catch(mysqli_sql_exception $e)
|
||||||
{
|
{
|
||||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery, $e));
|
||||||
}
|
}
|
||||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
$oKPI->ComputeStats('Query exec (mySQL)', $sSQLQuery);
|
||||||
if ($oResult === false)
|
if ($oResult === false)
|
||||||
{
|
{
|
||||||
$aContext = array('query' => $sSql);
|
$aContext = array('query' => $sSQLQuery);
|
||||||
|
|
||||||
$iMySqlErrorNo = self::$m_oMysqli->errno;
|
$iMySqlErrorNo = self::$m_oMysqli->errno;
|
||||||
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
|
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
|
||||||
@@ -664,134 +599,10 @@ class CMDBSource
|
|||||||
|
|
||||||
throw new MySQLException('Failed to issue SQL query', $aContext);
|
throw new MySQLException('Failed to issue SQL query', $aContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $oResult;
|
return $oResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If nested transaction, we are not starting a new one : only one global transaction will exist.
|
|
||||||
*
|
|
||||||
* Indeed [the official documentation](https://dev.mysql.com/doc/refman/5.6/en/commit.html) states :
|
|
||||||
*
|
|
||||||
* > Beginning a transaction causes any pending transaction to be committed
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
* @see m_iTransactionLevel
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
private static function StartTransaction()
|
|
||||||
{
|
|
||||||
$bHasExistingTransactions = self::IsInsideTransaction();
|
|
||||||
if (!$bHasExistingTransactions)
|
|
||||||
{
|
|
||||||
self::DBQuery('START TRANSACTION');
|
|
||||||
}
|
|
||||||
|
|
||||||
self::AddTransactionLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the COMMIT to the db only if we are at the root transaction level
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
* @see m_iTransactionLevel
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @throws \MySQLHasGoneAwayException
|
|
||||||
* @throws \MySQLNoTransactionException if called with no opened transaction
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
private static function Commit()
|
|
||||||
{
|
|
||||||
if (!self::IsInsideTransaction())
|
|
||||||
{
|
|
||||||
// should not happen !
|
|
||||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
|
||||||
}
|
|
||||||
|
|
||||||
self::RemoveLastTransactionLevel();
|
|
||||||
|
|
||||||
if (self::IsInsideTransaction())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
self::DBQuery('COMMIT');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the ROLLBACK to the db only if we are at the root transaction level
|
|
||||||
*
|
|
||||||
* The parameter allows to send a ROLLBACK whatever the current transaction level is
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
* @see m_iTransactionLevel
|
|
||||||
*
|
|
||||||
* @throws \MySQLNoTransactionException if called with no opened transaction
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @throws \MySQLHasGoneAwayException
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
private static function Rollback()
|
|
||||||
{
|
|
||||||
if (!self::IsInsideTransaction())
|
|
||||||
{
|
|
||||||
// should not happen !
|
|
||||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
|
||||||
}
|
|
||||||
self::RemoveLastTransactionLevel();
|
|
||||||
if (self::IsInsideTransaction())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self::DBQuery('ROLLBACK');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @api
|
|
||||||
* @see m_iTransactionLevel
|
|
||||||
* @return bool true if there is one transaction opened, false otherwise (not a single 'START TRANSACTION' sent)
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
public static function IsInsideTransaction()
|
|
||||||
{
|
|
||||||
return (self::$m_iTransactionLevel > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* @see m_iTransactionLevel
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
private static function AddTransactionLevel()
|
|
||||||
{
|
|
||||||
++self::$m_iTransactionLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* @see m_iTransactionLevel
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
private static function RemoveLastTransactionLevel()
|
|
||||||
{
|
|
||||||
if (self::$m_iTransactionLevel === 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
--self::$m_iTransactionLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* @see m_iTransactionLevel
|
|
||||||
* @since 2.7.0 N°679
|
|
||||||
*/
|
|
||||||
private static function RemoveAllTransactionLevels()
|
|
||||||
{
|
|
||||||
self::$m_iTransactionLevel = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sTable
|
* @param string $sTable
|
||||||
*
|
*
|
||||||
@@ -827,13 +638,6 @@ class CMDBSource
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $sSQLQuery
|
|
||||||
*
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @throws \MySQLHasGoneAwayException
|
|
||||||
*/
|
|
||||||
public static function DeleteFrom($sSQLQuery)
|
public static function DeleteFrom($sSQLQuery)
|
||||||
{
|
{
|
||||||
self::Query($sSQLQuery);
|
self::Query($sSQLQuery);
|
||||||
@@ -1302,14 +1106,14 @@ class CMDBSource
|
|||||||
public static function DBCheckTableCharsetAndCollation($sTableName)
|
public static function DBCheckTableCharsetAndCollation($sTableName)
|
||||||
{
|
{
|
||||||
$sDBName = self::DBName();
|
$sDBName = self::DBName();
|
||||||
$sTableInfoQuery = "SELECT C.CHARACTER_SET_NAME, T.TABLE_COLLATION
|
$sTableInfoQuery = "SELECT C.character_set_name, T.table_collation
|
||||||
FROM information_schema.`TABLES` T inner join information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` C
|
FROM information_schema.`TABLES` T inner join information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` C
|
||||||
ON T.table_collation = C.collation_name
|
ON T.table_collation = C.collation_name
|
||||||
WHERE T.table_schema = '$sDBName'
|
WHERE T.table_schema = '$sDBName'
|
||||||
AND T.table_name = '$sTableName';";
|
AND T.table_name = '$sTableName';";
|
||||||
$aTableInfo = self::QueryToArray($sTableInfoQuery);
|
$aTableInfo = self::QueryToArray($sTableInfoQuery);
|
||||||
$sTableCharset = $aTableInfo[0]['CHARACTER_SET_NAME'];
|
$sTableCharset = $aTableInfo[0]['character_set_name'];
|
||||||
$sTableCollation = $aTableInfo[0]['TABLE_COLLATION'];
|
$sTableCollation = $aTableInfo[0]['table_collation'];
|
||||||
|
|
||||||
if ((DEFAULT_CHARACTER_SET == $sTableCharset) && (DEFAULT_COLLATION == $sTableCollation))
|
if ((DEFAULT_CHARACTER_SET == $sTableCharset) && (DEFAULT_COLLATION == $sTableCollation))
|
||||||
{
|
{
|
||||||
@@ -1453,7 +1257,7 @@ class CMDBSource
|
|||||||
{
|
{
|
||||||
$sDBName = CMDBSource::DBName();
|
$sDBName = CMDBSource::DBName();
|
||||||
$sDBInfoQuery = "SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
|
$sDBInfoQuery = "SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
|
||||||
FROM information_schema.SCHEMATA WHERE SCHEMA_NAME = '$sDBName';";
|
FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$sDBName';";
|
||||||
$aDBInfo = CMDBSource::QueryToArray($sDBInfoQuery);
|
$aDBInfo = CMDBSource::QueryToArray($sDBInfoQuery);
|
||||||
$sDBCharset = $aDBInfo[0]['DEFAULT_CHARACTER_SET_NAME'];
|
$sDBCharset = $aDBInfo[0]['DEFAULT_CHARACTER_SET_NAME'];
|
||||||
$sDBCollation = $aDBInfo[0]['DEFAULT_COLLATION_NAME'];
|
$sDBCollation = $aDBInfo[0]['DEFAULT_COLLATION_NAME'];
|
||||||
|
|||||||
@@ -1,28 +1,25 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
// Copyright (C) 2010-2018 Combodo SARL
|
||||||
* Copyright (C) 2013-2019 Combodo SARL
|
//
|
||||||
*
|
// This file is part of iTop.
|
||||||
* This file is part of iTop.
|
//
|
||||||
*
|
// iTop is free software; you can redistribute it and/or modify
|
||||||
* iTop is free software; you can redistribute it and/or modify
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
* 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
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
// (at your option) any later version.
|
||||||
* (at your option) any later version.
|
//
|
||||||
*
|
// iTop is distributed in the hope that it will be useful,
|
||||||
* iTop is distributed in the hope that it will be useful,
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// GNU Affero General Public License for more details.
|
||||||
* GNU Affero General Public License for more details.
|
//
|
||||||
*
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
define('ITOP_APPLICATION', 'iTop');
|
define('ITOP_APPLICATION', 'iTop');
|
||||||
define('ITOP_APPLICATION_SHORT', 'iTop');
|
define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||||
define('ITOP_VERSION', '2.7.0-dev');
|
define('ITOP_VERSION', '2.6.2');
|
||||||
define('ITOP_REVISION', 'svn');
|
define('ITOP_REVISION', 'svn');
|
||||||
define('ITOP_BUILD_DATE', '$WCNOW$');
|
define('ITOP_BUILD_DATE', '$WCNOW$');
|
||||||
|
|
||||||
@@ -66,7 +63,7 @@ define('DEFAULT_MAX_DISPLAY_LIMIT', 15);
|
|||||||
define('DEFAULT_STANDARD_RELOAD_INTERVAL', 5 * 60);
|
define('DEFAULT_STANDARD_RELOAD_INTERVAL', 5 * 60);
|
||||||
define('DEFAULT_FAST_RELOAD_INTERVAL', 1 * 60);
|
define('DEFAULT_FAST_RELOAD_INTERVAL', 1 * 60);
|
||||||
define('DEFAULT_SECURE_CONNECTION_REQUIRED', false);
|
define('DEFAULT_SECURE_CONNECTION_REQUIRED', false);
|
||||||
define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|external|basic');
|
define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|basic|external');
|
||||||
define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
|
define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
|
||||||
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
|
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
|
||||||
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
|
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
|
||||||
@@ -95,8 +92,7 @@ class Config
|
|||||||
* New way to store the settings !
|
* New way to store the settings !
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
* @since 2.5.0 db* variables
|
* @since 2.5 db* variables
|
||||||
* @since 2.7.0 export_pdf_font param
|
|
||||||
*/
|
*/
|
||||||
protected $m_aSettings = array(
|
protected $m_aSettings = array(
|
||||||
'app_env_label' => array(
|
'app_env_label' => array(
|
||||||
@@ -174,27 +170,19 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'db_core_transactions_enabled' => array(
|
'db_character_set' => array( // @deprecated to remove in 2.7 ? N°1001 utf8mb4 switch
|
||||||
'type' => 'bool',
|
'type' => 'string',
|
||||||
'description' => 'If true, CRUD transactions in iTop core will be enabled',
|
'description' => 'Deprecated since iTop 2.5 : now using utf8mb4',
|
||||||
'default' => true,
|
'default' => 'DEPRECATED_2.5',
|
||||||
'value' => true,
|
'value' => '',
|
||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'db_core_transactions_retry_count' => array(
|
'db_collation' => array( // @deprecated to remove in 2.7 ? N°1001 utf8mb4 switch
|
||||||
'type' => 'integer',
|
'type' => 'string',
|
||||||
'description' => 'Number of times the current transaction is tried',
|
'description' => 'Deprecated since iTop 2.5 : now using utf8mb4_unicode_ci',
|
||||||
'default' => 3,
|
'default' => 'DEPRECATED_2.5',
|
||||||
'value' => 3,
|
'value' => '',
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'db_core_transactions_retry_delay_ms' => array(
|
|
||||||
'type' => 'integer',
|
|
||||||
'description' => 'Base delay in milliseconds between transaction tries',
|
|
||||||
'default' => 500,
|
|
||||||
'value' => 500,
|
|
||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
@@ -343,16 +331,6 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => true,
|
'show_in_conf_sample' => true,
|
||||||
),
|
),
|
||||||
'export_pdf_font' => array( // @since 2.7 PR #49
|
|
||||||
'type' => 'string',
|
|
||||||
'description' => 'Font used when generating a PDF file',
|
|
||||||
'default' => 'DejaVuSans', // DejaVuSans is a UTF-8 Unicode font, embedded in the TCPPDF lib we're using
|
|
||||||
// Standard PDF fonts like helvetica or times newroman are NOT Unicode
|
|
||||||
// A new DroidSansFallback can be used to improve CJK support (se PR #49)
|
|
||||||
'value' => '',
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'access_mode' => array(
|
'access_mode' => array(
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'description' => 'Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3',
|
'description' => 'Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3',
|
||||||
@@ -385,14 +363,6 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'log_filename_builder_impl' => array(
|
|
||||||
'type' => 'string',
|
|
||||||
'description' => 'Name of the ILogFileNameBuilder to use',
|
|
||||||
'default' => 'WeeklyRotatingLogFileNameBuilder',
|
|
||||||
'value' => '',
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'log_rest_service' => array(
|
'log_rest_service' => array(
|
||||||
'type' => 'bool',
|
'type' => 'bool',
|
||||||
'description' => 'Log the usage of the REST/JSON service',
|
'description' => 'Log the usage of the REST/JSON service',
|
||||||
@@ -712,15 +682,6 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'login_debug' => array(
|
|
||||||
'type' => 'bool',
|
|
||||||
'description' => 'Activate the login FSM debug',
|
|
||||||
// examples... not used (nor 'description')
|
|
||||||
'default' => false,
|
|
||||||
'value' => false,
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'forgot_password' => array(
|
'forgot_password' => array(
|
||||||
'type' => 'bool',
|
'type' => 'bool',
|
||||||
'description' => 'Enable the "Forgot password" feature',
|
'description' => 'Enable the "Forgot password" feature',
|
||||||
@@ -841,8 +802,8 @@ class Config
|
|||||||
),
|
),
|
||||||
'email_decoration_class' => array(
|
'email_decoration_class' => array(
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fas fa-envelope" will put a mail icon.',
|
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fa fa-envelope" will put a mail icon.',
|
||||||
'default' => 'fas fa-envelope',
|
'default' => 'fa fa-envelope',
|
||||||
'value' => '',
|
'value' => '',
|
||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
@@ -865,29 +826,21 @@ class Config
|
|||||||
),
|
),
|
||||||
'phone_number_decoration_class' => array(
|
'phone_number_decoration_class' => array(
|
||||||
'type' => 'string',
|
'type' => 'string',
|
||||||
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fas fa-phone" will put a phone icon.',
|
'description' => 'CSS class(es) to use as decoration for the HTML rendering of the attribute. eg. "fa fa-phone" will put a phone icon.',
|
||||||
'default' => 'fas fa-phone',
|
'default' => 'fa fa-phone',
|
||||||
'value' => '',
|
'value' => '',
|
||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'log_kpi_duration' => array(
|
'log_kpi_duration' => array(
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'description' => 'Level of logging for troubleshooting performance issues (1 to enable, 2 +blame callers) new: add "log_kpi_slow_queries" to limit the stats',
|
'description' => 'Level of logging for troubleshooting performance issues (1 to enable, 2 +blame callers)',
|
||||||
// examples... not used
|
// examples... not used
|
||||||
'default' => 0,
|
'default' => 0,
|
||||||
'value' => 0,
|
'value' => 0,
|
||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'log_kpi_slow_queries' => array(
|
|
||||||
'type' => 'float',
|
|
||||||
'description' => 'Log only KPI duration stats lasting more than this value in seconds (0 for all)',
|
|
||||||
'default' => 1,
|
|
||||||
'value' => 1,
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'log_kpi_memory' => array(
|
'log_kpi_memory' => array(
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'description' => 'Level of logging for troubleshooting memory limit issues',
|
'description' => 'Level of logging for troubleshooting memory limit issues',
|
||||||
@@ -1096,6 +1049,14 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'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(
|
'inline_image_max_display_width' => array(
|
||||||
'type' => 'integer',
|
'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.',
|
'description' => 'The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.',
|
||||||
@@ -1152,6 +1113,14 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
|
'disable_attachments_download_legacy_portal' => array(
|
||||||
|
'type' => 'bool',
|
||||||
|
'description' => 'Disable attachments download from legacy portal',
|
||||||
|
'default' => true,
|
||||||
|
'value' => true,
|
||||||
|
'source_of_value' => '',
|
||||||
|
'show_in_conf_sample' => true,
|
||||||
|
),
|
||||||
'secure_rest_services' => array(
|
'secure_rest_services' => array(
|
||||||
'type' => 'bool',
|
'type' => 'bool',
|
||||||
'description' => 'When set to true, only the users with the profile "REST Services User" are allowed to use the REST web services.',
|
'description' => 'When set to true, only the users with the profile "REST Services User" are allowed to use the REST web services.',
|
||||||
@@ -1200,38 +1169,14 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'use_legacy_dbsearch' => array(
|
'security.disable_inline_documents_sandbox' => array(
|
||||||
'type' => 'bool',
|
'type' => 'bool',
|
||||||
'description' => 'If set, DBSearch will use legacy SQL query generation',
|
'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,
|
'default' => false,
|
||||||
'value' => false,
|
'value' => false,
|
||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
'query_cache_enabled' => array(
|
|
||||||
'type' => 'bool',
|
|
||||||
'description' => 'If set, DBSearch will use cache for query generation',
|
|
||||||
'default' => true,
|
|
||||||
'value' => true,
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'expression_cache_enabled' => array(
|
|
||||||
'type' => 'bool',
|
|
||||||
'description' => 'If set, DBSearch will use cache for query expression generation',
|
|
||||||
'default' => true,
|
|
||||||
'value' => true,
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
'log_kpi_record_oql' => array(
|
|
||||||
'type' => 'integer',
|
|
||||||
'description' => '1 => Record OQL requests and parameters',
|
|
||||||
'default' => 0,
|
|
||||||
'value' => 0,
|
|
||||||
'source_of_value' => '',
|
|
||||||
'show_in_conf_sample' => false,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
public function IsProperty($sPropCode)
|
public function IsProperty($sPropCode)
|
||||||
@@ -1243,7 +1188,6 @@ class Config
|
|||||||
* @return string identifier that can be used for example to name WebStorage/SessionStorage keys (they
|
* @return string identifier that can be used for example to name WebStorage/SessionStorage keys (they
|
||||||
* are related to a whole domain, and a domain can host multiple itop)
|
* are related to a whole domain, and a domain can host multiple itop)
|
||||||
* Beware: do not expose server side information to the client !
|
* Beware: do not expose server side information to the client !
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function GetItopInstanceid()
|
public function GetItopInstanceid()
|
||||||
{
|
{
|
||||||
@@ -1289,6 +1233,7 @@ class Config
|
|||||||
}
|
}
|
||||||
$this->m_aSettings[$sPropCode]['value'] = $value;
|
$this->m_aSettings[$sPropCode]['value'] = $value;
|
||||||
$this->m_aSettings[$sPropCode]['source_of_value'] = $sSourceDesc;
|
$this->m_aSettings[$sPropCode]['source_of_value'] = $sSourceDesc;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1491,12 +1436,6 @@ class Config
|
|||||||
$sNoise = trim(ob_get_contents());
|
$sNoise = trim(ob_get_contents());
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
}
|
}
|
||||||
catch(Error $e)
|
|
||||||
{
|
|
||||||
// PHP 7
|
|
||||||
throw new ConfigException('Error in configuration file',
|
|
||||||
array('file' => $sConfigFile, 'error' => $e->getMessage().' at line '.$e->getLine()));
|
|
||||||
}
|
|
||||||
catch (Exception $e)
|
catch (Exception $e)
|
||||||
{
|
{
|
||||||
// well, never reach in case of parsing error :-(
|
// well, never reach in case of parsing error :-(
|
||||||
@@ -1504,6 +1443,12 @@ class Config
|
|||||||
throw new ConfigException('Error in configuration file',
|
throw new ConfigException('Error in configuration file',
|
||||||
array('file' => $sConfigFile, 'error' => $e->getMessage()));
|
array('file' => $sConfigFile, 'error' => $e->getMessage()));
|
||||||
}
|
}
|
||||||
|
catch(Error $e)
|
||||||
|
{
|
||||||
|
// PHP 7
|
||||||
|
throw new ConfigException('Error in configuration file',
|
||||||
|
array('file' => $sConfigFile, 'error' => $e->getMessage().' at line '.$e->getLine()));
|
||||||
|
}
|
||||||
if (strlen($sNoise) > 0)
|
if (strlen($sNoise) > 0)
|
||||||
{
|
{
|
||||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||||
@@ -1574,14 +1519,6 @@ class Config
|
|||||||
// (we have their final path at that point)
|
// (we have their final path at that point)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $sModule
|
|
||||||
* @param string $sProperty
|
|
||||||
* @param mixed $defaultvalue
|
|
||||||
*
|
|
||||||
* @return mixed|null if present, value defined in the configuration file, if not module parameter from XML
|
|
||||||
* @see \MetaModel::GetModuleParameter()
|
|
||||||
*/
|
|
||||||
public function GetModuleSetting($sModule, $sProperty, $defaultvalue = null)
|
public function GetModuleSetting($sModule, $sProperty, $defaultvalue = null)
|
||||||
{
|
{
|
||||||
if (isset($this->m_aModuleSettings[$sModule][$sProperty]))
|
if (isset($this->m_aModuleSettings[$sModule][$sProperty]))
|
||||||
@@ -1596,11 +1533,9 @@ class Config
|
|||||||
/**
|
/**
|
||||||
* @param string $sModule
|
* @param string $sModule
|
||||||
* @param string $sProperty
|
* @param string $sProperty
|
||||||
* @param mixed $defaultvalue
|
* @param mixed|null $defaultvalue
|
||||||
*
|
*
|
||||||
* @return mixed|null parameter value defined in the XML
|
* @return mixed|null
|
||||||
* @see \MetaModel::GetModuleSetting() to get from the configuration file first
|
|
||||||
* @link https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Axml_reference#modules_parameters
|
|
||||||
*/
|
*/
|
||||||
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
|
public function GetModuleParameter($sModule, $sProperty, $defaultvalue = null)
|
||||||
{
|
{
|
||||||
@@ -1632,6 +1567,83 @@ class Config
|
|||||||
$this->m_aAddons = $aAddons;
|
$this->m_aAddons = $aAddons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @deprecated 2.5 will be removed in 2.6
|
||||||
|
* @see Config::Get() as a replacement
|
||||||
|
*/
|
||||||
|
public function GetDBHost()
|
||||||
|
{
|
||||||
|
return $this->Get('db_host');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @deprecated 2.5 will be removed in 2.6
|
||||||
|
* @see Config::Get() as a replacement
|
||||||
|
*/
|
||||||
|
public function GetDBName()
|
||||||
|
{
|
||||||
|
return $this->Get('db_name');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @deprecated 2.5 will be removed in 2.6
|
||||||
|
* @see Config::Get() as a replacement
|
||||||
|
*/
|
||||||
|
public function GetDBSubname()
|
||||||
|
{
|
||||||
|
return $this->Get('db_subname');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
|
||||||
|
* @see Config::DEFAULT_CHARACTER_SET
|
||||||
|
*/
|
||||||
|
public function GetDBCharacterSet()
|
||||||
|
{
|
||||||
|
return DEFAULT_CHARACTER_SET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @deprecated 2.5 will be removed in 2.6 N°1001 utf8mb4 switch
|
||||||
|
* @see Config::DEFAULT_COLLATION
|
||||||
|
*/
|
||||||
|
public function GetDBCollation()
|
||||||
|
{
|
||||||
|
return DEFAULT_COLLATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @deprecated 2.5 will be removed in 2.6
|
||||||
|
* @see Config::Get() as a replacement
|
||||||
|
*/
|
||||||
|
public function GetDBUser()
|
||||||
|
{
|
||||||
|
return $this->Get('db_user');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @deprecated 2.5 will be removed in 2.6
|
||||||
|
* @see Config::Get() as a replacement
|
||||||
|
*/
|
||||||
|
public function GetDBPwd()
|
||||||
|
{
|
||||||
|
return $this->Get('db_pwd');
|
||||||
|
}
|
||||||
|
|
||||||
public function GetLogGlobal()
|
public function GetLogGlobal()
|
||||||
{
|
{
|
||||||
return $this->m_bLogGlobal;
|
return $this->m_bLogGlobal;
|
||||||
@@ -1885,6 +1897,7 @@ class Config
|
|||||||
'log_notification' => $this->m_bLogNotification,
|
'log_notification' => $this->m_bLogNotification,
|
||||||
'log_issue' => $this->m_bLogIssue,
|
'log_issue' => $this->m_bLogIssue,
|
||||||
'log_web_service' => $this->m_bLogWebService,
|
'log_web_service' => $this->m_bLogWebService,
|
||||||
|
'query_cache_enabled' => $this->m_bQueryCacheEnabled,
|
||||||
'secure_connection_required' => $this->m_bSecureConnectionRequired,
|
'secure_connection_required' => $this->m_bSecureConnectionRequired,
|
||||||
);
|
);
|
||||||
foreach ($aBoolValues as $sKey => $bValue)
|
foreach ($aBoolValues as $sKey => $bValue)
|
||||||
@@ -2088,7 +2101,7 @@ class Config
|
|||||||
* selected modules
|
* selected modules
|
||||||
*
|
*
|
||||||
* @param string $sModulesDir The relative path to the directory to scan for modules (typically the 'env-xxx'
|
* @param string $sModulesDir The relative path to the directory to scan for modules (typically the 'env-xxx'
|
||||||
* directory resulting from the compilation). If null nothing will be done.
|
* directory resulting from the compilation)
|
||||||
* @param array $aSelectedModules An array of selected modules' identifiers. If null all modules found will be
|
* @param array $aSelectedModules An array of selected modules' identifiers. If null all modules found will be
|
||||||
* considered as installed
|
* considered as installed
|
||||||
*
|
*
|
||||||
@@ -2096,53 +2109,51 @@ class Config
|
|||||||
*/
|
*/
|
||||||
public function UpdateIncludes($sModulesDir, $aSelectedModules = null)
|
public function UpdateIncludes($sModulesDir, $aSelectedModules = null)
|
||||||
{
|
{
|
||||||
if ($sModulesDir === null)
|
if (!is_null($sModulesDir))
|
||||||
{
|
{
|
||||||
return;
|
// Initialize the arrays below with default values for the application...
|
||||||
}
|
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
|
||||||
|
$aAddOns = $oEmptyConfig->GetAddOns();
|
||||||
|
|
||||||
// Initialize the arrays below with default values for the application...
|
$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
|
||||||
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
|
foreach ($aModules as $sModuleId => $aModuleInfo)
|
||||||
$aAddOns = $oEmptyConfig->GetAddOns();
|
|
||||||
|
|
||||||
$aModules = ModuleDiscovery::GetAvailableModules(array(APPROOT.$sModulesDir));
|
|
||||||
foreach ($aModules as $sModuleId => $aModuleInfo)
|
|
||||||
{
|
|
||||||
list ($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
|
||||||
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules))
|
|
||||||
{
|
{
|
||||||
if (isset($aModuleInfo['settings']))
|
list ($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||||
|
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules))
|
||||||
{
|
{
|
||||||
list ($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
if (isset($aModuleInfo['settings']))
|
||||||
foreach ($aModuleInfo['settings'] as $sProperty => $value)
|
|
||||||
{
|
{
|
||||||
if (isset($this->m_aModuleSettings[$sName][$sProperty]))
|
list ($sName, $sVersion) = ModuleDiscovery::GetModuleName($sModuleId);
|
||||||
|
foreach ($aModuleInfo['settings'] as $sProperty => $value)
|
||||||
{
|
{
|
||||||
// Do nothing keep the original value
|
if (isset($this->m_aModuleSettings[$sName][$sProperty]))
|
||||||
}
|
{
|
||||||
else
|
// Do nothing keep the original value
|
||||||
{
|
}
|
||||||
$this->SetModuleSetting($sName, $sProperty, $value);
|
else
|
||||||
|
{
|
||||||
|
$this->SetModuleSetting($sName, $sProperty, $value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (isset($aModuleInfo['installer']))
|
||||||
if (isset($aModuleInfo['installer']))
|
|
||||||
{
|
|
||||||
$sModuleInstallerClass = $aModuleInfo['installer'];
|
|
||||||
if (!class_exists($sModuleInstallerClass))
|
|
||||||
{
|
{
|
||||||
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']);
|
$sModuleInstallerClass = $aModuleInfo['installer'];
|
||||||
|
if (!class_exists($sModuleInstallerClass))
|
||||||
|
{
|
||||||
|
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']);
|
||||||
|
}
|
||||||
|
if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
|
||||||
|
{
|
||||||
|
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
|
||||||
|
}
|
||||||
|
$aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
|
||||||
|
call_user_func_array($aCallSpec, array($this));
|
||||||
}
|
}
|
||||||
if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
|
|
||||||
{
|
|
||||||
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
|
|
||||||
}
|
|
||||||
$aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
|
|
||||||
call_user_func_array($aCallSpec, array($this));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->SetAddOns($aAddOns);
|
||||||
}
|
}
|
||||||
$this->SetAddOns($aAddOns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -199,17 +199,7 @@ class SecurityException extends CoreException
|
|||||||
* Throwned when querying on an object that exists in the database but is archived
|
* Throwned when querying on an object that exists in the database but is archived
|
||||||
*
|
*
|
||||||
* @see N.1108
|
* @see N.1108
|
||||||
* @since 2.5.1
|
|
||||||
*/
|
*/
|
||||||
class ArchivedObjectException extends CoreException
|
class ArchivedObjectException extends CoreException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A parameter stored in the {@link Config} is invalid
|
|
||||||
*
|
|
||||||
* @since 2.7.0
|
|
||||||
*/
|
|
||||||
class InvalidConfigParamException extends CoreException
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ class DateTimeFormat
|
|||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
// Days
|
// Days
|
||||||
'd' => array('regexpr' => '(0[1-9]|[1-2][0-9]|3[0-1])', 'datepicker' => 'dd', 'excel' => 'dd', 'moment' => 'DD'), // Day of the month: 2 digits (with leading zero)
|
'd' => array('regexpr' => '(0[1-9]|[1-2][0-9]||3[0-1])', 'datepicker' => 'dd', 'excel' => 'dd', 'moment' => 'DD'), // Day of the month: 2 digits (with leading zero)
|
||||||
'j' => array('regexpr' => '([1-9]|[1-2][0-9]|3[0-1])', 'datepicker' => 'd', 'excel' => 'd', 'moment' => 'D'), // Day of the month: 1 or 2 digits (without leading zero)
|
'j' => array('regexpr' => '([1-9]|[1-2][0-9]||3[0-1])', 'datepicker' => 'd', 'excel' => 'd', 'moment' => 'D'), // Day of the month: 1 or 2 digits (without leading zero)
|
||||||
// Months
|
// Months
|
||||||
'm' => array('regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'mm', 'excel' => 'MM', 'moment' => 'MM' ), // Month on 2 digits i.e. 01-12
|
'm' => array('regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'mm', 'excel' => 'MM', 'moment' => 'MM' ), // Month on 2 digits i.e. 01-12
|
||||||
'n' => array('regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'm', 'excel' => 'm', 'moment' => 'M'), // Month on 1 or 2 digits 1-12
|
'n' => array('regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'm', 'excel' => 'm', 'moment' => 'M'), // Month on 1 or 2 digits 1-12
|
||||||
@@ -69,7 +69,7 @@ class DateTimeFormat
|
|||||||
// Hours
|
// Hours
|
||||||
'H' => array('regexpr' => '([0-1][0-9]|2[0-3])', 'datepicker' => 'HH', 'excel' => 'HH', 'moment' => 'HH'), // Hour 00..23
|
'H' => array('regexpr' => '([0-1][0-9]|2[0-3])', 'datepicker' => 'HH', 'excel' => 'HH', 'moment' => 'HH'), // Hour 00..23
|
||||||
'h' => array('regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'hh', 'excel' => 'hh', 'moment' => 'hh'), // Hour 01..12
|
'h' => array('regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'hh', 'excel' => 'hh', 'moment' => 'hh'), // Hour 01..12
|
||||||
'G' => array('regexpr' => '([0-9]|1[0-9]|2[0-3])', 'datepicker' => 'H', 'excel' => 'H', 'moment' => 'H'), // Hour 0..23
|
'G' => array('regexpr' => '([1-9]|[1[0-9]|2[0-3])', 'datepicker' => 'H', 'excel' => 'H', 'moment' => 'H'), // Hour 0..23
|
||||||
'g' => array('regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'h', 'excel' => 'h', 'moment' => 'h'), // Hour 1..12
|
'g' => array('regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'h', 'excel' => 'h', 'moment' => 'h'), // Hour 1..12
|
||||||
'a' => array('regexpr' => '(am|pm)', 'datepicker' => 'tt', 'excel' => 'am/pm', 'moment' => 'a'),
|
'a' => array('regexpr' => '(am|pm)', 'datepicker' => 'tt', 'excel' => 'am/pm', 'moment' => 'a'),
|
||||||
'A' => array('regexpr' => '(AM|PM)', 'datepicker' => 'TT', 'excel' => 'AM/PM', 'moment' => 'A'),
|
'A' => array('regexpr' => '(AM|PM)', 'datepicker' => 'TT', 'excel' => 'AM/PM', 'moment' => 'A'),
|
||||||
|
|||||||
@@ -133,11 +133,6 @@ abstract class DBObject implements iDisplay
|
|||||||
* * false => not modified (the same value as the original value was set)
|
* * false => not modified (the same value as the original value was set)
|
||||||
*/
|
*/
|
||||||
protected $m_aModifiedAtt = array();
|
protected $m_aModifiedAtt = array();
|
||||||
/**
|
|
||||||
* @var array attname => currentvalue Persists changes for {@link DBUpdate}
|
|
||||||
* @since 2.7.0 N°2293
|
|
||||||
*/
|
|
||||||
protected $m_aChanges;
|
|
||||||
/**
|
/**
|
||||||
* @var array Set of Synch data related to this object
|
* @var array Set of Synch data related to this object
|
||||||
* <ul>
|
* <ul>
|
||||||
@@ -1336,11 +1331,11 @@ abstract class DBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
if ($bClickable)
|
if ($bClickable)
|
||||||
{
|
{
|
||||||
$sIcon = "<span class=\"object-ref-icon fas $sFA fa-1x fa-fw\"></span>";
|
$sIcon = "<span class=\"object-ref-icon fa $sFA fa-1x fa-fw\"></span>";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sIcon = "<span class=\"object-ref-icon-disabled fas $sFA fa-1x fa-fw\"></span>";
|
$sIcon = "<span class=\"object-ref-icon-disabled fa $sFA fa-1x fa-fw\"></span>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1989,7 +1984,6 @@ abstract class DBObject implements iDisplay
|
|||||||
* @throws \OQLException
|
* @throws \OQLException
|
||||||
*
|
*
|
||||||
* @since 2.6 N°659 uniqueness constraint
|
* @since 2.6 N°659 uniqueness constraint
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
protected function DoCheckUniqueness()
|
protected function DoCheckUniqueness()
|
||||||
{
|
{
|
||||||
@@ -2087,7 +2081,6 @@ abstract class DBObject implements iDisplay
|
|||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
* @throws \OQLException
|
* @throws \OQLException
|
||||||
* @since 2.6 N°659 uniqueness constraint
|
* @since 2.6 N°659 uniqueness constraint
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
|
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
|
||||||
{
|
{
|
||||||
@@ -2555,7 +2548,7 @@ abstract class DBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
$sTable = MetaModel::DBGetTable($sTableClass);
|
$sTable = MetaModel::DBGetTable($sTableClass);
|
||||||
// Abstract classes or classes having no specific attribute do not have an associated table
|
// Abstract classes or classes having no specific attribute do not have an associated table
|
||||||
if ($sTable == '') { return false; }
|
if ($sTable == '') return false;
|
||||||
|
|
||||||
$sClass = get_class($this);
|
$sClass = get_class($this);
|
||||||
|
|
||||||
@@ -2575,10 +2568,7 @@ abstract class DBObject implements iDisplay
|
|||||||
foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
|
foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
|
||||||
{
|
{
|
||||||
// Skip this attribute if not defined in this table
|
// Skip this attribute if not defined in this table
|
||||||
if (!MetaModel::IsAttributeOrigin($sTableClass, $sAttCode) && !$oAttDef->CopyOnAllTables())
|
if (!MetaModel::IsAttributeOrigin($sTableClass, $sAttCode)) continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$aAttColumns = $oAttDef->GetSQLValues($this->m_aCurrValues[$sAttCode]);
|
$aAttColumns = $oAttDef->GetSQLValues($this->m_aCurrValues[$sAttCode]);
|
||||||
foreach($aAttColumns as $sColumn => $sValue)
|
foreach($aAttColumns as $sColumn => $sValue)
|
||||||
{
|
{
|
||||||
@@ -2591,7 +2581,7 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($aValuesToWrite) == 0) { return false; }
|
if (count($aValuesToWrite) == 0) return false;
|
||||||
|
|
||||||
if (MetaModel::DBIsReadOnly())
|
if (MetaModel::DBIsReadOnly())
|
||||||
{
|
{
|
||||||
@@ -2637,6 +2627,8 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Persists object to new records in the DB
|
* Persists object to new records in the DB
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
*
|
*
|
||||||
* @return int key of the newly created object
|
* @return int key of the newly created object
|
||||||
* @throws \ArchivedObjectException
|
* @throws \ArchivedObjectException
|
||||||
@@ -2646,9 +2638,6 @@ abstract class DBObject implements iDisplay
|
|||||||
* @throws \CoreWarning
|
* @throws \CoreWarning
|
||||||
* @throws \MySQLException
|
* @throws \MySQLException
|
||||||
* @throws \OQLException
|
* @throws \OQLException
|
||||||
* @throws \Exception
|
|
||||||
*
|
|
||||||
* @internal
|
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function DBInsertNoReload()
|
public function DBInsertNoReload()
|
||||||
@@ -2708,45 +2697,20 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
// First query built upon on the root class, because the ID must be created first
|
||||||
try
|
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
|
||||||
|
|
||||||
|
// Then do the leaf class, if different from the root class
|
||||||
|
if ($sClass != $sRootClass)
|
||||||
{
|
{
|
||||||
if ($bIsTransactionEnabled)
|
$this->DBInsertSingleTable($sClass);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($bIsTransactionEnabled)
|
|
||||||
{
|
|
||||||
CMDBSource::Query('COMMIT');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception $e)
|
|
||||||
|
// Then do the other classes
|
||||||
|
foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass)
|
||||||
{
|
{
|
||||||
if ($bIsTransactionEnabled)
|
if ($sParentClass == $sRootClass) continue;
|
||||||
{
|
$this->DBInsertSingleTable($sParentClass);
|
||||||
CMDBSource::Query('ROLLBACK');
|
|
||||||
}
|
|
||||||
throw $e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->OnObjectKeyReady();
|
$this->OnObjectKeyReady();
|
||||||
@@ -2769,14 +2733,23 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
// Activate any existing trigger
|
// Activate any existing trigger
|
||||||
$sClass = get_class($this);
|
$sClass = get_class($this);
|
||||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectCreate AS t WHERE t.target_class IN (:class_list)"), array(), $aParams);
|
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectCreate AS t WHERE t.target_class IN ('$sClassList')"));
|
||||||
while ($oTrigger = $oSet->Fetch())
|
while ($oTrigger = $oSet->Fetch())
|
||||||
{
|
{
|
||||||
/** @var \Trigger $oTrigger */
|
/** @var \Trigger $oTrigger */
|
||||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callbacks registered with RegisterCallback
|
||||||
|
if (isset($this->m_aCallbacks[self::CALLBACK_AFTERINSERT]))
|
||||||
|
{
|
||||||
|
foreach ($this->m_aCallbacks[self::CALLBACK_AFTERINSERT] as $aCallBackData)
|
||||||
|
{
|
||||||
|
call_user_func_array($aCallBackData['callback'], $aCallBackData['params']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->RecordObjCreation();
|
$this->RecordObjCreation();
|
||||||
|
|
||||||
return $this->m_iKey;
|
return $this->m_iKey;
|
||||||
@@ -3019,6 +2992,7 @@ abstract class DBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead");
|
throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protect against reentrance (e.g. cascading the update of ticket logs)
|
// Protect against reentrance (e.g. cascading the update of ticket logs)
|
||||||
static $aUpdateReentrance = array();
|
static $aUpdateReentrance = array();
|
||||||
$sKey = get_class($this).'::'.$this->GetKey();
|
$sKey = get_class($this).'::'.$this->GetKey();
|
||||||
@@ -3028,7 +3002,6 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
$aUpdateReentrance[$sKey] = true;
|
$aUpdateReentrance[$sKey] = true;
|
||||||
|
|
||||||
$this->m_aChanges = array(); // reset attribute to avoid stack collisions
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$this->DoComputeValues();
|
$this->DoComputeValues();
|
||||||
@@ -3036,14 +3009,13 @@ abstract class DBObject implements iDisplay
|
|||||||
$sState = $this->GetState();
|
$sState = $this->GetState();
|
||||||
if ($sState != '')
|
if ($sState != '')
|
||||||
{
|
{
|
||||||
foreach (MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
||||||
{
|
{
|
||||||
if ($oAttDef instanceof AttributeStopWatch)
|
if ($oAttDef instanceof AttributeStopWatch)
|
||||||
{
|
{
|
||||||
if (in_array($sState, $oAttDef->GetStates()))
|
if (in_array($sState, $oAttDef->GetStates()))
|
||||||
{
|
{
|
||||||
// Compute or recompute the deadlines
|
// Compute or recompute the deadlines
|
||||||
/** @var \ormStopWatch $oSW */
|
|
||||||
$oSW = $this->Get($sAttCode);
|
$oSW = $this->Get($sAttCode);
|
||||||
$oSW->ComputeDeadlines($this, $oAttDef);
|
$oSW->ComputeDeadlines($this, $oAttDef);
|
||||||
$this->Set($sAttCode, $oSW);
|
$this->Set($sAttCode, $oSW);
|
||||||
@@ -3058,7 +3030,6 @@ abstract class DBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
// Attempting to update an unchanged object
|
// Attempting to update an unchanged object
|
||||||
unset($aUpdateReentrance[$sKey]);
|
unset($aUpdateReentrance[$sKey]);
|
||||||
|
|
||||||
return $this->m_iKey;
|
return $this->m_iKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3066,11 +3037,7 @@ abstract class DBObject implements iDisplay
|
|||||||
list($bRes, $aIssues) = $this->CheckToWrite();
|
list($bRes, $aIssues) = $this->CheckToWrite();
|
||||||
if (!$bRes)
|
if (!$bRes)
|
||||||
{
|
{
|
||||||
throw new CoreCannotSaveObjectException(array(
|
throw new CoreCannotSaveObjectException(array('issues' => $aIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
||||||
'issues' => $aIssues,
|
|
||||||
'class' => get_class($this),
|
|
||||||
'id' => $this->GetKey()
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the original values (will be reset to the new values when the object get written to the DB)
|
// Save the original values (will be reset to the new values when the object get written to the DB)
|
||||||
@@ -3078,9 +3045,8 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
// Activate any existing trigger
|
// Activate any existing trigger
|
||||||
$sClass = get_class($this);
|
$sClass = get_class($this);
|
||||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectUpdate AS t WHERE t.target_class IN (:class_list)"),
|
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectUpdate AS t WHERE t.target_class IN ('$sClassList')"));
|
||||||
array(), $aParams);
|
|
||||||
while ($oTrigger = $oSet->Fetch())
|
while ($oTrigger = $oSet->Fetch())
|
||||||
{
|
{
|
||||||
/** @var \Trigger $oTrigger */
|
/** @var \Trigger $oTrigger */
|
||||||
@@ -3090,13 +3056,10 @@ abstract class DBObject implements iDisplay
|
|||||||
$bHasANewExternalKeyValue = false;
|
$bHasANewExternalKeyValue = false;
|
||||||
$aHierarchicalKeys = array();
|
$aHierarchicalKeys = array();
|
||||||
$aDBChanges = array();
|
$aDBChanges = array();
|
||||||
foreach ($aChanges as $sAttCode => $valuecurr)
|
foreach($aChanges as $sAttCode => $valuecurr)
|
||||||
{
|
{
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||||
if ($oAttDef->IsExternalKey())
|
if ($oAttDef->IsExternalKey()) $bHasANewExternalKeyValue = true;
|
||||||
{
|
|
||||||
$bHasANewExternalKeyValue = true;
|
|
||||||
}
|
|
||||||
if ($oAttDef->IsBasedOnDBColumns())
|
if ($oAttDef->IsBasedOnDBColumns())
|
||||||
{
|
{
|
||||||
$aDBChanges[$sAttCode] = $aChanges[$sAttCode];
|
$aDBChanges[$sAttCode] = $aChanges[$sAttCode];
|
||||||
@@ -3107,176 +3070,102 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$iTransactionRetry = 1;
|
if (!MetaModel::DBIsReadOnly())
|
||||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
|
||||||
if ($bIsTransactionEnabled)
|
|
||||||
{
|
{
|
||||||
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
// Update the left & right indexes for each hierarchical key
|
||||||
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
foreach($aHierarchicalKeys as $sAttCode => $oAttDef)
|
||||||
$iTransactionRetry = $iIsTransactionRetryCount;
|
|
||||||
}
|
|
||||||
while ($iTransactionRetry > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
$iTransactionRetry--;
|
$sTable = $sTable = MetaModel::DBGetTable(get_class($this), $sAttCode);
|
||||||
if ($bIsTransactionEnabled)
|
$sSQL = "SELECT `".$oAttDef->GetSQLRight()."` AS `right`, `".$oAttDef->GetSQLLeft()."` AS `left` FROM `$sTable` WHERE id=".$this->GetKey();
|
||||||
|
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||||
|
$iMyLeft = $aRes[0]['left'];
|
||||||
|
$iMyRight = $aRes[0]['right'];
|
||||||
|
$iDelta =$iMyRight - $iMyLeft + 1;
|
||||||
|
MetaModel::HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable);
|
||||||
|
|
||||||
|
if ($aDBChanges[$sAttCode] == 0)
|
||||||
{
|
{
|
||||||
CMDBSource::Query('START TRANSACTION');
|
// No new parent, insert completely at the right of the tree
|
||||||
}
|
$sSQL = "SELECT max(`".$oAttDef->GetSQLRight()."`) AS max FROM `$sTable`";
|
||||||
if (!MetaModel::DBIsReadOnly())
|
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||||
{
|
if (count($aRes) == 0)
|
||||||
// Update the left & right indexes for each hierarchical key
|
|
||||||
foreach ($aHierarchicalKeys as $sAttCode => $oAttDef)
|
|
||||||
{
|
{
|
||||||
$sTable = $sTable = MetaModel::DBGetTable(get_class($this), $sAttCode);
|
$iNewLeft = 1;
|
||||||
$sSQL = "SELECT `".$oAttDef->GetSQLRight()."` AS `right`, `".$oAttDef->GetSQLLeft()."` AS `left` FROM `$sTable` WHERE id=".$this->GetKey();
|
|
||||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
|
||||||
$iMyLeft = $aRes[0]['left'];
|
|
||||||
$iMyRight = $aRes[0]['right'];
|
|
||||||
$iDelta = $iMyRight - $iMyLeft + 1;
|
|
||||||
MetaModel::HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable);
|
|
||||||
|
|
||||||
if ($aDBChanges[$sAttCode] == 0)
|
|
||||||
{
|
|
||||||
// No new parent, insert completely at the right of the tree
|
|
||||||
$sSQL = "SELECT max(`".$oAttDef->GetSQLRight()."`) AS max FROM `$sTable`";
|
|
||||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
|
||||||
if (count($aRes) == 0)
|
|
||||||
{
|
|
||||||
$iNewLeft = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$iNewLeft = $aRes[0]['max'] + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Insert at the right of the specified parent
|
|
||||||
$sSQL = "SELECT `".$oAttDef->GetSQLRight()."` FROM `$sTable` WHERE id=".((int)$aDBChanges[$sAttCode]);
|
|
||||||
$iNewLeft = CMDBSource::QueryToScalar($sSQL);
|
|
||||||
}
|
|
||||||
|
|
||||||
MetaModel::HKReplugBranch($iNewLeft, $iNewLeft + $iDelta - 1, $oAttDef, $sTable);
|
|
||||||
|
|
||||||
$aHKChanges = array();
|
|
||||||
$aHKChanges[$sAttCode] = $aDBChanges[$sAttCode];
|
|
||||||
$aHKChanges[$oAttDef->GetSQLLeft()] = $iNewLeft;
|
|
||||||
$aHKChanges[$oAttDef->GetSQLRight()] = $iNewLeft + $iDelta - 1;
|
|
||||||
$aDBChanges[$sAttCode] = $aHKChanges; // the 3 values will be stored by MakeUpdateQuery below
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Update scalar attributes
|
|
||||||
if (count($aDBChanges) != 0)
|
|
||||||
{
|
{
|
||||||
$oFilter = new DBObjectSearch(get_class($this));
|
$iNewLeft = $aRes[0]['max']+1;
|
||||||
$oFilter->AddCondition('id', $this->m_iKey, '=');
|
|
||||||
$oFilter->AllowAllData();
|
|
||||||
|
|
||||||
$sSQL = $oFilter->MakeUpdateQuery($aDBChanges);
|
|
||||||
CMDBSource::Query($sSQL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->DBWriteLinks();
|
else
|
||||||
$this->WriteExternalAttributes();
|
|
||||||
|
|
||||||
$this->m_aChanges = $this->ListChanges(); // N°2293 save changes for use in user callbacks
|
|
||||||
$this->m_bDirty = false;
|
|
||||||
$this->m_aTouchedAtt = array();
|
|
||||||
$this->m_aModifiedAtt = array();
|
|
||||||
|
|
||||||
if (count($aChanges) != 0)
|
|
||||||
{
|
{
|
||||||
$this->RecordAttChanges($aChanges, $aOriginalValues);
|
// Insert at the right of the specified parent
|
||||||
|
$sSQL = "SELECT `".$oAttDef->GetSQLRight()."` FROM `$sTable` WHERE id=".((int)$aDBChanges[$sAttCode]);
|
||||||
|
$iNewLeft = CMDBSource::QueryToScalar($sSQL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bIsTransactionEnabled)
|
MetaModel::HKReplugBranch($iNewLeft, $iNewLeft + $iDelta - 1, $oAttDef, $sTable);
|
||||||
{
|
|
||||||
CMDBSource::Query('COMMIT');
|
$aHKChanges = array();
|
||||||
}
|
$aHKChanges[$sAttCode] = $aDBChanges[$sAttCode];
|
||||||
break;
|
$aHKChanges[$oAttDef->GetSQLLeft()] = $iNewLeft;
|
||||||
|
$aHKChanges[$oAttDef->GetSQLRight()] = $iNewLeft + $iDelta - 1;
|
||||||
|
$aDBChanges[$sAttCode] = $aHKChanges; // the 3 values will be stored by MakeUpdateQuery below
|
||||||
}
|
}
|
||||||
catch (MySQLException $e)
|
|
||||||
|
// Update scalar attributes
|
||||||
|
if (count($aDBChanges) != 0)
|
||||||
{
|
{
|
||||||
if ($bIsTransactionEnabled)
|
$oFilter = new DBObjectSearch(get_class($this));
|
||||||
{
|
$oFilter->AddCondition('id', $this->m_iKey, '=');
|
||||||
CMDBSource::Query('ROLLBACK');
|
$oFilter->AllowAllData();
|
||||||
if ($e->getCode() == 1213)
|
|
||||||
{
|
$sSQL = $oFilter->MakeUpdateQuery($aDBChanges);
|
||||||
// Deadlock found when trying to get lock; try restarting transaction
|
CMDBSource::Query($sSQL);
|
||||||
IssueLog::Error($e->getMessage());
|
|
||||||
if ($iTransactionRetry > 0)
|
|
||||||
{
|
|
||||||
// wait and retry
|
|
||||||
IssueLog::Error("Update TRANSACTION Retrying...");
|
|
||||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IssueLog::Error("Update Deadlock TRANSACTION prevention failed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$aErrors = array($e->getMessage());
|
|
||||||
throw new CoreCannotSaveObjectException(array(
|
|
||||||
'id' => $this->GetKey(),
|
|
||||||
'class' => get_class($this),
|
|
||||||
'issues' => $aErrors
|
|
||||||
));
|
|
||||||
}
|
|
||||||
catch (CoreCannotSaveObjectException $e)
|
|
||||||
{
|
|
||||||
if ($bIsTransactionEnabled)
|
|
||||||
{
|
|
||||||
CMDBSource::Query('ROLLBACK');
|
|
||||||
}
|
|
||||||
throw $e;
|
|
||||||
}
|
|
||||||
catch (Exception $e)
|
|
||||||
{
|
|
||||||
if ($bIsTransactionEnabled)
|
|
||||||
{
|
|
||||||
CMDBSource::Query('ROLLBACK');
|
|
||||||
}
|
|
||||||
$aErrors = array($e->getMessage());
|
|
||||||
throw new CoreCannotSaveObjectException(array(
|
|
||||||
'id' => $this->GetKey(),
|
|
||||||
'class' => get_class($this),
|
|
||||||
'issues' => $aErrors
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
$this->DBWriteLinks();
|
||||||
{
|
$this->WriteExternalAttributes();
|
||||||
$this->AfterUpdate();
|
|
||||||
|
|
||||||
// Reload to get the external attributes
|
$this->m_bDirty = false;
|
||||||
if ($bHasANewExternalKeyValue)
|
$this->m_aTouchedAtt = array();
|
||||||
|
$this->m_aModifiedAtt = array();
|
||||||
|
|
||||||
|
$this->AfterUpdate();
|
||||||
|
|
||||||
|
// Reload to get the external attributes
|
||||||
|
if ($bHasANewExternalKeyValue)
|
||||||
|
{
|
||||||
|
$this->Reload(true /* AllowAllData */);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Reset original values although the object has not been reloaded
|
||||||
|
foreach ($this->m_aLoadedAtt as $sAttCode => $bLoaded)
|
||||||
{
|
{
|
||||||
$this->Reload(true /* AllowAllData */);
|
if ($bLoaded)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Reset original values although the object has not been reloaded
|
|
||||||
foreach ($this->m_aLoadedAtt as $sAttCode => $bLoaded)
|
|
||||||
{
|
{
|
||||||
if ($bLoaded)
|
$value = $this->m_aCurrValues[$sAttCode];
|
||||||
{
|
$this->m_aOrigValues[$sAttCode] = is_object($value) ? clone $value : $value;
|
||||||
$value = $this->m_aCurrValues[$sAttCode];
|
|
||||||
$this->m_aOrigValues[$sAttCode] = is_object($value) ? clone $value : $value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception $e)
|
|
||||||
|
if (count($aChanges) != 0)
|
||||||
{
|
{
|
||||||
$aErrors = array($e->getMessage());
|
$this->RecordAttChanges($aChanges, $aOriginalValues);
|
||||||
throw new CoreCannotSaveObjectException(array('id' => $this->GetKey(), 'class' => get_class($this), 'issues' => $aErrors));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (CoreCannotSaveObjectException $e)
|
||||||
|
{
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
catch (Exception $e)
|
||||||
|
{
|
||||||
|
$aErrors = array($e->getMessage());
|
||||||
|
throw new CoreCannotSaveObjectException(array('id' => $this->GetKey(), 'class' => get_class($this), 'issues' => $aErrors));
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
unset($aUpdateReentrance[$sKey]);
|
unset($aUpdateReentrance[$sKey]);
|
||||||
@@ -3329,7 +3218,6 @@ abstract class DBObject implements iDisplay
|
|||||||
* @param string $sTableClass
|
* @param string $sTableClass
|
||||||
*
|
*
|
||||||
* @throws CoreException
|
* @throws CoreException
|
||||||
* @throws MySQLException
|
|
||||||
*/
|
*/
|
||||||
private function DBDeleteSingleTable($sTableClass)
|
private function DBDeleteSingleTable($sTableClass)
|
||||||
{
|
{
|
||||||
@@ -3356,118 +3244,68 @@ abstract class DBObject implements iDisplay
|
|||||||
*/
|
*/
|
||||||
protected function DBDeleteSingleObject()
|
protected function DBDeleteSingleObject()
|
||||||
{
|
{
|
||||||
if (MetaModel::DBIsReadOnly())
|
if (!MetaModel::DBIsReadOnly())
|
||||||
{
|
{
|
||||||
return;
|
$this->OnDelete();
|
||||||
}
|
|
||||||
|
|
||||||
$this->OnDelete();
|
// Activate any existing trigger
|
||||||
|
$sClass = get_class($this);
|
||||||
// Activate any existing trigger
|
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||||
$sClass = get_class($this);
|
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectDelete AS t WHERE t.target_class IN ('$sClassList')"));
|
||||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
while ($oTrigger = $oSet->Fetch())
|
||||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectDelete AS t WHERE t.target_class IN (:class_list)"), array(),
|
|
||||||
$aParams);
|
|
||||||
while ($oTrigger = $oSet->Fetch())
|
|
||||||
{
|
|
||||||
/** @var \Trigger $oTrigger */
|
|
||||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->RecordObjDeletion($this->m_iKey); // May cause a reload for storing history information
|
|
||||||
|
|
||||||
foreach (MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
|
||||||
{
|
|
||||||
if ($oAttDef->IsHierarchicalKey())
|
|
||||||
{
|
{
|
||||||
// Update the left & right indexes for each hierarchical key
|
/** @var \Trigger $oTrigger */
|
||||||
$sTable = $sTable = MetaModel::DBGetTable(get_class($this), $sAttCode);
|
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||||
/** @var \AttributeHierarchicalKey $oAttDef */
|
|
||||||
$sSQL = "SELECT `".$oAttDef->GetSQLRight()."` AS `right`, `".$oAttDef->GetSQLLeft()."` AS `left` FROM `$sTable` WHERE id=".CMDBSource::Quote($this->m_iKey);
|
|
||||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
|
||||||
$iMyLeft = $aRes[0]['left'];
|
|
||||||
$iMyRight = $aRes[0]['right'];
|
|
||||||
$iDelta = $iMyRight - $iMyLeft + 1;
|
|
||||||
MetaModel::HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable);
|
|
||||||
|
|
||||||
// No new parent for now, insert completely at the right of the tree
|
|
||||||
$sSQL = "SELECT max(`".$oAttDef->GetSQLRight()."`) AS max FROM `$sTable`";
|
|
||||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
|
||||||
if (count($aRes) == 0)
|
|
||||||
{
|
|
||||||
$iNewLeft = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$iNewLeft = $aRes[0]['max'] + 1;
|
|
||||||
}
|
|
||||||
MetaModel::HKReplugBranch($iNewLeft, $iNewLeft + $iDelta - 1, $oAttDef, $sTable);
|
|
||||||
}
|
}
|
||||||
elseif (!$oAttDef->LoadFromDB())
|
|
||||||
|
$this->RecordObjDeletion($this->m_iKey); // May cause a reload for storing history information
|
||||||
|
|
||||||
|
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
||||||
{
|
{
|
||||||
/** @var \AttributeCustomFields $oAttDef */
|
if ($oAttDef->IsHierarchicalKey())
|
||||||
$oAttDef->DeleteValue($this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$iTransactionRetry = 1;
|
|
||||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
|
||||||
if ($bIsTransactionEnabled)
|
|
||||||
{
|
|
||||||
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
|
||||||
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
|
||||||
$iTransactionRetry = $iIsTransactionRetryCount;
|
|
||||||
}
|
|
||||||
while ($iTransactionRetry > 0)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$iTransactionRetry--;
|
|
||||||
if ($bIsTransactionEnabled)
|
|
||||||
{
|
{
|
||||||
CMDBSource::Query('START TRANSACTION');
|
// Update the left & right indexes for each hierarchical key
|
||||||
}
|
$sTable = $sTable = MetaModel::DBGetTable(get_class($this), $sAttCode);
|
||||||
foreach (MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL) as $sParentClass)
|
/** @var \AttributeHierarchicalKey $oAttDef */
|
||||||
{
|
$sSQL = "SELECT `".$oAttDef->GetSQLRight()."` AS `right`, `".$oAttDef->GetSQLLeft()."` AS `left` FROM `$sTable` WHERE id=".CMDBSource::Quote($this->m_iKey);
|
||||||
$this->DBDeleteSingleTable($sParentClass);
|
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||||
}
|
$iMyLeft = $aRes[0]['left'];
|
||||||
if ($bIsTransactionEnabled)
|
$iMyRight = $aRes[0]['right'];
|
||||||
{
|
$iDelta =$iMyRight - $iMyLeft + 1;
|
||||||
CMDBSource::Query('COMMIT');
|
MetaModel::HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable);
|
||||||
}
|
|
||||||
break;
|
// No new parent for now, insert completely at the right of the tree
|
||||||
}
|
$sSQL = "SELECT max(`".$oAttDef->GetSQLRight()."`) AS max FROM `$sTable`";
|
||||||
catch (MySQLException $e)
|
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||||
{
|
if (count($aRes) == 0)
|
||||||
if ($bIsTransactionEnabled)
|
|
||||||
{
|
|
||||||
CMDBSource::Query('ROLLBACK');
|
|
||||||
if ($e->getCode() == 1213)
|
|
||||||
{
|
{
|
||||||
// Deadlock found when trying to get lock; try restarting transaction
|
$iNewLeft = 1;
|
||||||
IssueLog::Error($e->getMessage());
|
|
||||||
if ($iTransactionRetry > 0)
|
|
||||||
{
|
|
||||||
// wait and retry
|
|
||||||
IssueLog::Error("Delete TRANSACTION Retrying...");
|
|
||||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IssueLog::Error("Delete Deadlock TRANSACTION prevention failed.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$iNewLeft = $aRes[0]['max']+1;
|
||||||
|
}
|
||||||
|
MetaModel::HKReplugBranch($iNewLeft, $iNewLeft + $iDelta - 1, $oAttDef, $sTable);
|
||||||
|
}
|
||||||
|
elseif (!$oAttDef->LoadFromDB())
|
||||||
|
{
|
||||||
|
/** @var \AttributeCustomFields $oAttDef */
|
||||||
|
$oAttDef->DeleteValue($this);
|
||||||
}
|
}
|
||||||
throw $e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach(MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL) as $sParentClass)
|
||||||
|
{
|
||||||
|
$this->DBDeleteSingleTable($sParentClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->AfterDelete();
|
||||||
|
|
||||||
|
$this->m_bIsInDB = false;
|
||||||
|
// Fix for N°926: do NOT reset m_iKey as it can be used to have it for reporting purposes (see the REST service to delete
|
||||||
|
// objects, reported as bug N°926)
|
||||||
|
// Thought the key is not reset, using DBInsert or DBWrite will create an object having the same characteristics and a new ID. DBUpdate is protected
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->AfterDelete();
|
|
||||||
|
|
||||||
$this->m_bIsInDB = false;
|
|
||||||
// Fix for N°926: do NOT reset m_iKey as it can be used to have it for reporting purposes (see the REST service to delete
|
|
||||||
// objects, reported as bug N°926)
|
|
||||||
// Thought the key is not reset, using DBInsert or DBWrite will create an object having the same characteristics and a new ID. DBUpdate is protected
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3511,46 +3349,47 @@ abstract class DBObject implements iDisplay
|
|||||||
$aIssues = $oDeletionPlan->GetIssues();
|
$aIssues = $oDeletionPlan->GetIssues();
|
||||||
throw new DeleteException('Found issue(s)', array('target_class' => get_class($this), 'target_id' => $this->GetKey(), 'issues' => implode(', ', $aIssues)));
|
throw new DeleteException('Found issue(s)', array('target_class' => get_class($this), 'target_id' => $this->GetKey(), 'issues' => implode(', ', $aIssues)));
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
|
||||||
// Getting and setting time limit are not symetric:
|
|
||||||
// www.php.net/manual/fr/function.set-time-limit.php#72305
|
|
||||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
|
||||||
|
|
||||||
foreach ($oDeletionPlan->ListDeletes() as $sClass => $aToDelete)
|
|
||||||
{
|
{
|
||||||
foreach ($aToDelete as $iId => $aData)
|
// Getting and setting time limit are not symetric:
|
||||||
|
// www.php.net/manual/fr/function.set-time-limit.php#72305
|
||||||
|
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||||
|
|
||||||
|
foreach ($oDeletionPlan->ListDeletes() as $sClass => $aToDelete)
|
||||||
{
|
{
|
||||||
/** @var \DBObject $oToDelete */
|
foreach ($aToDelete as $iId => $aData)
|
||||||
$oToDelete = $aData['to_delete'];
|
|
||||||
// The deletion based on a deletion plan should not be done for each object if the deletion plan is common (Trac #457)
|
|
||||||
// because for each object we would try to update all the preceding ones... that are already deleted
|
|
||||||
// A better approach would be to change the API to apply the DBDelete on the deletion plan itself... just once
|
|
||||||
// As a temporary fix: delete only the objects that are still to be deleted...
|
|
||||||
if ($oToDelete->m_bIsInDB)
|
|
||||||
{
|
{
|
||||||
set_time_limit($iLoopTimeLimit);
|
/** @var \DBObject $oToDelete */
|
||||||
$oToDelete->DBDeleteSingleObject();
|
$oToDelete = $aData['to_delete'];
|
||||||
|
// The deletion based on a deletion plan should not be done for each oject if the deletion plan is common (Trac #457)
|
||||||
|
// because for each object we would try to update all the preceding ones... that are already deleted
|
||||||
|
// A better approach would be to change the API to apply the DBDelete on the deletion plan itself... just once
|
||||||
|
// As a temporary fix: delete only the objects that are still to be deleted...
|
||||||
|
if ($oToDelete->m_bIsInDB)
|
||||||
|
{
|
||||||
|
set_time_limit($iLoopTimeLimit);
|
||||||
|
$oToDelete->DBDeleteSingleObject();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($oDeletionPlan->ListUpdates() as $sClass => $aToUpdate)
|
foreach ($oDeletionPlan->ListUpdates() as $sClass => $aToUpdate)
|
||||||
{
|
|
||||||
foreach ($aToUpdate as $iId => $aData)
|
|
||||||
{
|
{
|
||||||
$oToUpdate = $aData['to_reset'];
|
foreach ($aToUpdate as $iId => $aData)
|
||||||
/** @var \DBObject $oToUpdate */
|
|
||||||
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef)
|
|
||||||
{
|
{
|
||||||
$oToUpdate->Set($sRemoteExtKey, $aData['values'][$sRemoteExtKey]);
|
$oToUpdate = $aData['to_reset'];
|
||||||
set_time_limit($iLoopTimeLimit);
|
/** @var \DBObject $oToUpdate */
|
||||||
$oToUpdate->DBUpdate();
|
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef)
|
||||||
|
{
|
||||||
|
$oToUpdate->Set($sRemoteExtKey, $aData['values'][$sRemoteExtKey]);
|
||||||
|
set_time_limit($iLoopTimeLimit);
|
||||||
|
$oToUpdate->DBUpdate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
set_time_limit($iPreviousTimeLimit);
|
set_time_limit($iPreviousTimeLimit);
|
||||||
|
}
|
||||||
|
|
||||||
return $oDeletionPlan;
|
return $oDeletionPlan;
|
||||||
}
|
}
|
||||||
@@ -3590,7 +3429,7 @@ abstract class DBObject implements iDisplay
|
|||||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||||
if (empty($sStateAttCode)) return array();
|
if (empty($sStateAttCode)) return array();
|
||||||
|
|
||||||
$sState = $this->Get($sStateAttCode);
|
$sState = $this->Get(MetaModel::GetStateAttributeCode(get_class($this)));
|
||||||
return MetaModel::EnumTransitions(get_class($this), $sState);
|
return MetaModel::EnumTransitions(get_class($this), $sState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3748,18 +3587,15 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Change state triggers...
|
// Change state triggers...
|
||||||
$aParams = array(
|
$sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||||
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
|
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sPreviousState'"));
|
||||||
'previous_state' => $sPreviousState,
|
|
||||||
'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())
|
while ($oTrigger = $oSet->Fetch())
|
||||||
{
|
{
|
||||||
/** @var \Trigger $oTrigger */
|
/** @var \Trigger $oTrigger */
|
||||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
$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);
|
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN ('$sClassList') AND t.state='$sNewState'"));
|
||||||
while ($oTrigger = $oSet->Fetch())
|
while ($oTrigger = $oSet->Fetch())
|
||||||
{
|
{
|
||||||
/** @var \Trigger $oTrigger */
|
/** @var \Trigger $oTrigger */
|
||||||
@@ -4108,11 +3944,9 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called after the object is updated into DB. You can get changes by calling {@link ListChanges}.
|
* this method is called after the object is updated into DB.
|
||||||
*
|
*
|
||||||
* @overwritable-hook You can extend this method in order to provide your own logic.
|
* @overwritable-hook You can extend this method in order to provide your own logic.
|
||||||
*
|
|
||||||
* @since 2.7.0 N°2293 can access object changes by calling {@link ListChanges}
|
|
||||||
*/
|
*/
|
||||||
protected function AfterUpdate()
|
protected function AfterUpdate()
|
||||||
{
|
{
|
||||||
@@ -4441,19 +4275,8 @@ abstract class DBObject implements iDisplay
|
|||||||
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||||
return $oGraph;
|
return $oGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function GetReferencingObjects($bAllowAllData = false)
|
||||||
* @internal
|
|
||||||
*
|
|
||||||
* @param bool $bAllowAllData
|
|
||||||
*
|
|
||||||
* @return array keys : attribute (AttributeDefinition), objects (set of linked objects)
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \MissingQueryArgument
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @throws \MySQLHasGoneAwayException
|
|
||||||
*/
|
|
||||||
protected function GetReferencingObjects($bAllowAllData = false)
|
|
||||||
{
|
{
|
||||||
$aDependentObjects = array();
|
$aDependentObjects = array();
|
||||||
$aRererencingMe = MetaModel::EnumReferencingClasses(get_class($this));
|
$aRererencingMe = MetaModel::EnumReferencingClasses(get_class($this));
|
||||||
@@ -4781,6 +4604,33 @@ abstract class DBObject implements iDisplay
|
|||||||
$oPage->details($aValues);
|
$oPage->details($aValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
const CALLBACK_AFTERINSERT = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a call back that will be called when some internal event happens
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @param $iType string Any of the CALLBACK_x constants
|
||||||
|
* @param $callback callable Call specification like a function name, or array('<class>', '<method>') or array($object, '<method>')
|
||||||
|
* @param $aParameters array Values that will be passed to the callback, after $this
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function RegisterCallback($iType, $callback, $aParameters = array())
|
||||||
|
{
|
||||||
|
$sCallBackName = '';
|
||||||
|
if (!is_callable($callback, false, $sCallBackName))
|
||||||
|
{
|
||||||
|
throw new Exception('Registering an unknown/protected function or wrong syntax for the call spec: '.$sCallBackName);
|
||||||
|
}
|
||||||
|
$this->m_aCallbacks[$iType][] = array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'params' => $aParameters
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes a text-like fingerprint identifying the content of the object
|
* Computes a text-like fingerprint identifying the content of the object
|
||||||
* but excluding the specified columns
|
* but excluding the specified columns
|
||||||
@@ -5220,21 +5070,10 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
* <p>Sets the <code>archive_flag</code> <b>For all of the class hierarchy</b><br>
|
* @param boolean $bArchive
|
||||||
* Also update the <code>archive_date</code> :
|
|
||||||
* <ul>
|
|
||||||
* <li>if $bArchive==false archive_date become null
|
|
||||||
* <li>if $bArchive==true && $archive_date == null archive_date take the current date
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* <p>Can be used to fix database inconsistencies on archive_flag field.
|
|
||||||
*
|
|
||||||
* @see \DBSearch::DBBulkWriteArchiveFlag()
|
|
||||||
*
|
|
||||||
* @param boolean $bArchive if true then sets archive_flag and archive_date flags
|
|
||||||
*
|
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected function DBWriteArchiveFlag($bArchive)
|
protected function DBWriteArchiveFlag($bArchive)
|
||||||
@@ -5280,8 +5119,12 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* Can be called to repair the database (tables consistency)
|
||||||
|
* The archive_date will be preserved
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @uses DBWriteArchiveFlag
|
|
||||||
*/
|
*/
|
||||||
public function DBArchive()
|
public function DBArchive()
|
||||||
{
|
{
|
||||||
@@ -5291,8 +5134,9 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @uses DBWriteArchiveFlag
|
|
||||||
*/
|
*/
|
||||||
public function DBUnarchive()
|
public function DBUnarchive()
|
||||||
{
|
{
|
||||||
@@ -5328,18 +5172,10 @@ abstract class DBObject implements iDisplay
|
|||||||
/**
|
/**
|
||||||
* Complete a new object with data from context
|
* Complete a new object with data from context
|
||||||
*
|
*
|
||||||
* @overwritable-hook You can extend this method in order to provide your own logic.
|
* @internal
|
||||||
|
*
|
||||||
|
* @param array $aContextParam Context used for creation form prefilling
|
||||||
*
|
*
|
||||||
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Aform_prefill wiki tutorial
|
|
||||||
*
|
|
||||||
* @param array $aContextParam Context used for creation form prefilling. Contains those keys :
|
|
||||||
* <ul>
|
|
||||||
* <li>string 'dest_class'
|
|
||||||
* <li>string 'origin' either console or portal
|
|
||||||
* <li>DBObject 'source_obj' fixed only when creating an external key object
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @since 2.5.0 N°729
|
|
||||||
*/
|
*/
|
||||||
public function PrefillCreationForm(&$aContextParam)
|
public function PrefillCreationForm(&$aContextParam)
|
||||||
{
|
{
|
||||||
@@ -5347,40 +5183,24 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete an object after a state transition with data from context
|
* Complete an object after a state transition with data from context
|
||||||
*
|
|
||||||
* @overwritable-hook You can extend this method in order to provide your own logic.
|
|
||||||
*
|
*
|
||||||
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Aform_prefill wiki tutorial
|
* @internal
|
||||||
|
*
|
||||||
|
* @param array $aContextParam Context used for creation form prefilling
|
||||||
*
|
*
|
||||||
* @param array $aContextParam Context used for creation form prefilling. Contains those keys :
|
|
||||||
* <ul>
|
|
||||||
* <li>array 'expected_attributes' provides display flags on attributes in the form
|
|
||||||
* <li>string 'origin' either console or portal
|
|
||||||
* <li>string 'stimulus' provide the applied stimulus
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @since 2.5.0 N°729
|
|
||||||
*/
|
*/
|
||||||
public function PrefillTransitionForm(&$aContextParam)
|
public function PrefillTransitionForm(&$aContextParam)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete a filter data from context (Called on source object)
|
* Complete a filter ($aContextParam['filter']) data from context
|
||||||
|
* (Called on source object)
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @param array $aContextParam Context used for creation form prefilling
|
||||||
*
|
*
|
||||||
* @overwritable-hook You can extend this method in order to provide your own logic.
|
|
||||||
*
|
|
||||||
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Aform_prefill wiki tutorial
|
|
||||||
*
|
|
||||||
* @param array $aContextParam Context used for creation form prefilling. Contains :
|
|
||||||
* <ul>
|
|
||||||
* <li>string 'dest_class'
|
|
||||||
* <li>DBObjectSearch 'filter'
|
|
||||||
* <li>string 'user' login string of the connected user
|
|
||||||
* <li>string 'origin' either 'console' or 'portal'
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @since 2.5.0 N°729
|
|
||||||
*/
|
*/
|
||||||
public function PrefillSearchForm(&$aContextParam)
|
public function PrefillSearchForm(&$aContextParam)
|
||||||
{
|
{
|
||||||
@@ -5388,14 +5208,12 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefill a creation / stimulus change / search form according to context, current state of an object, stimulus.. $sOperation
|
* Prefill a creation / stimulus change / search form according to context, current state of an object, stimulus.. $sOperation
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
|
* @param string $sOperation Operation identifier
|
||||||
* @param array $aContextParam Context used for creation form prefilling
|
* @param array $aContextParam Context used for creation form prefilling
|
||||||
*
|
*
|
||||||
* @param string $sOperation Operation identifier
|
|
||||||
*
|
|
||||||
* @since 2.5.0 N°729
|
|
||||||
*/
|
*/
|
||||||
public function PrefillForm($sOperation, &$aContextParam)
|
public function PrefillForm($sOperation, &$aContextParam)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -622,7 +622,6 @@ class DBObjectSearch extends DBSearch
|
|||||||
{
|
{
|
||||||
if (!$oAttDef->IsScalar()) continue;
|
if (!$oAttDef->IsScalar()) continue;
|
||||||
if ($oAttDef->IsExternalKey()) continue;
|
if ($oAttDef->IsExternalKey()) continue;
|
||||||
if ($oAttDef instanceof AttributePassword) continue;
|
|
||||||
$aFullTextFields[] = new FieldExpression($sAttCode, $this->GetClassAlias());
|
$aFullTextFields[] = new FieldExpression($sAttCode, $this->GetClassAlias());
|
||||||
}
|
}
|
||||||
$oTextFields = new CharConcatWSExpression(' ', $aFullTextFields);
|
$oTextFields = new CharConcatWSExpression(' ', $aFullTextFields);
|
||||||
@@ -980,9 +979,6 @@ class DBObjectSearch extends DBSearch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritDoc
|
|
||||||
*/
|
|
||||||
public function Intersect(DBSearch $oFilter)
|
public function Intersect(DBSearch $oFilter)
|
||||||
{
|
{
|
||||||
if ($oFilter instanceof DBUnionSearch)
|
if ($oFilter instanceof DBUnionSearch)
|
||||||
@@ -1097,12 +1093,7 @@ class DBObjectSearch extends DBSearch
|
|||||||
if (!array_key_exists($sKeyAttCode, $this->m_aPointingTo)) return array();
|
if (!array_key_exists($sKeyAttCode, $this->m_aPointingTo)) return array();
|
||||||
return $this->m_aPointingTo[$sKeyAttCode];
|
return $this->m_aPointingTo[$sKeyAttCode];
|
||||||
}
|
}
|
||||||
|
protected function GetCriteria_ReferencedBy()
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function GetCriteria_ReferencedBy()
|
|
||||||
{
|
{
|
||||||
return $this->m_aReferencedBy;
|
return $this->m_aReferencedBy;
|
||||||
}
|
}
|
||||||
@@ -1534,42 +1525,36 @@ class DBObjectSearch extends DBSearch
|
|||||||
|
|
||||||
public function MakeDeleteQuery($aArgs = array())
|
public function MakeDeleteQuery($aArgs = array())
|
||||||
{
|
{
|
||||||
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this);
|
$aModifierProperties = MetaModel::MakeModifierProperties($this);
|
||||||
$oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectDeleteQuery();
|
$oBuild = new QueryBuilderContext($this, $aModifierProperties);
|
||||||
|
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, array($this->GetClassAlias() => array()), array());
|
||||||
|
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
|
||||||
|
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
|
||||||
|
$oSQLQuery->OptimizeJoins(array());
|
||||||
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
|
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
|
||||||
$sRet = $oSQLQuery->RenderDelete($aScalarArgs);
|
$sRet = $oSQLQuery->RenderDelete($aScalarArgs);
|
||||||
return $sRet;
|
return $sRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $aValues is an array of $sAttCode => $value
|
|
||||||
* @param array $aArgs
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function MakeUpdateQuery($aValues, $aArgs = array())
|
public function MakeUpdateQuery($aValues, $aArgs = array())
|
||||||
{
|
{
|
||||||
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($this);
|
// $aValues is an array of $sAttCode => $value
|
||||||
$oSQLQuery = $oSQLObjectQueryBuilder->MakeSQLObjectUpdateQuery($aValues);
|
$aModifierProperties = MetaModel::MakeModifierProperties($this);
|
||||||
|
$oBuild = new QueryBuilderContext($this, $aModifierProperties);
|
||||||
|
$aRequested = array(); // Requested attributes are the updated attributes
|
||||||
|
foreach ($aValues as $sAttCode => $value)
|
||||||
|
{
|
||||||
|
$aRequested[$sAttCode] = MetaModel::GetAttributeDef($this->GetClass(), $sAttCode);
|
||||||
|
}
|
||||||
|
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, array($this->GetClassAlias() => $aRequested), $aValues);
|
||||||
|
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
|
||||||
|
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
|
||||||
|
$oSQLQuery->OptimizeJoins(array());
|
||||||
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
|
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
|
||||||
$sRet = $oSQLQuery->RenderUpdate($aScalarArgs);
|
$sRet = $oSQLQuery->RenderUpdate($aScalarArgs);
|
||||||
return $sRet;
|
return $sRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an SQLObjectQuery from the search. This SQLObjectQuery can be rendered as a select, select group by, update or delete
|
|
||||||
*
|
|
||||||
* @param array $aAttToLoad array of 'attCode' => AttributeDefinition
|
|
||||||
* @param bool $bGetCount true for count requests
|
|
||||||
* @param null array $aGroupByExpr array of 'field name' => FieldOQLExpression
|
|
||||||
* @param null array $aSelectedClasses
|
|
||||||
* @param null array $aSelectExpr array of 'attCode' => Expression
|
|
||||||
*
|
|
||||||
* @return array|mixed|\SQLObjectQuery|null
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||||
{
|
{
|
||||||
// Hide objects that are not visible to the current user
|
// Hide objects that are not visible to the current user
|
||||||
@@ -1635,7 +1620,7 @@ class DBObjectSearch extends DBSearch
|
|||||||
}
|
}
|
||||||
$aContextData['sModifierProperties'] = $sModifierProperties;
|
$aContextData['sModifierProperties'] = $sModifierProperties;
|
||||||
|
|
||||||
$sRawId = Dict::GetUserLanguage().'-'.$sOqlQuery.$sModifierProperties;
|
$sRawId = $sOqlQuery.$sModifierProperties;
|
||||||
if (!is_null($aAttToLoad))
|
if (!is_null($aAttToLoad))
|
||||||
{
|
{
|
||||||
$sRawId .= json_encode($aAttToLoad);
|
$sRawId .= json_encode($aAttToLoad);
|
||||||
@@ -1721,8 +1706,7 @@ class DBObjectSearch extends DBSearch
|
|||||||
if (!isset($oSQLQuery))
|
if (!isset($oSQLQuery))
|
||||||
{
|
{
|
||||||
$oKPI = new ExecutionKPI();
|
$oKPI = new ExecutionKPI();
|
||||||
$oSQLObjectQueryBuilder = new SQLObjectQueryBuilder($oSearch);
|
$oSQLQuery = $oSearch->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
||||||
$oSQLQuery = $oSQLObjectQueryBuilder->BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
|
||||||
$oKPI->ComputeStats('BuildSQLQueryStruct', $sOqlQuery);
|
$oKPI->ComputeStats('BuildSQLQueryStruct', $sOqlQuery);
|
||||||
|
|
||||||
if (self::$m_bQueryCacheEnabled)
|
if (self::$m_bQueryCacheEnabled)
|
||||||
@@ -1741,6 +1725,649 @@ class DBObjectSearch extends DBSearch
|
|||||||
return $oSQLQuery;
|
return $oSQLQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $aAttToLoad
|
||||||
|
* @param bool $bGetCount
|
||||||
|
* @param array $aModifierProperties
|
||||||
|
* @param array $aGroupByExpr
|
||||||
|
* @param array $aSelectedClasses
|
||||||
|
* @param array $aSelectExpr
|
||||||
|
*
|
||||||
|
* @return null|SQLObjectQuery
|
||||||
|
* @throws \CoreException
|
||||||
|
*/
|
||||||
|
protected function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||||
|
{
|
||||||
|
$oBuild = new QueryBuilderContext($this, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
||||||
|
|
||||||
|
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, $aAttToLoad, array());
|
||||||
|
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
|
||||||
|
if (is_array($aGroupByExpr))
|
||||||
|
{
|
||||||
|
$aCols = $oBuild->m_oQBExpressions->GetGroupBy();
|
||||||
|
$oSQLQuery->SetGroupBy($aCols);
|
||||||
|
$oSQLQuery->SetSelect($aCols);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
|
||||||
|
}
|
||||||
|
if ($aSelectExpr)
|
||||||
|
{
|
||||||
|
// Get the fields corresponding to the select expressions
|
||||||
|
foreach($oBuild->m_oQBExpressions->GetSelect() as $sAlias => $oExpr)
|
||||||
|
{
|
||||||
|
if (key_exists($sAlias, $aSelectExpr))
|
||||||
|
{
|
||||||
|
$oSQLQuery->AddSelect($sAlias, $oExpr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$aMandatoryTables = null;
|
||||||
|
if (self::$m_bOptimizeQueries)
|
||||||
|
{
|
||||||
|
if ($bGetCount)
|
||||||
|
{
|
||||||
|
// Simplify the query if just getting the count
|
||||||
|
$oSQLQuery->SetSelect(array());
|
||||||
|
}
|
||||||
|
$oBuild->m_oQBExpressions->GetMandatoryTables($aMandatoryTables);
|
||||||
|
$oSQLQuery->OptimizeJoins($aMandatoryTables);
|
||||||
|
}
|
||||||
|
// Filter tables as late as possible: do not interfere with the optimization process
|
||||||
|
foreach ($oBuild->GetFilteredTables() as $sTableAlias => $aConditions)
|
||||||
|
{
|
||||||
|
if ($aMandatoryTables && array_key_exists($sTableAlias, $aMandatoryTables))
|
||||||
|
{
|
||||||
|
foreach ($aConditions as $oCondition)
|
||||||
|
{
|
||||||
|
$oSQLQuery->AddCondition($oCondition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oSQLQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $oBuild
|
||||||
|
* @param null $aAttToLoad
|
||||||
|
* @param array $aValues
|
||||||
|
* @return null|SQLObjectQuery
|
||||||
|
* @throws \CoreException
|
||||||
|
*/
|
||||||
|
protected function MakeSQLObjectQuery(&$oBuild, $aAttToLoad = null, $aValues = array())
|
||||||
|
{
|
||||||
|
// Note: query class might be different than the class of the filter
|
||||||
|
// -> this occurs when we are linking our class to an external class (referenced by, or pointing to)
|
||||||
|
$sClass = $this->GetFirstJoinedClass();
|
||||||
|
$sClassAlias = $this->GetFirstJoinedClassAlias();
|
||||||
|
|
||||||
|
$bIsOnQueriedClass = array_key_exists($sClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
|
||||||
|
|
||||||
|
//self::DbgTrace("Entering: ".$this->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
|
||||||
|
|
||||||
|
//$sRootClass = MetaModel::GetRootClass($sClass);
|
||||||
|
$sKeyField = MetaModel::DBGetKey($sClass);
|
||||||
|
|
||||||
|
if ($bIsOnQueriedClass)
|
||||||
|
{
|
||||||
|
// default to the whole list of attributes + the very std id/finalclass
|
||||||
|
$oBuild->m_oQBExpressions->AddSelect($sClassAlias.'id', new FieldExpression('id', $sClassAlias));
|
||||||
|
if (is_null($aAttToLoad) || !array_key_exists($sClassAlias, $aAttToLoad))
|
||||||
|
{
|
||||||
|
$sSelectedClass = $oBuild->GetSelectedClass($sClassAlias);
|
||||||
|
$aAttList = MetaModel::ListAttributeDefs($sSelectedClass);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$aAttList = $aAttToLoad[$sClassAlias];
|
||||||
|
}
|
||||||
|
foreach ($aAttList as $sAttCode => $oAttDef)
|
||||||
|
{
|
||||||
|
if (!$oAttDef->IsScalar()) continue;
|
||||||
|
// keep because it can be used for sorting - if (!$oAttDef->LoadInObject()) continue;
|
||||||
|
|
||||||
|
if ($oAttDef->IsBasedOnOQLExpression())
|
||||||
|
{
|
||||||
|
$oBuild->m_oQBExpressions->AddSelect($sClassAlias.$sAttCode, new FieldExpression($sAttCode, $sClassAlias));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
|
||||||
|
{
|
||||||
|
$oBuild->m_oQBExpressions->AddSelect($sClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
|
||||||
|
$aExpectedAtts = array(); // array of (attcode => fieldexpression)
|
||||||
|
//echo "<p>".__LINE__.": GetUnresolvedFields($sClassAlias, ...)</p>\n";
|
||||||
|
$oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias, $aExpectedAtts);
|
||||||
|
|
||||||
|
// Compute a clear view of required joins (from the current class)
|
||||||
|
// Build the list of external keys:
|
||||||
|
// -> ext keys required by an explicit join
|
||||||
|
// -> ext keys mentionned in a 'pointing to' condition
|
||||||
|
// -> ext keys required for an external field
|
||||||
|
// -> ext keys required for a friendly name
|
||||||
|
//
|
||||||
|
$aExtKeys = array(); // array of sTableClass => array of (sAttCode (keys) => array of (sAttCode (fields)=> oAttDef))
|
||||||
|
//
|
||||||
|
// Optimization: could be partially computed once for all (cached) ?
|
||||||
|
//
|
||||||
|
|
||||||
|
if ($bIsOnQueriedClass)
|
||||||
|
{
|
||||||
|
// Get all Ext keys for the queried class (??)
|
||||||
|
foreach(MetaModel::GetKeysList($sClass) as $sKeyAttCode)
|
||||||
|
{
|
||||||
|
$sKeyTableClass = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode);
|
||||||
|
$aExtKeys[$sKeyTableClass][$sKeyAttCode] = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get all Ext keys used by the filter
|
||||||
|
foreach ($this->GetCriteria_PointingTo() as $sKeyAttCode => $aPointingTo)
|
||||||
|
{
|
||||||
|
if (array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo))
|
||||||
|
{
|
||||||
|
$sKeyTableClass = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode);
|
||||||
|
$aExtKeys[$sKeyTableClass][$sKeyAttCode] = array();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$aFNJoinAlias = array(); // array of (subclass => alias)
|
||||||
|
foreach ($aExpectedAtts as $sExpectedAttCode => $oExpression)
|
||||||
|
{
|
||||||
|
if (!MetaModel::IsValidAttCode($sClass, $sExpectedAttCode)) continue;
|
||||||
|
$oAttDef = MetaModel::GetAttributeDef($sClass, $sExpectedAttCode);
|
||||||
|
if ($oAttDef->IsBasedOnOQLExpression())
|
||||||
|
{
|
||||||
|
// To optimize: detect a restriction on child classes in the condition expression
|
||||||
|
// e.g. SELECT FunctionalCI WHERE finalclass IN ('Server', 'VirtualMachine')
|
||||||
|
$oExpression = static::GetPolymorphicExpression($sClass, $sExpectedAttCode);
|
||||||
|
|
||||||
|
$aRequiredFields = array();
|
||||||
|
$oExpression->GetUnresolvedFields('', $aRequiredFields);
|
||||||
|
$aTranslateFields = array();
|
||||||
|
foreach($aRequiredFields as $sSubClass => $aFields)
|
||||||
|
{
|
||||||
|
foreach($aFields as $sAttCode => $oField)
|
||||||
|
{
|
||||||
|
$oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode);
|
||||||
|
if ($oAttDef->IsExternalKey())
|
||||||
|
{
|
||||||
|
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
|
||||||
|
$aExtKeys[$sClassOfAttribute][$sAttCode] = array();
|
||||||
|
}
|
||||||
|
elseif ($oAttDef->IsExternalField())
|
||||||
|
{
|
||||||
|
$sKeyAttCode = $oAttDef->GetKeyAttCode();
|
||||||
|
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode);
|
||||||
|
$aExtKeys[$sClassOfAttribute][$sKeyAttCode][$sAttCode] = $oAttDef;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MetaModel::IsParentClass($sClassOfAttribute, $sClass))
|
||||||
|
{
|
||||||
|
// The attribute is part of the standard query
|
||||||
|
//
|
||||||
|
$sAliasForAttribute = $sClassAlias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// The attribute will be available from an additional outer join
|
||||||
|
// For each subclass (table) one single join is enough
|
||||||
|
//
|
||||||
|
if (!array_key_exists($sClassOfAttribute, $aFNJoinAlias))
|
||||||
|
{
|
||||||
|
$sAliasForAttribute = $oBuild->GenerateClassAlias($sClassAlias.'_fn_'.$sClassOfAttribute, $sClassOfAttribute);
|
||||||
|
$aFNJoinAlias[$sClassOfAttribute] = $sAliasForAttribute;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sAliasForAttribute = $aFNJoinAlias[$sClassOfAttribute];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$aTranslateFields[$sSubClass][$sAttCode] = new FieldExpression($sAttCode, $sAliasForAttribute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$oExpression = $oExpression->Translate($aTranslateFields, false);
|
||||||
|
|
||||||
|
$aTranslateNow = array();
|
||||||
|
$aTranslateNow[$sClassAlias][$sExpectedAttCode] = $oExpression;
|
||||||
|
$oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the ext fields used in the select (eventually adds an external key)
|
||||||
|
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
|
||||||
|
{
|
||||||
|
if ($oAttDef->IsExternalField())
|
||||||
|
{
|
||||||
|
if (array_key_exists($sAttCode, $aExpectedAtts))
|
||||||
|
{
|
||||||
|
// Add the external attribute
|
||||||
|
$sKeyAttCode = $oAttDef->GetKeyAttCode();
|
||||||
|
$sKeyTableClass = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode);
|
||||||
|
$aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$bRootFirst = MetaModel::GetConfig()->Get('optimize_requests_for_join_count');
|
||||||
|
if ($bRootFirst)
|
||||||
|
{
|
||||||
|
// First query built from the root, adding all tables including the leaf
|
||||||
|
// Before N.1065 we were joining from the leaf first, but this wasn't a good choice :
|
||||||
|
// most of the time (obsolescence, friendlyname, ...) we want to get a root attribute !
|
||||||
|
//
|
||||||
|
$oSelectBase = null;
|
||||||
|
$aClassHierarchy = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, true);
|
||||||
|
$bIsClassStandaloneClass = (count($aClassHierarchy) == 1);
|
||||||
|
foreach($aClassHierarchy as $sSomeClass)
|
||||||
|
{
|
||||||
|
if (!MetaModel::HasTable($sSomeClass))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::DbgTrace("Adding join from root to leaf: $sSomeClass... let's call MakeSQLObjectQuerySingleTable()");
|
||||||
|
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSomeClass, $aExtKeys, $aValues);
|
||||||
|
if (is_null($oSelectBase))
|
||||||
|
{
|
||||||
|
$oSelectBase = $oSelectParentTable;
|
||||||
|
if (!$bIsClassStandaloneClass && (MetaModel::IsRootClass($sSomeClass)))
|
||||||
|
{
|
||||||
|
// As we're linking to root class first, we're adding a where clause on the finalClass attribute :
|
||||||
|
// COALESCE($sRootClassFinalClass IN ('$sExpectedClasses'), 1)
|
||||||
|
// If we don't, the child classes can be removed in the query optimisation phase, including the leaf that was queried
|
||||||
|
// So we still need to filter records to only those corresponding to the child classes !
|
||||||
|
// The coalesce is mandatory if we have a polymorphic query (left join)
|
||||||
|
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
|
||||||
|
$sFinalClassSqlColumnName = MetaModel::DBGetClassField($sSomeClass);
|
||||||
|
$oClassExpr = new FieldExpression($sFinalClassSqlColumnName, $oSelectBase->GetTableAlias());
|
||||||
|
$oInExpression = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
|
||||||
|
$oTrueExpression = new TrueExpression();
|
||||||
|
$aCoalesceAttr = array($oInExpression, $oTrueExpression);
|
||||||
|
$oFinalClassRestriction = new FunctionExpression("COALESCE", $aCoalesceAttr);
|
||||||
|
|
||||||
|
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sSomeClass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// First query built upon on the leaf (ie current) class
|
||||||
|
//
|
||||||
|
self::DbgTrace("Main (=leaf) class, call MakeSQLObjectQuerySingleTable()");
|
||||||
|
if (MetaModel::HasTable($sClass))
|
||||||
|
{
|
||||||
|
$oSelectBase = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sClass, $aExtKeys, $aValues);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$oSelectBase = null;
|
||||||
|
|
||||||
|
// As the join will not filter on the expected classes, we have to specify it explicitely
|
||||||
|
$sExpectedClasses = implode("', '", MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
|
||||||
|
$oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')");
|
||||||
|
$oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then we join the queries of the eventual parent classes (compound model)
|
||||||
|
foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass)
|
||||||
|
{
|
||||||
|
if (!MetaModel::HasTable($sParentClass)) continue;
|
||||||
|
|
||||||
|
self::DbgTrace("Parent class: $sParentClass... let's call MakeSQLObjectQuerySingleTable()");
|
||||||
|
$oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sParentClass, $aExtKeys, $aValues);
|
||||||
|
if (is_null($oSelectBase))
|
||||||
|
{
|
||||||
|
$oSelectBase = $oSelectParentTable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sParentClass));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter on objects referencing me
|
||||||
|
//
|
||||||
|
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||||
|
{
|
||||||
|
foreach($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
|
||||||
|
{
|
||||||
|
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
|
||||||
|
{
|
||||||
|
foreach ($aFilters as $oForeignFilter)
|
||||||
|
{
|
||||||
|
$oForeignKeyAttDef = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
||||||
|
|
||||||
|
self::DbgTrace("Referenced by foreign key: $sForeignExtKeyAttCode... let's call MakeSQLObjectQuery()");
|
||||||
|
//self::DbgTrace($oForeignFilter);
|
||||||
|
//self::DbgTrace($oForeignFilter->ToOQL());
|
||||||
|
//self::DbgTrace($oSelectForeign);
|
||||||
|
//self::DbgTrace($oSelectForeign->RenderSelect(array()));
|
||||||
|
|
||||||
|
$sForeignClassAlias = $oForeignFilter->GetFirstJoinedClassAlias();
|
||||||
|
$oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sForeignExtKeyAttCode, $sForeignClassAlias));
|
||||||
|
|
||||||
|
if ($oForeignKeyAttDef instanceof AttributeObjectKey)
|
||||||
|
{
|
||||||
|
$sClassAttCode = $oForeignKeyAttDef->Get('class_attcode');
|
||||||
|
|
||||||
|
// Add the condition: `$sForeignClassAlias`.$sClassAttCode IN (subclasses of $sClass')
|
||||||
|
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
|
||||||
|
$oClassExpr = new FieldExpression($sClassAttCode, $sForeignClassAlias);
|
||||||
|
$oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
|
||||||
|
$oBuild->m_oQBExpressions->AddCondition($oClassRestriction);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oSelectForeign = $oForeignFilter->MakeSQLObjectQuery($oBuild, $aAttToLoad);
|
||||||
|
|
||||||
|
$oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
|
||||||
|
$sForeignKeyTable = $oJoinExpr->GetParent();
|
||||||
|
$sForeignKeyColumn = $oJoinExpr->GetName();
|
||||||
|
|
||||||
|
if ($iOperatorCode == TREE_OPERATOR_EQUALS)
|
||||||
|
{
|
||||||
|
$oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Hierarchical key
|
||||||
|
$KeyLeft = $oForeignKeyAttDef->GetSQLLeft();
|
||||||
|
$KeyRight = $oForeignKeyAttDef->GetSQLRight();
|
||||||
|
|
||||||
|
$oSelectBase->AddInnerJoinTree($oSelectForeign, $KeyLeft, $KeyRight, $KeyLeft, $KeyRight, $sForeignKeyTable, $iOperatorCode, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional JOINS for Friendly names
|
||||||
|
//
|
||||||
|
foreach ($aFNJoinAlias as $sSubClass => $sSubClassAlias)
|
||||||
|
{
|
||||||
|
$oSubClassFilter = new DBObjectSearch($sSubClass, $sSubClassAlias);
|
||||||
|
$oSelectFN = $oSubClassFilter->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSubClass, $aExtKeys, array());
|
||||||
|
$oSelectBase->AddLeftJoin($oSelectFN, $sKeyField, MetaModel::DBGetKey($sSubClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
// That's all... cross fingers and we'll get some working query
|
||||||
|
|
||||||
|
//MyHelpers::var_dump_html($oSelectBase, true);
|
||||||
|
//MyHelpers::var_dump_html($oSelectBase->RenderSelect(), true);
|
||||||
|
if (self::$m_bDebugQuery) $oSelectBase->DisplayHtml();
|
||||||
|
return $oSelectBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function MakeSQLObjectQuerySingleTable(&$oBuild, $aAttToLoad, $sTableClass, $aExtKeys, $aValues)
|
||||||
|
{
|
||||||
|
// $aExtKeys is an array of sTableClass => array of (sAttCode (keys) => array of sAttCode (fields))
|
||||||
|
|
||||||
|
// Prepare the query for a single table (compound objects)
|
||||||
|
// Ignores the items (attributes/filters) that are not on the target table
|
||||||
|
// Perform an (inner or left) join for every external key (and specify the expected fields)
|
||||||
|
//
|
||||||
|
// Returns an SQLQuery
|
||||||
|
//
|
||||||
|
$sTargetClass = $this->GetFirstJoinedClass();
|
||||||
|
$sTargetAlias = $this->GetFirstJoinedClassAlias();
|
||||||
|
$sTable = MetaModel::DBGetTable($sTableClass);
|
||||||
|
$sTableAlias = $oBuild->GenerateTableAlias($sTargetAlias.'_'.$sTable, $sTable);
|
||||||
|
|
||||||
|
$aTranslation = array();
|
||||||
|
$aExpectedAtts = array();
|
||||||
|
$oBuild->m_oQBExpressions->GetUnresolvedFields($sTargetAlias, $aExpectedAtts);
|
||||||
|
|
||||||
|
$bIsOnQueriedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
|
||||||
|
|
||||||
|
self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$this->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
|
||||||
|
|
||||||
|
// 1 - SELECT and UPDATE
|
||||||
|
//
|
||||||
|
// Note: no need for any values nor fields for foreign Classes (ie not the queried Class)
|
||||||
|
//
|
||||||
|
$aUpdateValues = array();
|
||||||
|
|
||||||
|
|
||||||
|
// 1/a - Get the key and friendly name
|
||||||
|
//
|
||||||
|
// We need one pkey to be the key, let's take the first one available
|
||||||
|
$oSelectedIdField = null;
|
||||||
|
$oIdField = new FieldExpressionResolved(MetaModel::DBGetKey($sTableClass), $sTableAlias);
|
||||||
|
$aTranslation[$sTargetAlias]['id'] = $oIdField;
|
||||||
|
|
||||||
|
if ($bIsOnQueriedClass)
|
||||||
|
{
|
||||||
|
// Add this field to the list of queried fields (required for the COUNT to work fine)
|
||||||
|
$oSelectedIdField = $oIdField;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1/b - Get the other attributes
|
||||||
|
//
|
||||||
|
foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
|
||||||
|
{
|
||||||
|
// Skip this attribute if not defined in this table
|
||||||
|
if (MetaModel::GetAttributeOrigin($sTargetClass, $sAttCode) != $sTableClass) continue;
|
||||||
|
|
||||||
|
// Skip this attribute if not made of SQL columns
|
||||||
|
if (count($oAttDef->GetSQLExpressions()) == 0) continue;
|
||||||
|
|
||||||
|
// Update...
|
||||||
|
//
|
||||||
|
if ($bIsOnQueriedClass && array_key_exists($sAttCode, $aValues))
|
||||||
|
{
|
||||||
|
assert ($oAttDef->IsBasedOnDBColumns());
|
||||||
|
foreach ($oAttDef->GetSQLValues($aValues[$sAttCode]) as $sColumn => $sValue)
|
||||||
|
{
|
||||||
|
$aUpdateValues[$sColumn] = $sValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 - The SQL query, for this table only
|
||||||
|
//
|
||||||
|
$oSelectBase = new SQLObjectQuery($sTable, $sTableAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField);
|
||||||
|
|
||||||
|
// 3 - Resolve expected expressions (translation table: alias.attcode => table.column)
|
||||||
|
//
|
||||||
|
foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
|
||||||
|
{
|
||||||
|
// Skip this attribute if not defined in this table
|
||||||
|
if (MetaModel::GetAttributeOrigin($sTargetClass, $sAttCode) != $sTableClass) continue;
|
||||||
|
|
||||||
|
// Select...
|
||||||
|
//
|
||||||
|
if ($oAttDef->IsExternalField())
|
||||||
|
{
|
||||||
|
// skip, this will be handled in the joined tables (done hereabove)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// standard field, or external key
|
||||||
|
// add it to the output
|
||||||
|
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
|
||||||
|
{
|
||||||
|
if (array_key_exists($sAttCode.$sColId, $aExpectedAtts))
|
||||||
|
{
|
||||||
|
$oFieldSQLExp = new FieldExpressionResolved($sSQLExpr, $sTableAlias);
|
||||||
|
foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
|
||||||
|
{
|
||||||
|
$oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sTargetClass, $sAttCode, $sColId, $oFieldSQLExp, $oSelectBase);
|
||||||
|
}
|
||||||
|
$aTranslation[$sTargetAlias][$sAttCode.$sColId] = $oFieldSQLExp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4 - The external keys -> joins...
|
||||||
|
//
|
||||||
|
$aAllPointingTo = $this->GetCriteria_PointingTo();
|
||||||
|
|
||||||
|
if (array_key_exists($sTableClass, $aExtKeys))
|
||||||
|
{
|
||||||
|
foreach ($aExtKeys[$sTableClass] as $sKeyAttCode => $aExtFields)
|
||||||
|
{
|
||||||
|
$oKeyAttDef = MetaModel::GetAttributeDef($sTableClass, $sKeyAttCode);
|
||||||
|
|
||||||
|
$aPointingTo = $this->GetCriteria_PointingTo($sKeyAttCode);
|
||||||
|
if (!array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo))
|
||||||
|
{
|
||||||
|
// The join was not explicitely defined in the filter,
|
||||||
|
// we need to do it now
|
||||||
|
$sKeyClass = $oKeyAttDef->GetTargetClass();
|
||||||
|
$sKeyClassAlias = $oBuild->GenerateClassAlias($sKeyClass.'_'.$sKeyAttCode, $sKeyClass);
|
||||||
|
$oExtFilter = new DBObjectSearch($sKeyClass, $sKeyClassAlias);
|
||||||
|
|
||||||
|
$aAllPointingTo[$sKeyAttCode][TREE_OPERATOR_EQUALS][$sKeyClassAlias] = $oExtFilter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($aAllPointingTo as $sKeyAttCode => $aPointingTo)
|
||||||
|
{
|
||||||
|
foreach($aPointingTo as $iOperatorCode => $aFilter)
|
||||||
|
{
|
||||||
|
foreach($aFilter as $oExtFilter)
|
||||||
|
{
|
||||||
|
if (!MetaModel::IsValidAttCode($sTableClass, $sKeyAttCode)) continue; // Not defined in the class, skip it
|
||||||
|
// The aliases should not conflict because normalization occured while building the filter
|
||||||
|
$oKeyAttDef = MetaModel::GetAttributeDef($sTableClass, $sKeyAttCode);
|
||||||
|
$sKeyClass = $oExtFilter->GetFirstJoinedClass();
|
||||||
|
$sKeyClassAlias = $oExtFilter->GetFirstJoinedClassAlias();
|
||||||
|
|
||||||
|
// Note: there is no search condition in $oExtFilter, because normalization did merge the condition onto the top of the filter tree
|
||||||
|
|
||||||
|
if ($iOperatorCode == TREE_OPERATOR_EQUALS)
|
||||||
|
{
|
||||||
|
if (array_key_exists($sTableClass, $aExtKeys) && array_key_exists($sKeyAttCode, $aExtKeys[$sTableClass]))
|
||||||
|
{
|
||||||
|
// Specify expected attributes for the target class query
|
||||||
|
// ... and use the current alias !
|
||||||
|
$aTranslateNow = array(); // Translation for external fields - must be performed before the join is done (recursion...)
|
||||||
|
foreach($aExtKeys[$sTableClass][$sKeyAttCode] as $sAttCode => $oAtt)
|
||||||
|
{
|
||||||
|
$oExtAttDef = $oAtt->GetExtAttDef();
|
||||||
|
if ($oExtAttDef->IsBasedOnOQLExpression())
|
||||||
|
{
|
||||||
|
$aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression($oExtAttDef->GetCode(), $sKeyClassAlias);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sExtAttCode = $oAtt->GetExtAttCode();
|
||||||
|
// Translate mainclass.extfield => remoteclassalias.remotefieldcode
|
||||||
|
$oRemoteAttDef = MetaModel::GetAttributeDef($sKeyClass, $sExtAttCode);
|
||||||
|
foreach ($oRemoteAttDef->GetSQLExpressions() as $sColId => $sRemoteAttExpr)
|
||||||
|
{
|
||||||
|
$aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($oKeyAttDef instanceof AttributeObjectKey)
|
||||||
|
{
|
||||||
|
// Add the condition: `$sTargetAlias`.$sClassAttCode IN (subclasses of $sKeyClass')
|
||||||
|
$sClassAttCode = $oKeyAttDef->Get('class_attcode');
|
||||||
|
$oClassAttDef = MetaModel::GetAttributeDef($sTargetClass, $sClassAttCode);
|
||||||
|
foreach ($oClassAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
|
||||||
|
{
|
||||||
|
$aTranslateNow[$sTargetAlias][$sClassAttCode.$sColId] = new FieldExpressionResolved($sSQLExpr, $sTableAlias);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sKeyClass, ENUM_CHILD_CLASSES_ALL));
|
||||||
|
$oClassExpr = new FieldExpression($sClassAttCode, $sTargetAlias);
|
||||||
|
$oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
|
||||||
|
$oBuild->m_oQBExpressions->AddCondition($oClassRestriction);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate prior to recursing
|
||||||
|
//
|
||||||
|
$oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
|
||||||
|
|
||||||
|
self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeSQLObjectQuery()");
|
||||||
|
$oBuild->m_oQBExpressions->PushJoinField(new FieldExpression('id', $sKeyClassAlias));
|
||||||
|
|
||||||
|
$oSelectExtKey = $oExtFilter->MakeSQLObjectQuery($oBuild, $aAttToLoad);
|
||||||
|
|
||||||
|
$oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
|
||||||
|
$sExternalKeyTable = $oJoinExpr->GetParent();
|
||||||
|
$sExternalKeyField = $oJoinExpr->GetName();
|
||||||
|
|
||||||
|
$aCols = $oKeyAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc())
|
||||||
|
$sLocalKeyField = current($aCols); // get the first column for an external key
|
||||||
|
|
||||||
|
self::DbgTrace("External key $sKeyAttCode, Join on $sLocalKeyField = $sExternalKeyField");
|
||||||
|
if ($oKeyAttDef->IsNullAllowed())
|
||||||
|
{
|
||||||
|
$oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$oSelectBase->AddInnerJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif(MetaModel::GetAttributeOrigin($sKeyClass, $sKeyAttCode) == $sTableClass)
|
||||||
|
{
|
||||||
|
$oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sKeyAttCode, $sKeyClassAlias));
|
||||||
|
$oSelectExtKey = $oExtFilter->MakeSQLObjectQuery($oBuild, $aAttToLoad);
|
||||||
|
$oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
|
||||||
|
$sExternalKeyTable = $oJoinExpr->GetParent();
|
||||||
|
$sExternalKeyField = $oJoinExpr->GetName();
|
||||||
|
$sLeftIndex = $sExternalKeyField.'_left'; // TODO use GetSQLLeft()
|
||||||
|
$sRightIndex = $sExternalKeyField.'_right'; // TODO use GetSQLRight()
|
||||||
|
|
||||||
|
$LocalKeyLeft = $oKeyAttDef->GetSQLLeft();
|
||||||
|
$LocalKeyRight = $oKeyAttDef->GetSQLRight();
|
||||||
|
|
||||||
|
$oSelectBase->AddInnerJoinTree($oSelectExtKey, $LocalKeyLeft, $LocalKeyRight, $sLeftIndex, $sRightIndex, $sExternalKeyTable, $iOperatorCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate the selected columns
|
||||||
|
//
|
||||||
|
$oBuild->m_oQBExpressions->Translate($aTranslation, false);
|
||||||
|
|
||||||
|
// Filter out archived records
|
||||||
|
//
|
||||||
|
if (MetaModel::IsArchivable($sTableClass))
|
||||||
|
{
|
||||||
|
if (!$oBuild->GetRootFilter()->GetArchiveMode())
|
||||||
|
{
|
||||||
|
$bIsOnJoinedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetJoinedClasses());
|
||||||
|
if ($bIsOnJoinedClass)
|
||||||
|
{
|
||||||
|
if (MetaModel::IsParentClass($sTableClass, $sTargetClass))
|
||||||
|
{
|
||||||
|
$oNotArchived = new BinaryExpression(new FieldExpressionResolved('archive_flag', $sTableAlias), '=', new ScalarExpression(0));
|
||||||
|
$oBuild->AddFilteredTable($sTableAlias, $oNotArchived);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $oSelectBase;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the expression for the class and its subclasses (if finalclass = 'subclass' ...)
|
* Get the expression for the class and its subclasses (if finalclass = 'subclass' ...)
|
||||||
* Simplifies the final expression by grouping classes having the same expression
|
* Simplifies the final expression by grouping classes having the same expression
|
||||||
|
|||||||
@@ -17,22 +17,8 @@
|
|||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
|
||||||
$bUseLegacyDBSearch = utils::GetConfig()->Get('use_legacy_dbsearch');
|
require_once('dbobjectsearch.class.php');
|
||||||
|
require_once('dbunionsearch.class.php');
|
||||||
if ($bUseLegacyDBSearch)
|
|
||||||
{
|
|
||||||
// excluded from autoload
|
|
||||||
require_once (APPROOT.'core/legacy/querybuilderexpressionslegacy.class.inc.php');
|
|
||||||
require_once (APPROOT.'core/legacy/querybuildercontextlegacy.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/legacy/dbobjectsearchlegacy.class.php');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// excluded from autoload
|
|
||||||
require_once (APPROOT.'core/querybuilderexpressions.class.inc.php');
|
|
||||||
require_once (APPROOT.'core/querybuildercontext.class.inc.php');
|
|
||||||
require_once(APPROOT.'core/dbobjectsearch.class.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object search
|
* An object search
|
||||||
@@ -951,7 +937,7 @@ abstract class DBSearch
|
|||||||
$e->addInfo('OQL', $this->ToOQL());
|
$e->addInfo('OQL', $this->ToOQL());
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
$this->AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $bExcludeNullValues, $aSelectExpr, $aOrderBy, $iLimitCount, $iLimitStart, $sRes);
|
$this->AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $sRes);
|
||||||
return $sRes;
|
return $sRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -961,7 +947,7 @@ abstract class DBSearch
|
|||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
* @param array|hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||||
* @param array $aArgs
|
* @param array $aArgs
|
||||||
* @param null $aAttToLoad
|
* @param null $aAttToLoad
|
||||||
* @param null $aExtendedDataSpec
|
* @param null $aExtendedDataSpec
|
||||||
@@ -1038,8 +1024,8 @@ abstract class DBSearch
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// $bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
|
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
|
||||||
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, true);
|
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
|
||||||
if ($sClassAlias == '_itop_')
|
if ($sClassAlias == '_itop_')
|
||||||
{
|
{
|
||||||
IssueLog::Info('SQL Query (_itop_): '.$sRes);
|
IssueLog::Info('SQL Query (_itop_): '.$sRes);
|
||||||
@@ -1051,7 +1037,7 @@ abstract class DBSearch
|
|||||||
$e->addInfo('OQL', $this->ToOQL());
|
$e->addInfo('OQL', $this->ToOQL());
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
$this->AddQueryTraceSelect($oSQLQuery->GetSourceOQL(), $aOrderBy, $aScalarArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sRes);
|
$this->AddQueryTraceSelect($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sRes);
|
||||||
return $sRes;
|
return $sRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1087,21 +1073,18 @@ abstract class DBSearch
|
|||||||
$oSearch = $this;
|
$oSearch = $this;
|
||||||
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered())
|
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered())
|
||||||
{
|
{
|
||||||
foreach ($this->GetSelectedClasses() as $sClass)
|
$oVisibleObjects = UserRights::GetSelectFilter($this->GetClass(), $this->GetModifierProperties('UserRightsGetSelectFilter'));
|
||||||
|
if ($oVisibleObjects === false)
|
||||||
{
|
{
|
||||||
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
|
// Make sure this is a valid search object, saying NO for all
|
||||||
if ($oVisibleObjects === false)
|
$oVisibleObjects = DBObjectSearch::FromEmptySet($this->GetClass());
|
||||||
{
|
}
|
||||||
// Make sure this is a valid search object, saying NO for all
|
if (is_object($oVisibleObjects))
|
||||||
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
|
{
|
||||||
}
|
$oVisibleObjects->AllowAllData();
|
||||||
if (is_object($oVisibleObjects))
|
$oSearch = $this->Intersect($oVisibleObjects);
|
||||||
{
|
/** @var DBSearch $oSearch */
|
||||||
$oVisibleObjects->AllowAllData();
|
$oSearch->SetDataFiltered();
|
||||||
$oSearch = $this->Intersect($oVisibleObjects);
|
|
||||||
/** @var DBSearch $oSearch */
|
|
||||||
$oSearch->SetDataFiltered();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$oSQLQuery = $oSearch->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, null, $aSelectExpr);
|
$oSQLQuery = $oSearch->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, null, $aSelectExpr);
|
||||||
@@ -1243,112 +1226,61 @@ abstract class DBSearch
|
|||||||
self::$m_bOptimizeQueries = $bEnabled;
|
self::$m_bOptimizeQueries = $bEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $sOql
|
* @internal
|
||||||
* @param $aOrderBy
|
*
|
||||||
* @param $aArgs
|
* @param $aOrderBy
|
||||||
* @param $aAttToLoad
|
* @param $aArgs
|
||||||
* @param $aExtendedDataSpec
|
* @param $aAttToLoad
|
||||||
* @param $iLimitCount
|
* @param $aExtendedDataSpec
|
||||||
* @param $iLimitStart
|
* @param $iLimitCount
|
||||||
* @param $bGetCount
|
* @param $iLimitStart
|
||||||
* @param $sSql
|
* @param $bGetCount
|
||||||
*
|
* @param $sSql
|
||||||
* @throws \ConfigException
|
*
|
||||||
* @throws \CoreException
|
* @throws MySQLException
|
||||||
* @internal
|
*/
|
||||||
*
|
protected function AddQueryTraceSelect($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sSql)
|
||||||
*/
|
|
||||||
protected function AddQueryTraceSelect($sOql, $aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sSql)
|
|
||||||
{
|
{
|
||||||
if (self::$m_bTraceQueries)
|
if (self::$m_bTraceQueries)
|
||||||
{
|
{
|
||||||
$aQueryData = array(
|
$aQueryData = array(
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
|
'filter' => $this,
|
||||||
'order_by' => $aOrderBy,
|
'order_by' => $aOrderBy,
|
||||||
|
'args' => $aArgs,
|
||||||
'att_to_load' => $aAttToLoad,
|
'att_to_load' => $aAttToLoad,
|
||||||
|
'extended_data_spec' => $aExtendedDataSpec,
|
||||||
'limit_count' => $iLimitCount,
|
'limit_count' => $iLimitCount,
|
||||||
'limit_start' => $iLimitStart,
|
'limit_start' => $iLimitStart,
|
||||||
'is_count' => $bGetCount
|
'is_count' => $bGetCount
|
||||||
);
|
);
|
||||||
|
$sOql = $this->ToOQL(true, $aArgs);
|
||||||
DBSearch::EnableQueryTrace(false);
|
self::AddQueryTrace($aQueryData, $sOql, $sSql);
|
||||||
$aQueryData['oql'] = $this->ToOQL(true, $aArgs);
|
|
||||||
DBSearch::EnableQueryTrace(true);
|
|
||||||
|
|
||||||
if (!empty($aAttToLoad))
|
|
||||||
{
|
|
||||||
$aAttToLoadNames = array();
|
|
||||||
foreach ($aAttToLoad as $sClass => $aAttributes)
|
|
||||||
{
|
|
||||||
$aAttToLoadNames[$sClass] = array();
|
|
||||||
foreach ($aAttributes as $sAttCode => $oAttDef)
|
|
||||||
{
|
|
||||||
$aAttToLoadNames[$sClass][] = $sAttCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aAttToLoadNames = null;
|
|
||||||
}
|
|
||||||
$aQueryData['att_to_load'] = $aAttToLoadNames;
|
|
||||||
|
|
||||||
$hLogFile = @fopen(APPROOT.'log/oql_records.txt', 'a');
|
|
||||||
if ($hLogFile !== false)
|
|
||||||
{
|
|
||||||
flock($hLogFile,LOCK_EX);
|
|
||||||
fwrite($hLogFile,serialize($aQueryData)."\n");
|
|
||||||
fflush($hLogFile);
|
|
||||||
flock($hLogFile,LOCK_UN);
|
|
||||||
fclose($hLogFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $aArgs
|
* @internal
|
||||||
* @param $aGroupByExpr
|
*
|
||||||
* @param $bExcludeNullValues
|
* @param $aArgs
|
||||||
* @param $aSelectExpr
|
* @param $aGroupByExpr
|
||||||
* @param $aOrderBy
|
* @param $sSql
|
||||||
* @param $iLimitCount
|
*
|
||||||
* @param $iLimitStart
|
* @throws MySQLException
|
||||||
* @param $sSql
|
*/
|
||||||
*
|
protected function AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $sSql)
|
||||||
* @throws \ConfigException
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @internal
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $bExcludeNullValues, $aSelectExpr, $aOrderBy, $iLimitCount, $iLimitStart, $sSql)
|
|
||||||
{
|
{
|
||||||
if (self::$m_bTraceQueries)
|
if (self::$m_bTraceQueries)
|
||||||
{
|
{
|
||||||
$aQueryData = array(
|
$aQueryData = array(
|
||||||
'type' => 'group_by',
|
'type' => 'group_by',
|
||||||
'order_by' => $aOrderBy,
|
'filter' => $this,
|
||||||
'group_by_expr' => $aGroupByExpr,
|
'args' => $aArgs,
|
||||||
'exclude_null_values' => $bExcludeNullValues,
|
'group_by_expr' => $aGroupByExpr
|
||||||
'select_expr' => $aSelectExpr,
|
|
||||||
'limit_count' => $iLimitCount,
|
|
||||||
'limit_start' => $iLimitStart,
|
|
||||||
);
|
);
|
||||||
|
$sOql = $this->ToOQL(true, $aArgs);
|
||||||
$aQueryData['oql'] = $this->ToOQL(true, $aArgs);
|
self::AddQueryTrace($aQueryData, $sOql, $sSql);
|
||||||
$aQueryData['group_by_expr'] = Expression::ConvertArrayToOQL($aQueryData['group_by_expr'], $aArgs);
|
|
||||||
$aQueryData['select_expr'] = Expression::ConvertArrayToOQL($aQueryData['select_expr'], $aArgs);
|
|
||||||
|
|
||||||
$hLogFile = @fopen(APPROOT.'log/oql_group_by_records.txt', 'a');
|
|
||||||
if ($hLogFile !== false)
|
|
||||||
{
|
|
||||||
flock($hLogFile,LOCK_EX);
|
|
||||||
fwrite($hLogFile,serialize($aQueryData)."\n");
|
|
||||||
fflush($hLogFile);
|
|
||||||
flock($hLogFile,LOCK_UN);
|
|
||||||
fclose($hLogFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1492,16 +1424,15 @@ abstract class DBSearch
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates archive_flag and archive_date fields in the whole class hierarchy
|
* Experimental!
|
||||||
*
|
|
||||||
* @see \DBObject::DBWriteArchiveFlag()
|
|
||||||
*
|
|
||||||
* @param boolean $bArchive
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
* @todo implement the change tracking
|
* @todo implement the change tracking
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*
|
||||||
|
* @param $bArchive
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function DBBulkWriteArchiveFlag($bArchive)
|
function DBBulkWriteArchiveFlag($bArchive)
|
||||||
{
|
{
|
||||||
$sClass = $this->GetClass();
|
$sClass = $this->GetClass();
|
||||||
if (!MetaModel::IsArchivable($sClass))
|
if (!MetaModel::IsArchivable($sClass))
|
||||||
|
|||||||
@@ -455,9 +455,8 @@ class DBUnionSearch extends DBSearch
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new DBUnionSearch object where duplicates queries have been removed based on their OQLs
|
* Returns a new DBUnionSearch object where duplicates queries have been removed based on their OQLs
|
||||||
*
|
*
|
||||||
* @return \DBUnionSearch
|
* @return \DBUnionSearch
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
*/
|
||||||
public function RemoveDuplicateQueries()
|
public function RemoveDuplicateQueries()
|
||||||
{
|
{
|
||||||
@@ -499,7 +498,7 @@ class DBUnionSearch extends DBSearch
|
|||||||
{
|
{
|
||||||
if (count($this->aSearches) == 1)
|
if (count($this->aSearches) == 1)
|
||||||
{
|
{
|
||||||
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectedClasses, $aSelectExpr);
|
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
$aSQLQueries = array();
|
$aSQLQueries = array();
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ class DisplayableNode extends GraphNode
|
|||||||
return $aNode;
|
return $aNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
|
public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
|
||||||
{
|
{
|
||||||
$Alpha = 1.0;
|
$Alpha = 1.0;
|
||||||
$oPdf->SetFillColor(200, 200, 200);
|
$oPdf->SetFillColor(200, 200, 200);
|
||||||
@@ -161,8 +161,8 @@ class DisplayableNode extends GraphNode
|
|||||||
$idx++;
|
$idx++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$oPdf->SetFontParams('', 24 * $fScale, '', true);
|
$oPdf->SetFont('dejavusans', '', 24 * $fScale, '', true);
|
||||||
$width = $oPdf->GetStringWidth($this->GetProperty('label'));
|
$width = $oPdf->GetStringWidth($this->GetProperty('label'));
|
||||||
$height = $oPdf->GetStringHeight(1000, $this->GetProperty('label'));
|
$height = $oPdf->GetStringHeight(1000, $this->GetProperty('label'));
|
||||||
$oPdf->setAlpha(0.6 * $Alpha);
|
$oPdf->setAlpha(0.6 * $Alpha);
|
||||||
@@ -532,7 +532,7 @@ class DisplayableRedundancyNode extends DisplayableNode
|
|||||||
return $aNode;
|
return $aNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
|
public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
|
||||||
{
|
{
|
||||||
$oPdf->SetAlpha(1);
|
$oPdf->SetAlpha(1);
|
||||||
if($this->GetProperty('is_reached_count') > $this->GetProperty('threshold'))
|
if($this->GetProperty('is_reached_count') > $this->GetProperty('threshold'))
|
||||||
@@ -547,9 +547,9 @@ class DisplayableRedundancyNode extends DisplayableNode
|
|||||||
$oPdf->Circle($this->x*$fScale, $this->y*$fScale, 16*$fScale, 0, 360, 'DF');
|
$oPdf->Circle($this->x*$fScale, $this->y*$fScale, 16*$fScale, 0, 360, 'DF');
|
||||||
|
|
||||||
$oPdf->SetTextColor(255, 255, 255);
|
$oPdf->SetTextColor(255, 255, 255);
|
||||||
$oPdf->SetFontParams('', 28 * $fScale, '', true);
|
$oPdf->SetFont('dejavusans', '', 28 * $fScale, '', true);
|
||||||
$sLabel = (string)$this->GetProperty('label');
|
$sLabel = (string)$this->GetProperty('label');
|
||||||
$width = $oPdf->GetStringWidth($sLabel, iTopPDF::GetPdfFont(), 'B', 24 * $fScale);
|
$width = $oPdf->GetStringWidth($sLabel, 'dejavusans', 'B', 24*$fScale);
|
||||||
$height = $oPdf->GetStringHeight(1000, $sLabel);
|
$height = $oPdf->GetStringHeight(1000, $sLabel);
|
||||||
$xPos = (float)$this->x*$fScale - $width/2;
|
$xPos = (float)$this->x*$fScale - $width/2;
|
||||||
$yPos = (float)$this->y*$fScale - $height/2;
|
$yPos = (float)$this->y*$fScale - $height/2;
|
||||||
@@ -764,7 +764,7 @@ class DisplayableGroupNode extends DisplayableNode
|
|||||||
return $aNode;
|
return $aNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
|
public function RenderAsPDF(TCPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
|
||||||
{
|
{
|
||||||
$bReached = $this->GetProperty('is_reached');
|
$bReached = $this->GetProperty('is_reached');
|
||||||
$oPdf->SetFillColor(255, 255, 255);
|
$oPdf->SetFillColor(255, 255, 255);
|
||||||
@@ -794,7 +794,7 @@ class DisplayableGroupNode extends DisplayableNode
|
|||||||
$oPdf->Image($sIconPath, ($this->x - 17)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
|
$oPdf->Image($sIconPath, ($this->x - 17)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
|
||||||
$oPdf->Image($sIconPath, ($this->x + 1)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
|
$oPdf->Image($sIconPath, ($this->x + 1)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
|
||||||
$oPdf->Image($sIconPath, ($this->x -8)*$fScale, ($this->y +1)*$fScale, 16*$fScale, 16*$fScale);
|
$oPdf->Image($sIconPath, ($this->x -8)*$fScale, ($this->y +1)*$fScale, 16*$fScale, 16*$fScale);
|
||||||
$oPdf->SetFontParams('', 24 * $fScale, '', true);
|
$oPdf->SetFont('dejavusans', '', 24 * $fScale, '', true);
|
||||||
$width = $oPdf->GetStringWidth($this->GetProperty('label'));
|
$width = $oPdf->GetStringWidth($this->GetProperty('label'));
|
||||||
$oPdf->SetTextColor(0, 0, 0);
|
$oPdf->SetTextColor(0, 0, 0);
|
||||||
$oPdf->Text($this->x*$fScale - $width/2, ($this->y + 25)*$fScale, $this->GetProperty('label'));
|
$oPdf->Text($this->x*$fScale - $width/2, ($this->y + 25)*$fScale, $this->GetProperty('label'));
|
||||||
@@ -1285,7 +1285,7 @@ class DisplayableGraph extends SimpleGraph
|
|||||||
* @param hash $aContextDefs
|
* @param hash $aContextDefs
|
||||||
* @return hash An array ('xmin' => , 'xmax' => ,'ymin' => , 'ymax' => ) of the remaining available area to paint the graph
|
* @return hash An array ('xmin' => , 'xmax' => ,'ymin' => , 'ymax' => ) of the remaining available area to paint the graph
|
||||||
*/
|
*/
|
||||||
protected function RenderKey(iTopPDF $oPdf, $sComments, $xMin, $yMin, $xMax, $yMax, $aContextDefs)
|
protected function RenderKey(TCPDF $oPdf, $sComments, $xMin, $yMin, $xMax, $yMax, $aContextDefs)
|
||||||
{
|
{
|
||||||
$fFontSize = 7; // in mm
|
$fFontSize = 7; // in mm
|
||||||
$fIconSize = 6; // in mm
|
$fIconSize = 6; // in mm
|
||||||
@@ -1296,7 +1296,7 @@ class DisplayableGraph extends SimpleGraph
|
|||||||
$aIcons = array();
|
$aIcons = array();
|
||||||
$aContexts = array();
|
$aContexts = array();
|
||||||
$aContextIcons = array();
|
$aContextIcons = array();
|
||||||
$oPdf->SetFontParams('', $fFontSize, '', true);
|
$oPdf->SetFont('dejavusans', '', $fFontSize, '', true);
|
||||||
foreach($oIterator as $sId => $oNode)
|
foreach($oIterator as $sId => $oNode)
|
||||||
{
|
{
|
||||||
if ($sClass = $oNode->GetObjectClass())
|
if ($sClass = $oNode->GetObjectClass())
|
||||||
@@ -1446,7 +1446,7 @@ class DisplayableGraph extends SimpleGraph
|
|||||||
<<<EOF
|
<<<EOF
|
||||||
<div id="ds_flash" class="search_box">
|
<div id="ds_flash" class="search_box">
|
||||||
<form id="dh_flash" class="search_form_handler closed">
|
<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>
|
<h2 class="sf_title"><span class="sft_long">$sSftShort</span><span class="sft_short">$sSftShort</span><span class="sft_toggler fa fa-caret-down pull-right" title="$sSearchToggle"></span></h2>
|
||||||
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
|
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,6 +24,8 @@
|
|||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
require_once(APPROOT.'/lib/swiftmailer/lib/swift_required.php');
|
||||||
|
|
||||||
Swift_Preferences::getInstance()->setCharset('UTF-8');
|
Swift_Preferences::getInstance()->setCharset('UTF-8');
|
||||||
|
|
||||||
|
|
||||||
@@ -331,6 +333,7 @@ class EMail
|
|||||||
{
|
{
|
||||||
if (($sMimeType === 'text/html') && ($sCustomStyles !== null))
|
if (($sMimeType === 'text/html') && ($sCustomStyles !== null))
|
||||||
{
|
{
|
||||||
|
require_once(APPROOT.'lib/emogrifier/Classes/Emogrifier.php');
|
||||||
$emogrifier = new \Pelago\Emogrifier($sBody, $sCustomStyles);
|
$emogrifier = new \Pelago\Emogrifier($sBody, $sCustomStyles);
|
||||||
$sBody = $emogrifier->emogrify(); // Adds html/body tags if not already present
|
$sBody = $emogrifier->emogrify(); // Adds html/body tags if not already present
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,120 +1,89 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
// Copyright (c) 2010-2017 Combodo SARL
|
||||||
* Copyright (C) 2013-2019 Combodo SARL
|
//
|
||||||
*
|
// This file is part of iTop.
|
||||||
* This file is part of iTop.
|
//
|
||||||
*
|
// iTop is free software; you can redistribute it and/or modify
|
||||||
* iTop is free software; you can redistribute it and/or modify
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
* 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
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
// (at your option) any later version.
|
||||||
* (at your option) any later version.
|
//
|
||||||
*
|
// iTop is distributed in the hope that it will be useful,
|
||||||
* iTop is distributed in the hope that it will be useful,
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// GNU Affero General Public License for more details.
|
||||||
* GNU Affero General Public License for more details.
|
//
|
||||||
*
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
* 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 ExpressionCache
|
|
||||||
*/
|
|
||||||
class ExpressionCache
|
class ExpressionCache
|
||||||
{
|
{
|
||||||
|
static private $aCache = array();
|
||||||
|
|
||||||
/**
|
static public function GetCachedExpression($sClass, $sAttCode)
|
||||||
* @param string $sClass
|
|
||||||
* @param string $sAttCode
|
|
||||||
*
|
|
||||||
* @return mixed|null
|
|
||||||
*/
|
|
||||||
public static function GetCachedExpression($sClass, $sAttCode)
|
|
||||||
{
|
{
|
||||||
if (!utils::GetConfig()->Get('expression_cache_enabled'))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read current cache
|
// read current cache
|
||||||
@include_once (static::GetCacheFileName());
|
@include_once (static::GetCacheFileName());
|
||||||
|
|
||||||
$oExpr = null;
|
$oExpr = null;
|
||||||
$sKey = static::GetKey($sClass, $sAttCode);
|
$sKey = static::GetKey($sClass, $sAttCode);
|
||||||
$sCacheClass = self::GetCacheClassName();
|
if (array_key_exists($sKey, static::$aCache))
|
||||||
if (class_exists($sCacheClass))
|
|
||||||
{
|
{
|
||||||
/** @noinspection PhpUndefinedFieldInspection The property is dynamically generated */
|
$oExpr = static::$aCache[$sKey];
|
||||||
if (array_key_exists($sKey, $sCacheClass::$aCache))
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (class_exists('ExpressionCacheData'))
|
||||||
{
|
{
|
||||||
$sVal = $sCacheClass::$aCache[$sKey];
|
if (array_key_exists($sKey, ExpressionCacheData::$aCache))
|
||||||
$oExpr = unserialize($sVal);
|
{
|
||||||
|
$sVal = ExpressionCacheData::$aCache[$sKey];
|
||||||
|
$oExpr = unserialize($sVal);
|
||||||
|
static::$aCache[$sKey] = $oExpr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $oExpr;
|
return $oExpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
static public function Warmup()
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \DictExceptionUnknownLanguage
|
|
||||||
*/
|
|
||||||
public static function Warmup()
|
|
||||||
{
|
{
|
||||||
if (!utils::GetConfig()->Get('expression_cache_enabled'))
|
$sFilePath = static::GetCacheFileName();
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Store current language
|
|
||||||
$sUserLang = Dict::GetUserLanguage();
|
|
||||||
$aLanguages = Dict::GetLanguages();
|
|
||||||
foreach($aLanguages as $sLang => $aLang)
|
|
||||||
{
|
|
||||||
Dict::SetUserLanguage($sLang);
|
|
||||||
$sFilePath = static::GetCacheFileName();
|
|
||||||
$sCacheClass = self::GetCacheClassName();
|
|
||||||
|
|
||||||
if (!is_file($sFilePath))
|
if (!is_file($sFilePath))
|
||||||
{
|
{
|
||||||
$content = <<<EOF
|
$content = <<<EOF
|
||||||
<?php
|
<?php
|
||||||
// Copyright (c) 2010-2019 Combodo SARL
|
// Copyright (c) 2010-2017 Combodo SARL
|
||||||
// Generated Expression Cache file for $sLang
|
// Generated Expression Cache file
|
||||||
|
|
||||||
class $sCacheClass
|
class ExpressionCacheData
|
||||||
{
|
{
|
||||||
static \$aCache = array(
|
static \$aCache = array(
|
||||||
EOF;
|
EOF;
|
||||||
|
|
||||||
foreach (MetaModel::GetClasses() as $sClass)
|
foreach(MetaModel::GetClasses() as $sClass)
|
||||||
|
{
|
||||||
|
$content .= static::GetSerializedExpression($sClass, 'friendlyname');
|
||||||
|
if (MetaModel::IsObsoletable($sClass))
|
||||||
{
|
{
|
||||||
$content .= static::GetSerializedExpression($sClass, 'friendlyname');
|
$content .= static::GetSerializedExpression($sClass, 'obsolescence_flag');
|
||||||
if (MetaModel::IsObsoletable($sClass))
|
|
||||||
{
|
|
||||||
$content .= static::GetSerializedExpression($sClass, 'obsolescence_flag');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$content .= <<<EOF
|
$content .= <<<EOF
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
EOF;
|
EOF;
|
||||||
|
|
||||||
SetupUtils::builddir(dirname($sFilePath));
|
SetupUtils::builddir(dirname($sFilePath));
|
||||||
file_put_contents($sFilePath, $content);
|
file_put_contents($sFilePath, $content);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Dict::SetUserLanguage($sUserLang);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static private function GetSerializedExpression($sClass, $sAttCode)
|
||||||
* @param string $sClass
|
|
||||||
* @param string $sAttCode
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private static function GetSerializedExpression($sClass, $sAttCode)
|
|
||||||
{
|
{
|
||||||
$sKey = static::GetKey($sClass, $sAttCode);
|
$sKey = static::GetKey($sClass, $sAttCode);
|
||||||
$oExpr = DBObjectSearch::GetPolymorphicExpression($sClass, $sAttCode);
|
$oExpr = DBObjectSearch::GetPolymorphicExpression($sClass, $sAttCode);
|
||||||
@@ -122,44 +91,20 @@ EOF;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sClass
|
* @param $sClass
|
||||||
* @param string $sAttCode
|
* @param $sAttCode
|
||||||
*
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private static function GetKey($sClass, $sAttCode)
|
static private function GetKey($sClass, $sAttCode)
|
||||||
{
|
{
|
||||||
return $sClass.'::'.$sAttCode;
|
return $sClass.'::'.$sAttCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public static function GetCacheFileName()
|
public static function GetCacheFileName()
|
||||||
{
|
{
|
||||||
$sLangName = self::GetLangName();
|
return utils::GetCachePath().'expressioncache.php';
|
||||||
return utils::GetCachePath().'expressioncache/expressioncache-' . $sLangName . '.php';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private static function GetCacheClassName()
|
|
||||||
{
|
|
||||||
$sLangName = self::GetLangName();
|
|
||||||
$sCacheClass = "ExpressionCacheData$sLangName";
|
|
||||||
return $sCacheClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
private static function GetLangName()
|
|
||||||
{
|
|
||||||
$sLang = Dict::GetUserLanguage();
|
|
||||||
$sLangName = str_replace(" ", "", $sLang);
|
|
||||||
return $sLangName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -34,43 +34,51 @@ abstract class HTMLSanitizer
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanitize an HTML string with the configured sanitizer, falling back to HTMLDOMSanitizer in case of Exception or invalid configuration
|
* Sanitize an HTML string with the configured sanitizer, falling back to HTMLDOMSanitizer in case of Exception or invalid configuration
|
||||||
|
*
|
||||||
* @param string $sHTML
|
* @param string $sHTML
|
||||||
|
* @param string $sConfigKey
|
||||||
|
*
|
||||||
* @return string
|
* @return string
|
||||||
|
* @noinspection SelfClassReferencingInspection
|
||||||
*/
|
*/
|
||||||
public static function Sanitize($sHTML)
|
public static function Sanitize($sHTML, $sConfigKey = 'html_sanitizer')
|
||||||
{
|
{
|
||||||
$sSanitizerClass = MetaModel::GetConfig()->Get('html_sanitizer');
|
$sSanitizerClass = MetaModel::GetConfig()->Get($sConfigKey);
|
||||||
if(!class_exists($sSanitizerClass))
|
if (!class_exists($sSanitizerClass)) {
|
||||||
{
|
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||||
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
|
|
||||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
$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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(!is_subclass_of($sSanitizerClass, 'HTMLSanitizer'))
|
|
||||||
{
|
try {
|
||||||
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
|
|
||||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
$oSanitizer = new $sSanitizerClass();
|
$oSanitizer = new $sSanitizerClass();
|
||||||
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
|
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
|
||||||
}
|
}
|
||||||
catch(Exception $e)
|
catch(Exception $e) {
|
||||||
{
|
if ($sConfigKey === 'html_sanitizer') {
|
||||||
if($sSanitizerClass != 'HTMLDOMSanitizer')
|
if ($sSanitizerClass !== HTMLDOMSanitizer::class) {
|
||||||
{
|
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
|
||||||
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
|
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
|
||||||
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
|
// try again with the HTMLDOMSanitizer
|
||||||
// try again with the HTMLDOMSanitizer
|
$oSanitizer = new HTMLDOMSanitizer();
|
||||||
$oSanitizer = new HTMLDOMSanitizer();
|
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
|
||||||
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
|
} else {
|
||||||
}
|
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
|
||||||
else
|
IssueLog::Error('The HTML will NOT be sanitized.');
|
||||||
{
|
$sCleanHTML = $sHTML;
|
||||||
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
|
}
|
||||||
IssueLog::Error('The HTML will NOT be sanitized.');
|
} else {
|
||||||
$sCleanHTML = $sHTML;
|
IssueLog::Error('Failed to sanitize string with "'.$sSanitizerClass.'", will return original value ! Exception: '.$e->getMessage());
|
||||||
|
$sCleanHTML = $sHTML;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $sCleanHTML;
|
return $sCleanHTML;
|
||||||
@@ -79,13 +87,10 @@ abstract class HTMLSanitizer
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Dummy HTMLSanitizer which does nothing at all!
|
* Dummy HTMLSanitizer which does nothing at all!
|
||||||
*
|
|
||||||
* Can be used if HTML Sanitization is not important
|
* Can be used if HTML Sanitization is not important
|
||||||
* (for example when importing "safe" data during an on-boarding)
|
* (for example when importing "safe" data during an on-boarding)
|
||||||
* and performance is at stake
|
* and performance is at stake
|
||||||
*
|
*
|
||||||
* **Warning** : this won't filter HTML inserted in iTop at all, so this is a great security issue !
|
|
||||||
* Also, the InlineImage objects processing won't be called.
|
|
||||||
*/
|
*/
|
||||||
class HTMLNullSanitizer extends HTMLSanitizer
|
class HTMLNullSanitizer extends HTMLSanitizer
|
||||||
{
|
{
|
||||||
@@ -97,67 +102,167 @@ class HTMLNullSanitizer extends HTMLSanitizer
|
|||||||
{
|
{
|
||||||
return $sHTML;
|
return $sHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A standard-compliant HTMLSanitizer based on the HTMLPurifier library by Edward Z. Yang
|
* Common implementation for sanitizer using DOM parsing
|
||||||
* Complete but quite slow
|
|
||||||
* http://htmlpurifier.org
|
|
||||||
*/
|
*/
|
||||||
/*
|
abstract class DOMSanitizer extends HTMLSanitizer
|
||||||
class HTMLPurifierSanitizer extends HTMLSanitizer
|
|
||||||
{
|
{
|
||||||
protected static $oPurifier = null;
|
/** @var DOMDocument */
|
||||||
|
protected $oDoc;
|
||||||
public function __construct()
|
|
||||||
{
|
abstract public function GetTagsWhiteList();
|
||||||
if (self::$oPurifier == null)
|
|
||||||
{
|
abstract public function GetTagsBlackList();
|
||||||
$sLibPath = APPROOT.'lib/htmlpurifier/HTMLPurifier.auto.php';
|
|
||||||
if (!file_exists($sLibPath))
|
abstract public function GetAttrsWhiteList();
|
||||||
{
|
|
||||||
throw new Exception("Missing library '$sLibPath', cannot use HTMLPurifierSanitizer.");
|
abstract public function GetAttrsBlackList();
|
||||||
}
|
|
||||||
require_once($sLibPath);
|
abstract public function GetStylesWhiteList();
|
||||||
|
|
||||||
$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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DoSanitize($sHTML)
|
public function DoSanitize($sHTML)
|
||||||
{
|
{
|
||||||
$sCleanHtml = self::$oPurifier->purify($sHTML);
|
$this->oDoc = new DOMDocument();
|
||||||
return $sCleanHtml;
|
$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| )*</o:p>@', '<br>', $sHTML);
|
||||||
|
|
||||||
|
$this->LoadDoc($sHTML);
|
||||||
|
|
||||||
|
$this->CleanNode($this->oDoc);
|
||||||
|
|
||||||
|
$sCleanHtml = $this->PrintDoc();
|
||||||
|
|
||||||
|
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')) {
|
||||||
|
InlineImage::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 HTMLSanitizer
|
|
||||||
|
class HTMLDOMSanitizer extends DOMSanitizer
|
||||||
{
|
{
|
||||||
protected $oDoc;
|
protected $oDoc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
|
||||||
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
||||||
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $aTagsWhiteList = array(
|
protected static $aTagsWhiteList = array(
|
||||||
'html' => array(),
|
'html' => array(),
|
||||||
@@ -206,6 +311,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
|||||||
'q' => array(),
|
'q' => array(),
|
||||||
'hr' => array('style'),
|
'hr' => array('style'),
|
||||||
'pre' => array(),
|
'pre' => array(),
|
||||||
|
'center' => array(),
|
||||||
);
|
);
|
||||||
|
|
||||||
protected static $aAttrsWhiteList = array(
|
protected static $aAttrsWhiteList = array(
|
||||||
@@ -213,8 +319,8 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var array
|
|
||||||
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
||||||
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected static $aStylesWhiteList = array(
|
protected static $aStylesWhiteList = array(
|
||||||
'background-color',
|
'background-color',
|
||||||
@@ -238,160 +344,199 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
|||||||
'white-space',
|
'white-space',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public function GetTagsWhiteList()
|
||||||
|
{
|
||||||
|
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 __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
|
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.
|
// 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"
|
// 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
|
// Regular urls
|
||||||
$sUrlPattern = utils::GetConfig()->Get('url_validation_pattern');
|
$sUrlPattern = utils::GetConfig()->Get('url_validation_pattern');
|
||||||
|
|
||||||
// Mailto urls
|
// 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
|
// Notification placeholders
|
||||||
// eg. $this->caller_id$, $this->hyperlink()$, $this->hyperlink(portal)$, $APP_URL$, $MODULES_URL$, ...
|
// 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])
|
// 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)';
|
$sPlaceholderPattern = '(\$|%24)[\w-]*(->[\w]*(\([\w-]*?\))?)?(\$|%24)';
|
||||||
|
|
||||||
$sPattern = $sUrlPattern . '|' . $sMailtoPattern . '|' . $sPlaceholderPattern;
|
$sPattern = $sUrlPattern.'|'.$sMailtoPattern.'|'.$sPlaceholderPattern;
|
||||||
$sPattern = '/'.str_replace('/', '\/', $sPattern).'/i';
|
$sPattern = '/'.str_replace('/', '\/', $sPattern).'/i';
|
||||||
self::$aAttrsWhiteList['href'] = $sPattern;
|
self::$aAttrsWhiteList['href'] = $sPattern;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function DoSanitize($sHTML)
|
public function LoadDoc($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| )*</o:p>@', '<br>', $sHTML);
|
|
||||||
|
|
||||||
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
|
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
|
||||||
|
}
|
||||||
$this->CleanNode($this->oDoc);
|
|
||||||
|
public function PrintDoc()
|
||||||
|
{
|
||||||
$oXPath = new DOMXPath($this->oDoc);
|
$oXPath = new DOMXPath($this->oDoc);
|
||||||
$sXPath = "//body";
|
$sXPath = "//body";
|
||||||
$oNodesList = $oXPath->query($sXPath);
|
$oNodesList = $oXPath->query($sXPath);
|
||||||
|
|
||||||
if ($oNodesList->length == 0)
|
if ($oNodesList->length == 0) {
|
||||||
{
|
|
||||||
// No body, save the whole document
|
// No body, save the whole document
|
||||||
$sCleanHtml = $this->oDoc->saveHTML();
|
$sCleanHtml = $this->oDoc->saveHTML();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Export only the content of the body tag
|
// Export only the content of the body tag
|
||||||
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
|
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
|
||||||
// remove the body tag itself
|
// remove the body tag itself
|
||||||
$sCleanHtml = str_replace( array('<body>', '</body>'), '', $sCleanHtml);
|
$sCleanHtml = str_replace(array('<body>', '</body>'), '', $sCleanHtml);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sCleanHtml;
|
return $sCleanHtml;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
protected function CleanNode(DOMNode $oElement)
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.6.5 2.7.6 3.0.0 N°4360
|
||||||
|
*/
|
||||||
|
class SVGDOMSanitizer extends DOMSanitizer
|
||||||
|
{
|
||||||
|
public function GetTagsWhiteList()
|
||||||
{
|
{
|
||||||
$aAttrToRemove = array();
|
return [];
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function CleanStyle($sStyle)
|
/**
|
||||||
|
* @return string[]
|
||||||
|
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
|
||||||
|
*/
|
||||||
|
public function GetTagsBlackList()
|
||||||
{
|
{
|
||||||
$aAllowedStyles = array();
|
return [
|
||||||
$aItems = explode(';', $sStyle);
|
'script',
|
||||||
{
|
];
|
||||||
foreach($aItems as $sItem)
|
|
||||||
{
|
|
||||||
$aElements = explode(':', trim($sItem));
|
|
||||||
if (in_array(trim(strtolower($aElements[0])), static::$aStylesWhiteList))
|
|
||||||
{
|
|
||||||
$aAllowedStyles[] = trim($sItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return implode(';', $aAllowedStyles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function IsValidAttributeContent($sAttributeName, $sValue)
|
public function GetAttrsWhiteList()
|
||||||
{
|
{
|
||||||
if (array_key_exists($sAttributeName, self::$aAttrsWhiteList))
|
return [];
|
||||||
{
|
|
||||||
return preg_match(self::$aAttrsWhiteList[$sAttributeName], $sValue);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* @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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ EOF
|
|||||||
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
|
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||||
|
|
||||||
return
|
return
|
||||||
<<<JS
|
<<<EOF
|
||||||
// Hook the file upload of all CKEditor instances
|
// Hook the file upload of all CKEditor instances
|
||||||
$('.htmlEditor').each(function() {
|
$('.htmlEditor').each(function() {
|
||||||
var oEditor = $(this).ckeditorGet();
|
var oEditor = $(this).ckeditorGet();
|
||||||
@@ -545,7 +545,7 @@ EOF
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
JS
|
EOF
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,12 +200,15 @@ class ExecutionKPI
|
|||||||
|
|
||||||
self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>");
|
self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>");
|
||||||
|
|
||||||
$fSlowQueries = MetaModel::GetConfig()->Get('log_kpi_slow_queries');
|
|
||||||
|
|
||||||
// Report operation details
|
// Report operation details
|
||||||
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
||||||
{
|
{
|
||||||
$bDisplayHeader = true;
|
$sOperationHtml = '<a name="'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
||||||
|
self::Report("<h4>$sOperationHtml</h4>");
|
||||||
|
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
|
||||||
|
self::Report("<thead>");
|
||||||
|
self::Report(" <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>");
|
||||||
|
self::Report("</thead>");
|
||||||
foreach ($aOpStats as $sArguments => $aEvents)
|
foreach ($aOpStats as $sArguments => $aEvents)
|
||||||
{
|
{
|
||||||
$sHtmlArguments = '<a name="'.md5($sExecId.$sArguments).'"><div style="white-space: pre-wrap;">'.$sArguments.'</div></a>';
|
$sHtmlArguments = '<a name="'.md5($sExecId.$sArguments).'"><div style="white-space: pre-wrap;">'.$sArguments.'</div></a>';
|
||||||
@@ -245,28 +248,12 @@ class ExecutionKPI
|
|||||||
$sTotalInter = round($fTotalInter, 3);
|
$sTotalInter = round($fTotalInter, 3);
|
||||||
$sMinInter = round($fMinInter, 3);
|
$sMinInter = round($fMinInter, 3);
|
||||||
$sMaxInter = round($fMaxInter, 3);
|
$sMaxInter = round($fMaxInter, 3);
|
||||||
if (($fTotalInter >= $fSlowQueries))
|
self::Report("<tr>");
|
||||||
{
|
self::Report(" <td>$sHtmlArguments</td><td>$iCountInter</td><td>$sTotalInter</td><td>$sMinInter</td><td>$sMaxInter</td>");
|
||||||
if ($bDisplayHeader)
|
self::Report("</tr>");
|
||||||
{
|
|
||||||
$sOperationHtml = '<a name="'.md5($sExecId.$sOperation).'">'.$sOperation.'</a>';
|
|
||||||
self::Report("<h4>$sOperationHtml</h4>");
|
|
||||||
self::Report("<table border=\"1\" style=\"$sTableStyle\">");
|
|
||||||
self::Report("<thead>");
|
|
||||||
self::Report(" <th>Operation details (+ blame caller if log_kpi_duration = 2)</th><th>Count</th><th>Duration</th><th>Min</th><th>Max</th>");
|
|
||||||
self::Report("</thead>");
|
|
||||||
$bDisplayHeader = false;
|
|
||||||
}
|
|
||||||
self::Report("<tr>");
|
|
||||||
self::Report(" <td>$sHtmlArguments</td><td>$iCountInter</td><td>$sTotalInter</td><td>$sMinInter</td><td>$sMaxInter</td>");
|
|
||||||
self::Report("</tr>");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$bDisplayHeader)
|
|
||||||
{
|
|
||||||
self::Report("</table>");
|
|
||||||
self::Report("<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>");
|
|
||||||
}
|
}
|
||||||
|
self::Report("</table>");
|
||||||
|
self::Report("<p><a href=\"#".md5($sExecId)."\">Back to page stats</a></p>");
|
||||||
}
|
}
|
||||||
self::Report('<a name="end-'.md5($sExecId).'"> </a>');
|
self::Report('<a name="end-'.md5($sExecId).'"> </a>');
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,107 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Copyright (C) 2010-2017 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/>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Associated with the metamodel -> MakeQuery/MakeQuerySingleTable
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
class QueryBuilderContext
|
|
||||||
{
|
|
||||||
protected $m_oRootFilter;
|
|
||||||
protected $m_aClassAliases;
|
|
||||||
protected $m_aTableAliases;
|
|
||||||
protected $m_aModifierProperties;
|
|
||||||
protected $m_aSelectedClasses;
|
|
||||||
protected $m_aFilteredTables;
|
|
||||||
|
|
||||||
public $m_oQBExpressions;
|
|
||||||
|
|
||||||
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
|
||||||
{
|
|
||||||
$this->m_oRootFilter = $oFilter;
|
|
||||||
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr, $aSelectExpr);
|
|
||||||
|
|
||||||
$this->m_aClassAliases = $oFilter->GetJoinedClasses();
|
|
||||||
$this->m_aTableAliases = array();
|
|
||||||
$this->m_aFilteredTables = array();
|
|
||||||
|
|
||||||
$this->m_aModifierProperties = $aModifierProperties;
|
|
||||||
if (is_null($aSelectedClasses))
|
|
||||||
{
|
|
||||||
$this->m_aSelectedClasses = $oFilter->GetSelectedClasses();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// For the unions, the selected classes can be upper in the hierarchy (lowest common ancestor)
|
|
||||||
$this->m_aSelectedClasses = $aSelectedClasses;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetRootFilter()
|
|
||||||
{
|
|
||||||
return $this->m_oRootFilter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GenerateTableAlias($sNewName, $sRealName)
|
|
||||||
{
|
|
||||||
return MetaModel::GenerateUniqueAlias($this->m_aTableAliases, $sNewName, $sRealName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GenerateClassAlias($sNewName, $sRealName)
|
|
||||||
{
|
|
||||||
return MetaModel::GenerateUniqueAlias($this->m_aClassAliases, $sNewName, $sRealName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetModifierProperties($sPluginClass)
|
|
||||||
{
|
|
||||||
if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
|
|
||||||
{
|
|
||||||
return $this->m_aModifierProperties[$sPluginClass];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSelectedClass($sAlias)
|
|
||||||
{
|
|
||||||
return $this->m_aSelectedClasses[$sAlias];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddFilteredTable($sTableAlias, $oCondition)
|
|
||||||
{
|
|
||||||
if (array_key_exists($sTableAlias, $this->m_aFilteredTables))
|
|
||||||
{
|
|
||||||
$this->m_aFilteredTables[$sTableAlias][] = $oCondition;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->m_aFilteredTables[$sTableAlias] = array($oCondition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetFilteredTables()
|
|
||||||
{
|
|
||||||
return $this->m_aFilteredTables;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
class QueryBuilderExpressions
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Expression
|
|
||||||
*/
|
|
||||||
protected $m_oConditionExpr;
|
|
||||||
/**
|
|
||||||
* @var Expression[]
|
|
||||||
*/
|
|
||||||
protected $m_aSelectExpr;
|
|
||||||
/**
|
|
||||||
* @var Expression[]
|
|
||||||
*/
|
|
||||||
protected $m_aGroupByExpr;
|
|
||||||
/**
|
|
||||||
* @var Expression[]
|
|
||||||
*/
|
|
||||||
protected $m_aJoinFields;
|
|
||||||
/**
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
protected $m_aClassIds;
|
|
||||||
|
|
||||||
public function __construct(DBObjectSearch $oSearch, $aGroupByExpr = null, $aSelectExpr = null)
|
|
||||||
{
|
|
||||||
$this->m_oConditionExpr = $oSearch->GetCriteria();
|
|
||||||
if (!$oSearch->GetShowObsoleteData())
|
|
||||||
{
|
|
||||||
foreach ($oSearch->GetSelectedClasses() as $sAlias => $sClass)
|
|
||||||
{
|
|
||||||
if (MetaModel::IsObsoletable($sClass))
|
|
||||||
{
|
|
||||||
$oNotObsolete = new BinaryExpression(new FieldExpression('obsolescence_flag', $sAlias), '=', new ScalarExpression(0));
|
|
||||||
$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oNotObsolete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->m_aSelectExpr = is_null($aSelectExpr) ? array() : $aSelectExpr;
|
|
||||||
$this->m_aGroupByExpr = $aGroupByExpr;
|
|
||||||
$this->m_aJoinFields = array();
|
|
||||||
|
|
||||||
$this->m_aClassIds = array();
|
|
||||||
foreach ($oSearch->GetJoinedClasses() as $sClassAlias => $sClass)
|
|
||||||
{
|
|
||||||
$this->m_aClassIds[$sClassAlias] = new FieldExpression('id', $sClassAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSelect()
|
|
||||||
{
|
|
||||||
return $this->m_aSelectExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetGroupBy()
|
|
||||||
{
|
|
||||||
return $this->m_aGroupByExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetCondition()
|
|
||||||
{
|
|
||||||
return $this->m_oConditionExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Expression|mixed
|
|
||||||
*/
|
|
||||||
public function PopJoinField()
|
|
||||||
{
|
|
||||||
return array_pop($this->m_aJoinFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $sAttAlias
|
|
||||||
* @param Expression $oExpression
|
|
||||||
*/
|
|
||||||
public function AddSelect($sAttAlias, Expression $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_aSelectExpr[$sAttAlias] = $oExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Expression $oExpression
|
|
||||||
*/
|
|
||||||
public function AddCondition(Expression $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oExpression);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Expression $oExpression
|
|
||||||
*/
|
|
||||||
public function PushJoinField(Expression $oExpression)
|
|
||||||
{
|
|
||||||
array_push($this->m_aJoinFields, $oExpression);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get tables representing the queried objects
|
|
||||||
* Could be further optimized: when the first join is an outer join, then the rest can be omitted
|
|
||||||
*
|
|
||||||
* @param array $aTables
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function GetMandatoryTables(&$aTables = null)
|
|
||||||
{
|
|
||||||
if (is_null($aTables))
|
|
||||||
{
|
|
||||||
$aTables = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->m_aClassIds as $sClass => $oExpression)
|
|
||||||
{
|
|
||||||
$oExpression->CollectUsedParents($aTables);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aTables;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
|
||||||
{
|
|
||||||
$this->m_oConditionExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
|
||||||
foreach ($this->m_aSelectExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
|
||||||
}
|
|
||||||
if ($this->m_aGroupByExpr)
|
|
||||||
{
|
|
||||||
foreach ($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($this->m_aJoinFields as $oExpression)
|
|
||||||
{
|
|
||||||
$oExpression->GetUnresolvedFields($sAlias, $aUnresolved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
|
||||||
{
|
|
||||||
$this->m_oConditionExpr = $this->m_oConditionExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
foreach ($this->m_aSelectExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$this->m_aSelectExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
}
|
|
||||||
if ($this->m_aGroupByExpr)
|
|
||||||
{
|
|
||||||
foreach ($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$this->m_aGroupByExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($this->m_aJoinFields as $index => $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_aJoinFields[$index] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->m_aClassIds as $sClass => $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_aClassIds[$sClass] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function RenameParam($sOldName, $sNewName)
|
|
||||||
{
|
|
||||||
$this->m_oConditionExpr->RenameParam($sOldName, $sNewName);
|
|
||||||
foreach ($this->m_aSelectExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$this->m_aSelectExpr[$sColAlias] = $oExpr->RenameParam($sOldName, $sNewName);
|
|
||||||
}
|
|
||||||
if ($this->m_aGroupByExpr)
|
|
||||||
{
|
|
||||||
foreach ($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$this->m_aGroupByExpr[$sColAlias] = $oExpr->RenameParam($sOldName, $sNewName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($this->m_aJoinFields as $index => $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_aJoinFields[$index] = $oExpression->RenameParam($sOldName, $sNewName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,173 +16,47 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 2.7.0 N°2518
|
|
||||||
*/
|
|
||||||
interface ILogFileNameBuilder
|
|
||||||
{
|
|
||||||
public function __construct($sFileFullPath);
|
|
||||||
|
|
||||||
public function GetLogFilePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
class DefaultLogFileNameBuilder implements ILogFileNameBuilder
|
|
||||||
{
|
|
||||||
private $sLogFileFullPath;
|
|
||||||
|
|
||||||
public function __construct($sFileFullPath)
|
|
||||||
{
|
|
||||||
$this->sLogFileFullPath = $sFileFullPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetLogFilePath()
|
|
||||||
{
|
|
||||||
return $this->sLogFileFullPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a suffix to the filename
|
|
||||||
*
|
|
||||||
* @since 2.7.0 N°2518
|
|
||||||
*/
|
|
||||||
abstract class RotatingLogFileNameBuilder implements ILogFileNameBuilder
|
|
||||||
{
|
|
||||||
protected $sFilePath;
|
|
||||||
protected $sFileBaseName;
|
|
||||||
protected $sFileExtension;
|
|
||||||
|
|
||||||
public function __construct($sFileFullPath)
|
|
||||||
{
|
|
||||||
$aPathParts = pathinfo($sFileFullPath);
|
|
||||||
|
|
||||||
$this->sFilePath = $aPathParts['dirname'];
|
|
||||||
$this->sFileBaseName = $aPathParts['filename'];
|
|
||||||
$this->sFileExtension = $aPathParts['extension'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetLogFilePath()
|
|
||||||
{
|
|
||||||
$sFileSuffix = $this->GetFileSuffix();
|
|
||||||
|
|
||||||
return $this->sFilePath
|
|
||||||
.'/'
|
|
||||||
.$this->sFileBaseName
|
|
||||||
.'.'.$sFileSuffix
|
|
||||||
.'.'.$this->sFileExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected function GetFileSuffix();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 2.7.0 N°2518
|
|
||||||
*/
|
|
||||||
class DailyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
|
|
||||||
{
|
|
||||||
protected function GetFileSuffix()
|
|
||||||
{
|
|
||||||
return date('Y-m-d');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 2.7.0 N°2518
|
|
||||||
*/
|
|
||||||
class WeeklyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
|
|
||||||
{
|
|
||||||
protected function GetFileSuffix()
|
|
||||||
{
|
|
||||||
$sWeekYear = date('o');
|
|
||||||
$sWeekNumber = date('W');
|
|
||||||
|
|
||||||
return $sWeekYear.'-week'.$sWeekNumber;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @since 2.7.0 N°2518
|
|
||||||
*/
|
|
||||||
class LogFileNameBuilderFactory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Uses the 'log_filename_builder_impl' config parameter
|
|
||||||
*
|
|
||||||
* @param string $sFileFullPath
|
|
||||||
*
|
|
||||||
* @return \ILogFileNameBuilder
|
|
||||||
* @throws \ConfigException
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public static function GetInstance($sFileFullPath)
|
|
||||||
{
|
|
||||||
$oConfig = utils::GetConfig();
|
|
||||||
$sFileNameBuilderImpl = $oConfig->Get('log_filename_builder_impl');
|
|
||||||
if (empty($sFileNameBuilderImpl))
|
|
||||||
{
|
|
||||||
$sFileNameBuilderImpl = 'DefaultLogFileNameBuilder';
|
|
||||||
}
|
|
||||||
|
|
||||||
return new $sFileNameBuilderImpl($sFileFullPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File logging
|
* File logging
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
* @since 2.7.0 allow to rotate file (N°2518)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class FileLog
|
class FileLog
|
||||||
{
|
{
|
||||||
protected $oFileNameBuilder;
|
protected $m_sFile = ''; // log is disabled if this is empty
|
||||||
|
|
||||||
/**
|
|
||||||
* FileLog constructor.
|
|
||||||
*
|
|
||||||
* @param string $sFileName
|
|
||||||
*
|
|
||||||
* @throws \ConfigException
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function __construct($sFileName = '')
|
public function __construct($sFileName = '')
|
||||||
{
|
{
|
||||||
$this->oFileNameBuilder = LogFileNameBuilderFactory::GetInstance($sFileName);
|
$this->m_sFile = $sFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Error($sText)
|
public function Error($sText)
|
||||||
{
|
{
|
||||||
$this->Write('Error | '.$sText);
|
self::Write("Error | ".$sText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Warning($sText)
|
public function Warning($sText)
|
||||||
{
|
{
|
||||||
$this->Write('Warning | '.$sText);
|
self::Write("Warning | ".$sText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Info($sText)
|
public function Info($sText)
|
||||||
{
|
{
|
||||||
$this->Write('Info | '.$sText);
|
self::Write("Info | ".$sText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function Ok($sText)
|
public function Ok($sText)
|
||||||
{
|
{
|
||||||
$this->Write('Ok | '.$sText);
|
self::Write("Ok | ".$sText);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function Write($sText)
|
protected function Write($sText)
|
||||||
{
|
{
|
||||||
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
|
if (strlen($this->m_sFile) == 0) return;
|
||||||
|
|
||||||
if (empty($sLogFilePath))
|
$hLogFile = @fopen($this->m_sFile, 'a');
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$hLogFile = @fopen($sLogFilePath, 'a');
|
|
||||||
if ($hLogFile !== false)
|
if ($hLogFile !== false)
|
||||||
{
|
{
|
||||||
flock($hLogFile, LOCK_EX);
|
flock($hLogFile, LOCK_EX);
|
||||||
@@ -199,7 +73,6 @@ abstract class LogAPI
|
|||||||
{
|
{
|
||||||
public static function Enable($sTargetFile)
|
public static function Enable($sTargetFile)
|
||||||
{
|
{
|
||||||
// m_oFileLog is not defined as a class attribute so that each impl will have its own
|
|
||||||
static::$m_oFileLog = new FileLog($sTargetFile);
|
static::$m_oFileLog = new FileLog($sTargetFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,23 +17,14 @@
|
|||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||||
//
|
//
|
||||||
|
|
||||||
require_once APPROOT.'core/modulehandler.class.inc.php';
|
require_once(APPROOT.'core/modulehandler.class.inc.php');
|
||||||
require_once APPROOT.'core/querymodifier.class.inc.php';
|
require_once(APPROOT.'core/querybuildercontext.class.inc.php');
|
||||||
require_once APPROOT.'core/metamodelmodifier.inc.php';
|
require_once(APPROOT.'core/querymodifier.class.inc.php');
|
||||||
require_once APPROOT.'core/computing.inc.php';
|
require_once(APPROOT.'core/metamodelmodifier.inc.php');
|
||||||
require_once APPROOT.'core/relationgraph.class.inc.php';
|
require_once(APPROOT.'core/computing.inc.php');
|
||||||
require_once APPROOT.'core/apc-compat.php';
|
require_once(APPROOT.'core/relationgraph.class.inc.php');
|
||||||
require_once APPROOT.'core/expressioncache.class.inc.php';
|
require_once(APPROOT.'core/apc-compat.php');
|
||||||
|
require_once(APPROOT.'core/expressioncache.class.inc.php');
|
||||||
|
|
||||||
/**
|
|
||||||
* We need to have all iLoginFSMExtension/iLoginDataExtension impl loaded ! Cannot use autoloader...
|
|
||||||
*/
|
|
||||||
require_once APPROOT.'application/loginform.class.inc.php';
|
|
||||||
require_once APPROOT.'application/loginbasic.class.inc.php';
|
|
||||||
require_once APPROOT.'application/logindefault.class.inc.php';
|
|
||||||
require_once APPROOT.'application/loginexternal.class.inc.php';
|
|
||||||
require_once APPROOT.'application/loginurl.class.inc.php';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Metamodel
|
* Metamodel
|
||||||
@@ -944,7 +935,7 @@ abstract class MetaModel
|
|||||||
* @param string $sClass Name of the class
|
* @param string $sClass Name of the class
|
||||||
* @param string $sAttCode Code of the attributes
|
* @param string $sAttCode Code of the attributes
|
||||||
*
|
*
|
||||||
* @return array List of attribute codes that depend on the given attribute, empty array if none.
|
* @return Array List of attribute codes that depend on the given attribute, empty array if none.
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
@@ -1093,30 +1084,17 @@ abstract class MetaModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get "finalclass" DB field name
|
|
||||||
* @param string $sClass
|
* @param string $sClass
|
||||||
*
|
*
|
||||||
* @return string
|
* @return mixed
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
*/
|
*/
|
||||||
final static public function DBGetClassField($sClass)
|
final static public function DBGetClassField($sClass)
|
||||||
{
|
{
|
||||||
self::_check_subclass($sClass);
|
self::_check_subclass($sClass);
|
||||||
|
|
||||||
// Leaf classes have no "finalclass" field.
|
|
||||||
// Non Leaf classes have the same field as the root class
|
|
||||||
if (!self::IsLeafClass($sClass))
|
|
||||||
{
|
|
||||||
$sClass = MetaModel::GetRootClass($sClass);
|
|
||||||
}
|
|
||||||
return self::$m_aClassParams[$sClass]["db_finalclass_field"];
|
return self::$m_aClassParams[$sClass]["db_finalclass_field"];
|
||||||
}
|
}
|
||||||
|
|
||||||
final public static function IsLeafClass($sClass)
|
|
||||||
{
|
|
||||||
return empty(self::$m_aChildClasses[$sClass]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sClass
|
* @param string $sClass
|
||||||
*
|
*
|
||||||
@@ -1126,7 +1104,16 @@ abstract class MetaModel
|
|||||||
final static public function IsStandaloneClass($sClass)
|
final static public function IsStandaloneClass($sClass)
|
||||||
{
|
{
|
||||||
self::_check_subclass($sClass);
|
self::_check_subclass($sClass);
|
||||||
return (empty(self::$m_aChildClasses[$sClass]) && empty(self::$m_aParentClasses[$sClass]));
|
|
||||||
|
if (count(self::$m_aChildClasses[$sClass]) == 0)
|
||||||
|
{
|
||||||
|
if (count(self::$m_aParentClasses[$sClass]) == 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1200,7 +1187,7 @@ abstract class MetaModel
|
|||||||
/**
|
/**
|
||||||
* array of ("classname" => array of attributes)
|
* array of ("classname" => array of attributes)
|
||||||
*
|
*
|
||||||
* @var \AttributeDefinition[][]
|
* @var \AttributeDefinition[]
|
||||||
*/
|
*/
|
||||||
private static $m_aAttribDefs = array();
|
private static $m_aAttribDefs = array();
|
||||||
/**
|
/**
|
||||||
@@ -1417,7 +1404,7 @@ abstract class MetaModel
|
|||||||
* @param string $sClass Class name
|
* @param string $sClass Class name
|
||||||
* @param string $sAttCode Attribute code
|
* @param string $sAttCode Attribute code
|
||||||
*
|
*
|
||||||
* @return \AttributeDefinition the AttributeDefinition of the $sAttCode attribute of the $sClass class
|
* @return AttributeDefinition the AttributeDefinition of the $sAttCode attribute of the $sClass class
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
final static public function GetAttributeDef($sClass, $sAttCode)
|
final static public function GetAttributeDef($sClass, $sAttCode)
|
||||||
@@ -2210,10 +2197,10 @@ abstract class MetaModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//else
|
else
|
||||||
//{
|
{
|
||||||
// Cannot take the legacy system into account... simply ignore it
|
// Cannot take the legacy system into account... simply ignore it
|
||||||
//}
|
}
|
||||||
} // foreach class
|
} // foreach class
|
||||||
|
|
||||||
// Perform the up/down reconciliation for the legacy definitions
|
// Perform the up/down reconciliation for the legacy definitions
|
||||||
@@ -2709,7 +2696,6 @@ abstract class MetaModel
|
|||||||
*/
|
*/
|
||||||
public static function GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs = array(), $sContains = '', $iAdditionalValue = null)
|
public static function GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs = array(), $sContains = '', $iAdditionalValue = null)
|
||||||
{
|
{
|
||||||
/** @var \AttributeExternalKey $oAttDef */
|
|
||||||
$oAttDef = self::GetAttributeDef($sClass, $sAttCode);
|
$oAttDef = self::GetAttributeDef($sClass, $sAttCode);
|
||||||
return $oAttDef->GetAllowedValuesAsObjectSet($aArgs, $sContains, $iAdditionalValue);
|
return $oAttDef->GetAllowedValuesAsObjectSet($aArgs, $sContains, $iAdditionalValue);
|
||||||
}
|
}
|
||||||
@@ -2794,7 +2780,7 @@ abstract class MetaModel
|
|||||||
|
|
||||||
// Build the list of available extensions
|
// Build the list of available extensions
|
||||||
//
|
//
|
||||||
$aInterfaces = array('iApplicationUIExtension', 'iPreferencesExtension', 'iApplicationObjectExtension', 'iLoginFSMExtension', 'iLoginDataExtension', 'iLogoutExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider');
|
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider');
|
||||||
foreach($aInterfaces as $sInterface)
|
foreach($aInterfaces as $sInterface)
|
||||||
{
|
{
|
||||||
self::$m_aExtensionClasses[$sInterface] = array();
|
self::$m_aExtensionClasses[$sInterface] = array();
|
||||||
@@ -4163,10 +4149,6 @@ abstract class MetaModel
|
|||||||
// Compute query modifiers properties (can be set in the search itself, by the context, etc.)
|
// Compute query modifiers properties (can be set in the search itself, by the context, etc.)
|
||||||
//
|
//
|
||||||
$aModifierProperties = array();
|
$aModifierProperties = array();
|
||||||
/**
|
|
||||||
* @var string $sPluginClass
|
|
||||||
* @var iQueryModifier $oQueryModifier
|
|
||||||
*/
|
|
||||||
foreach(MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
|
foreach(MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
|
||||||
{
|
{
|
||||||
// Lowest precedence: the application context
|
// Lowest precedence: the application context
|
||||||
@@ -4396,7 +4378,8 @@ abstract class MetaModel
|
|||||||
{
|
{
|
||||||
$iChildId = $aValues['id'];
|
$iChildId = $aValues['id'];
|
||||||
$iLeft = $iCurrIndex++;
|
$iLeft = $iCurrIndex++;
|
||||||
self::HKInitChildren($sTable, $sAttCode, $oAttDef, $iChildId, $iCurrIndex);
|
//FIXME calling ourselves but no return statement in this method ?!!???
|
||||||
|
$aChildren = self::HKInitChildren($sTable, $sAttCode, $oAttDef, $iChildId, $iCurrIndex);
|
||||||
$iRight = $iCurrIndex++;
|
$iRight = $iCurrIndex++;
|
||||||
$sSQL = "UPDATE `$sTable` SET `$sLeft` = $iLeft, `$sRight` = $iRight WHERE id= $iChildId";
|
$sSQL = "UPDATE `$sTable` SET `$sLeft` = $iLeft, `$sRight` = $iRight WHERE id= $iChildId";
|
||||||
CMDBSource::Query($sSQL);
|
CMDBSource::Query($sSQL);
|
||||||
@@ -5419,14 +5402,6 @@ abstract class MetaModel
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
$aAlterTableItems[$sTable][$sField] = "ADD $sFieldDefinition";
|
$aAlterTableItems[$sTable][$sField] = "ADD $sFieldDefinition";
|
||||||
$aAdditionalRequests = self::GetAdditionalRequestAfterAlter($sClass, $sTable, $sField);
|
|
||||||
if (!empty($aAdditionalRequests))
|
|
||||||
{
|
|
||||||
foreach ($aAdditionalRequests as $sAdditionalRequest)
|
|
||||||
{
|
|
||||||
$aPostTableAlteration[$sTable][] = $sAdditionalRequest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($bIndexNeeded)
|
if ($bIndexNeeded)
|
||||||
@@ -5861,10 +5836,10 @@ abstract class MetaModel
|
|||||||
{
|
{
|
||||||
$sAction = $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action'];
|
$sAction = $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action'];
|
||||||
|
|
||||||
//if ($sAction == 'Delete')
|
if ($sAction == 'Delete')
|
||||||
//{
|
{
|
||||||
// No need to update, the record will be deleted!
|
// No need to update, the record will be deleted!
|
||||||
//}
|
}
|
||||||
|
|
||||||
if ($sAction == 'Update')
|
if ($sAction == 'Update')
|
||||||
{
|
{
|
||||||
@@ -6254,10 +6229,6 @@ abstract class MetaModel
|
|||||||
{
|
{
|
||||||
self::$m_oConfig = $oConfiguration;
|
self::$m_oConfig = $oConfiguration;
|
||||||
|
|
||||||
// N°2478 utils has his own private attribute
|
|
||||||
// @see utils::GetConfig : it always call MetaModel, but to be sure we're doing this extra copy anyway O:)
|
|
||||||
utils::SetConfig($oConfiguration);
|
|
||||||
|
|
||||||
// Set log ASAP
|
// Set log ASAP
|
||||||
if (self::$m_oConfig->GetLogGlobal())
|
if (self::$m_oConfig->GetLogGlobal())
|
||||||
{
|
{
|
||||||
@@ -6291,7 +6262,7 @@ abstract class MetaModel
|
|||||||
&& function_exists('apc_store');
|
&& function_exists('apc_store');
|
||||||
|
|
||||||
DBSearch::EnableQueryCache(self::$m_oConfig->GetQueryCacheEnabled(), self::$m_bUseAPCCache, self::$m_oConfig->Get('apc_cache.query_ttl'));
|
DBSearch::EnableQueryCache(self::$m_oConfig->GetQueryCacheEnabled(), self::$m_bUseAPCCache, self::$m_oConfig->Get('apc_cache.query_ttl'));
|
||||||
DBSearch::EnableQueryTrace(self::$m_oConfig->GetLogQueries() || self::$m_oConfig->Get('log_kpi_record_oql'));
|
DBSearch::EnableQueryTrace(self::$m_oConfig->GetLogQueries());
|
||||||
DBSearch::EnableQueryIndentation(self::$m_oConfig->Get('query_indentation_enabled'));
|
DBSearch::EnableQueryIndentation(self::$m_oConfig->Get('query_indentation_enabled'));
|
||||||
DBSearch::EnableOptimizeQuery(self::$m_oConfig->Get('query_optimization_enabled'));
|
DBSearch::EnableOptimizeQuery(self::$m_oConfig->Get('query_optimization_enabled'));
|
||||||
|
|
||||||
@@ -6703,7 +6674,7 @@ abstract class MetaModel
|
|||||||
* @param bool $bAllowAllData if true then user rights will be bypassed - use with care!
|
* @param bool $bAllowAllData if true then user rights will be bypassed - use with care!
|
||||||
* @param null $aModifierProperties
|
* @param null $aModifierProperties
|
||||||
*
|
*
|
||||||
* @return \DBObject null if : (the object is not found) or (archive mode disabled and object is archived and
|
* @return \cmdbAbstractObject null if : (the object is not found) or (archive mode disabled and object is archived and
|
||||||
* $bMustBeFound=false)
|
* $bMustBeFound=false)
|
||||||
* @throws CoreException if no result found and $bMustBeFound=true
|
* @throws CoreException if no result found and $bMustBeFound=true
|
||||||
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
|
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
|
||||||
@@ -6912,11 +6883,10 @@ abstract class MetaModel
|
|||||||
* Instantiate a persistable object (not yet persisted)
|
* Instantiate a persistable object (not yet persisted)
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*
|
|
||||||
* @param string $sClass A persistable class
|
* @param string $sClass A persistable class
|
||||||
* @param array|null $aValues array of attcode => attribute value to preset
|
* @param array|null $aValues array of attcode => attribute value to preset
|
||||||
*
|
*
|
||||||
* @return \cmdbAbstractObject
|
* @return DBObject
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
*/
|
*/
|
||||||
public static function NewObject($sClass, $aValues = null)
|
public static function NewObject($sClass, $aValues = null)
|
||||||
@@ -7000,7 +6970,7 @@ abstract class MetaModel
|
|||||||
* Surpasses BulkDelete as it can handle abstract classes, but has the other limitation as it bypasses standard
|
* Surpasses BulkDelete as it can handle abstract classes, but has the other limitation as it bypasses standard
|
||||||
* objects handlers
|
* objects handlers
|
||||||
*
|
*
|
||||||
* @param \DBSearch $oFilter Scope of objects to wipe out
|
* @param string $oFilter Scope of objects to wipe out
|
||||||
*
|
*
|
||||||
* @return int The count of deleted objects
|
* @return int The count of deleted objects
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
@@ -7252,9 +7222,11 @@ abstract class MetaModel
|
|||||||
* @param string $sInput
|
* @param string $sInput
|
||||||
* @param array $aParams
|
* @param array $aParams
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return string
|
||||||
|
*
|
||||||
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
static public function ApplyParams($sInput, $aParams)
|
public static function ApplyParams($sInput, $aParams)
|
||||||
{
|
{
|
||||||
$aParams = static::AddMagicPlaceholders($aParams);
|
$aParams = static::AddMagicPlaceholders($aParams);
|
||||||
|
|
||||||
@@ -7264,7 +7236,7 @@ abstract class MetaModel
|
|||||||
|
|
||||||
$aSearches = array();
|
$aSearches = array();
|
||||||
$aReplacements = array();
|
$aReplacements = array();
|
||||||
foreach($aParams as $sSearch => $replace)
|
foreach ($aParams as $sSearch => $replace)
|
||||||
{
|
{
|
||||||
// Some environment parameters are objects, we just need scalars
|
// Some environment parameters are objects, we just need scalars
|
||||||
if (is_object($replace))
|
if (is_object($replace))
|
||||||
@@ -7499,25 +7471,6 @@ abstract class MetaModel
|
|||||||
return $sRet;
|
return $sRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function GetAdditionalRequestAfterAlter($sClass, $sTable, $sField)
|
|
||||||
{
|
|
||||||
$aRequests = array();
|
|
||||||
|
|
||||||
// Copy finalclass fields from root class to intermediate classes
|
|
||||||
if ($sField == self::DBGetClassField($sClass))
|
|
||||||
{
|
|
||||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
|
||||||
$sRootTable = self::DBGetTable($sRootClass);
|
|
||||||
$sKey = self::DBGetKey($sClass);
|
|
||||||
$sRootKey = self::DBGetKey($sRootClass);
|
|
||||||
$sRootField = self::DBGetClassField($sRootClass);
|
|
||||||
// Copy the finalclass of the root table
|
|
||||||
$sRequest = "UPDATE `$sTable`,`$sRootTable` SET `$sTable`.`$sField` = `$sRootTable`.`$sRootField` WHERE `$sTable`.`$sKey` = `$sRootTable`.`$sRootKey`";
|
|
||||||
$aRequests[] = $sRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aRequests;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,29 +47,6 @@ abstract class Expression
|
|||||||
*/
|
*/
|
||||||
abstract public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true);
|
abstract public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true);
|
||||||
|
|
||||||
public final static function ConvertArrayToOQL($aExpressions, $aArgs)
|
|
||||||
{
|
|
||||||
$aRet = array();
|
|
||||||
foreach ($aExpressions as $sName => $oExpression)
|
|
||||||
{
|
|
||||||
/** @var Expression $oExpression */
|
|
||||||
$aRet[$sName] = $oExpression->RenderExpression(false, $aArgs);
|
|
||||||
}
|
|
||||||
return $aRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final static function ConvertArrayFromOQL($aExpressions)
|
|
||||||
{
|
|
||||||
$aRet = array();
|
|
||||||
foreach ($aExpressions as $sName => $sConditionExpr)
|
|
||||||
{
|
|
||||||
/** @var Expression $oExpression */
|
|
||||||
$aRet[$sName] = Expression::FromOQL($sConditionExpr);
|
|
||||||
}
|
|
||||||
return $aRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* recursive rendering
|
* recursive rendering
|
||||||
*
|
*
|
||||||
@@ -1559,19 +1536,7 @@ class VariableExpression extends UnaryExpression
|
|||||||
$oRet = null;
|
$oRet = null;
|
||||||
if (array_key_exists($this->m_sName, $aArgs))
|
if (array_key_exists($this->m_sName, $aArgs))
|
||||||
{
|
{
|
||||||
if(is_array($aArgs[$this->m_sName]))
|
$oRet = new ScalarExpression($aArgs[$this->m_sName]);
|
||||||
{
|
|
||||||
$aExpressions = array();
|
|
||||||
foreach($aArgs[$this->m_sName] as $sValue)
|
|
||||||
{
|
|
||||||
$aExpressions[] = new ScalarExpression($sValue);
|
|
||||||
}
|
|
||||||
$oRet = new ListExpression($aExpressions);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oRet = new ScalarExpression($aArgs[$this->m_sName]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
elseif (($iPos = strpos($this->m_sName, '->')) !== false)
|
elseif (($iPos = strpos($this->m_sName, '->')) !== false)
|
||||||
{
|
{
|
||||||
@@ -2324,3 +2289,183 @@ class CharConcatWSExpression extends CharConcatExpression
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class QueryBuilderExpressions
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Expression
|
||||||
|
*/
|
||||||
|
protected $m_oConditionExpr;
|
||||||
|
/**
|
||||||
|
* @var Expression[]
|
||||||
|
*/
|
||||||
|
protected $m_aSelectExpr;
|
||||||
|
/**
|
||||||
|
* @var Expression[]
|
||||||
|
*/
|
||||||
|
protected $m_aGroupByExpr;
|
||||||
|
/**
|
||||||
|
* @var Expression[]
|
||||||
|
*/
|
||||||
|
protected $m_aJoinFields;
|
||||||
|
/**
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $m_aClassIds;
|
||||||
|
|
||||||
|
public function __construct(DBObjectSearch $oSearch, $aGroupByExpr = null, $aSelectExpr = null)
|
||||||
|
{
|
||||||
|
$this->m_oConditionExpr = $oSearch->GetCriteria();
|
||||||
|
if (!$oSearch->GetShowObsoleteData())
|
||||||
|
{
|
||||||
|
foreach ($oSearch->GetSelectedClasses() as $sAlias => $sClass)
|
||||||
|
{
|
||||||
|
if (MetaModel::IsObsoletable($sClass))
|
||||||
|
{
|
||||||
|
$oNotObsolete = new BinaryExpression(new FieldExpression('obsolescence_flag', $sAlias), '=', new ScalarExpression(0));
|
||||||
|
$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oNotObsolete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->m_aSelectExpr = is_null($aSelectExpr) ? array() : $aSelectExpr;
|
||||||
|
$this->m_aGroupByExpr = $aGroupByExpr;
|
||||||
|
$this->m_aJoinFields = array();
|
||||||
|
|
||||||
|
$this->m_aClassIds = array();
|
||||||
|
foreach($oSearch->GetJoinedClasses() as $sClassAlias => $sClass)
|
||||||
|
{
|
||||||
|
$this->m_aClassIds[$sClassAlias] = new FieldExpression('id', $sClassAlias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetSelect()
|
||||||
|
{
|
||||||
|
return $this->m_aSelectExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetGroupBy()
|
||||||
|
{
|
||||||
|
return $this->m_aGroupByExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetCondition()
|
||||||
|
{
|
||||||
|
return $this->m_oConditionExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Expression|mixed
|
||||||
|
*/
|
||||||
|
public function PopJoinField()
|
||||||
|
{
|
||||||
|
return array_pop($this->m_aJoinFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sAttAlias
|
||||||
|
* @param Expression $oExpression
|
||||||
|
*/
|
||||||
|
public function AddSelect($sAttAlias, Expression $oExpression)
|
||||||
|
{
|
||||||
|
$this->m_aSelectExpr[$sAttAlias] = $oExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Expression $oExpression
|
||||||
|
*/
|
||||||
|
public function AddCondition(Expression $oExpression)
|
||||||
|
{
|
||||||
|
$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Expression $oExpression
|
||||||
|
*/
|
||||||
|
public function PushJoinField(Expression $oExpression)
|
||||||
|
{
|
||||||
|
array_push($this->m_aJoinFields, $oExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get tables representing the queried objects
|
||||||
|
* Could be further optimized: when the first join is an outer join, then the rest can be omitted
|
||||||
|
* @param array $aTables
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function GetMandatoryTables(&$aTables = null)
|
||||||
|
{
|
||||||
|
if (is_null($aTables)) $aTables = array();
|
||||||
|
|
||||||
|
foreach($this->m_aClassIds as $sClass => $oExpression)
|
||||||
|
{
|
||||||
|
$oExpression->CollectUsedParents($aTables);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aTables;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetUnresolvedFields($sAlias, &$aUnresolved)
|
||||||
|
{
|
||||||
|
$this->m_oConditionExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||||
|
foreach($this->m_aSelectExpr as $sColAlias => $oExpr)
|
||||||
|
{
|
||||||
|
$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||||
|
}
|
||||||
|
if ($this->m_aGroupByExpr)
|
||||||
|
{
|
||||||
|
foreach($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
||||||
|
{
|
||||||
|
$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach($this->m_aJoinFields as $oExpression)
|
||||||
|
{
|
||||||
|
$oExpression->GetUnresolvedFields($sAlias, $aUnresolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
||||||
|
{
|
||||||
|
$this->m_oConditionExpr = $this->m_oConditionExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||||
|
foreach($this->m_aSelectExpr as $sColAlias => $oExpr)
|
||||||
|
{
|
||||||
|
$this->m_aSelectExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||||
|
}
|
||||||
|
if ($this->m_aGroupByExpr)
|
||||||
|
{
|
||||||
|
foreach($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
||||||
|
{
|
||||||
|
$this->m_aGroupByExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach($this->m_aJoinFields as $index => $oExpression)
|
||||||
|
{
|
||||||
|
$this->m_aJoinFields[$index] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($this->m_aClassIds as $sClass => $oExpression)
|
||||||
|
{
|
||||||
|
$this->m_aClassIds[$sClass] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function RenameParam($sOldName, $sNewName)
|
||||||
|
{
|
||||||
|
$this->m_oConditionExpr->RenameParam($sOldName, $sNewName);
|
||||||
|
foreach($this->m_aSelectExpr as $sColAlias => $oExpr)
|
||||||
|
{
|
||||||
|
$this->m_aSelectExpr[$sColAlias] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||||
|
}
|
||||||
|
if ($this->m_aGroupByExpr)
|
||||||
|
{
|
||||||
|
foreach($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
||||||
|
{
|
||||||
|
$this->m_aGroupByExpr[$sColAlias] = $oExpr->RenameParam($sOldName, $sNewName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach($this->m_aJoinFields as $index => $oExpression)
|
||||||
|
{
|
||||||
|
$this->m_aJoinFields[$index] = $oExpression->RenameParam($sOldName, $sNewName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
class OQLActualClassTreeResolver
|
|
||||||
{
|
|
||||||
/** @var OQLClassNode */
|
|
||||||
private $oOQLClassNode;
|
|
||||||
/** @var QueryBuilderContext */
|
|
||||||
private $oBuild;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OQLActualClassTreeResolver constructor.
|
|
||||||
*
|
|
||||||
* @param OQLClassNode $oOQLClassNode
|
|
||||||
* @param QueryBuilderContext $oBuild
|
|
||||||
* @param array $aJoinedAliases
|
|
||||||
*/
|
|
||||||
public function __construct($oOQLClassNode, $oBuild)
|
|
||||||
{
|
|
||||||
$this->oOQLClassNode = $oOQLClassNode;
|
|
||||||
$this->oBuild = $oBuild;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign attributes on their original classes
|
|
||||||
*
|
|
||||||
* @param string $sIncomingKeyAttCode Key used for the join (entry point of the class)
|
|
||||||
*
|
|
||||||
* @return \OQLClassNode
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function Resolve($sIncomingKeyAttCode = null)
|
|
||||||
{
|
|
||||||
$sClass = $this->oOQLClassNode->GetNodeClass();
|
|
||||||
$sClassAlias = $this->oOQLClassNode->GetNodeClassAlias();
|
|
||||||
$aExpectedAttributes = $this->oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias);
|
|
||||||
if (!is_null($sIncomingKeyAttCode) && !isset($aExpectedAttributes[$sIncomingKeyAttCode]))
|
|
||||||
{
|
|
||||||
// Add entry point as expected attribute
|
|
||||||
$aExpectedAttributes[$sIncomingKeyAttCode] = new FieldExpression($sIncomingKeyAttCode, $sClassAlias);
|
|
||||||
}
|
|
||||||
$aClasses = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, false);
|
|
||||||
/** @var OQLClassNode[] $aClassAndAncestorsNodes */
|
|
||||||
$aClassAndAncestorsNodes = array();
|
|
||||||
foreach ($aClasses as $sFamilyClass)
|
|
||||||
{
|
|
||||||
// Remove unnecessary classes
|
|
||||||
if (MetaModel::HasTable($sFamilyClass))
|
|
||||||
{
|
|
||||||
$aClassAndAncestorsNodes[$sFamilyClass] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($aClassAndAncestorsNodes))
|
|
||||||
{
|
|
||||||
throw new CoreException("Impossible to query the class $sClass");
|
|
||||||
}
|
|
||||||
|
|
||||||
$oBaseNode = null;
|
|
||||||
$aTranslateFields = array();
|
|
||||||
foreach ($aExpectedAttributes as $sAttCode => $oExpression)
|
|
||||||
{
|
|
||||||
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
|
|
||||||
if (is_null($aClassAndAncestorsNodes[$sOriginClass]))
|
|
||||||
{
|
|
||||||
if ($sOriginClass == $sClass)
|
|
||||||
{
|
|
||||||
$sOriginClassAlias = $sClassAlias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sOriginClassAlias = $this->oBuild->GenerateTableAlias($sClassAlias.'_'.$sOriginClass, $sClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
$oOriginClassNode = new OQLClassNode($this->oBuild, $sOriginClass, $sOriginClassAlias, $sClassAlias);
|
|
||||||
$aClassAndAncestorsNodes[$sOriginClass] = $oOriginClassNode;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oOriginClassNode = $aClassAndAncestorsNodes[$sOriginClass];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($sOriginClass != $sClass)
|
|
||||||
{
|
|
||||||
// Alias changed, set a new translation
|
|
||||||
$sOriginClassAlias = $oOriginClassNode->GetNodeClassAlias();
|
|
||||||
$aTranslateFields[$sClassAlias][$sAttCode] = new FieldExpression($sAttCode, $sOriginClassAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add Joins corresponding to external keys
|
|
||||||
$this->ResolveJoins($sAttCode, $oOriginClassNode);
|
|
||||||
|
|
||||||
if ($sAttCode === $sIncomingKeyAttCode)
|
|
||||||
{
|
|
||||||
// This is the entry point of the class
|
|
||||||
$oBaseNode = $oOriginClassNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create joins for ancestor classes
|
|
||||||
/** @var \OQLClassNode $oBaseNode */
|
|
||||||
$sFirstValidAncestor = null;
|
|
||||||
foreach ($aClassAndAncestorsNodes as $sOriginClass => $oOriginClassNode)
|
|
||||||
{
|
|
||||||
if (is_null($sFirstValidAncestor))
|
|
||||||
{
|
|
||||||
$sFirstValidAncestor = $sOriginClass;
|
|
||||||
}
|
|
||||||
if (is_null($oOriginClassNode))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (is_null($oBaseNode))
|
|
||||||
{
|
|
||||||
$oBaseNode = $oOriginClassNode;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($oBaseNode === $oOriginClassNode)
|
|
||||||
{
|
|
||||||
// Don't link to itself
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$oBaseNode->AddInnerJoin($oOriginClassNode, 'id', 'id');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_null($oBaseNode))
|
|
||||||
{
|
|
||||||
// If no class was generated above, keep the first valid ancestor
|
|
||||||
if (is_null($sFirstValidAncestor) || ($sFirstValidAncestor == $sClass))
|
|
||||||
{
|
|
||||||
// take current node
|
|
||||||
$oBaseNode = $this->oOQLClassNode->CloneNode();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Use the first valid class to build a default node
|
|
||||||
$sDefaultClassAlias = $this->oBuild->GenerateTableAlias($sClassAlias.'_'.$sFirstValidAncestor, $sClass);
|
|
||||||
$oBaseNode = new OQLClassNode($this->oBuild, $sFirstValidAncestor, $sDefaultClassAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aExpectedAttributes['id']) && !isset($aClassAndAncestorsNodes[$sClass]))
|
|
||||||
{
|
|
||||||
$sFirstClassAlias = $oBaseNode->GetNodeClassAlias();
|
|
||||||
$aTranslateFields[$sClassAlias]['id'] = new FieldExpression('id', $sFirstClassAlias);
|
|
||||||
}
|
|
||||||
$this->oBuild->m_oQBExpressions->Translate($aTranslateFields, false);
|
|
||||||
|
|
||||||
// Add Joins corresponding to 'id'
|
|
||||||
$this->ResolveJoins('id', $oBaseNode);
|
|
||||||
|
|
||||||
// Add finalclass condition if not the requested class
|
|
||||||
if ($oBaseNode->GetNodeClass() != $sClass)
|
|
||||||
{
|
|
||||||
$sExpectedClasses = implode("', '", MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
|
|
||||||
$oInExpression = Expression::FromOQL("`".$oBaseNode->GetNodeClassAlias()."`.finalclass IN ('$sExpectedClasses')");
|
|
||||||
$oTrueExpression = new TrueExpression();
|
|
||||||
$aCoalesceAttr = array($oInExpression, $oTrueExpression);
|
|
||||||
$oFinalClassRestriction = new FunctionExpression("COALESCE", $aCoalesceAttr);
|
|
||||||
$this->oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $oBaseNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move the joins from the selected class to the class where the external key is instantiated
|
|
||||||
* The joined class is also resolved using the right key as entry point
|
|
||||||
*
|
|
||||||
* @param string $sAttCode (can be an external key)
|
|
||||||
* @param \OQLClassNode $oOriginClassNode real class to join
|
|
||||||
*
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private function ResolveJoins($sAttCode, OQLClassNode $oOriginClassNode)
|
|
||||||
{
|
|
||||||
// Joins on the selected class
|
|
||||||
$aJoins = $this->oOQLClassNode->GetJoins();
|
|
||||||
|
|
||||||
if (isset($aJoins[$sAttCode]))
|
|
||||||
{
|
|
||||||
foreach ($aJoins[$sAttCode] as $oBaseOQLJoin)
|
|
||||||
{
|
|
||||||
// transfer the join from OQL class tree to actual class tree
|
|
||||||
$oBaseJoinedClassNode = $oBaseOQLJoin->GetOOQLClassNode();
|
|
||||||
$oOQLActualClassTreeResolver = new OQLActualClassTreeResolver($oBaseJoinedClassNode, $this->oBuild);
|
|
||||||
// Use the right key to link to actual join class tree
|
|
||||||
$oResolvedClassNode = $oOQLActualClassTreeResolver->Resolve($oBaseOQLJoin->GetRightField());
|
|
||||||
$oOriginClassNode->AddOQLJoin($sAttCode, $oBaseOQLJoin->NewOQLJoinWithClassNode($oResolvedClassNode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,321 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
class OQLClassNode
|
|
||||||
{
|
|
||||||
private $sNodeClass;
|
|
||||||
private $sNodeClassAlias;
|
|
||||||
/** @var string Class alias coming from OQL */
|
|
||||||
private $sOQLClassAlias;
|
|
||||||
/** @var OQLJoin[][] */
|
|
||||||
private $aJoins;
|
|
||||||
private $aExtKeys;
|
|
||||||
private $oBuild;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OQLClassNode constructor.
|
|
||||||
*
|
|
||||||
* @param QueryBuilderContext $oBuild
|
|
||||||
* @param string $sNodeClass Current node class
|
|
||||||
* @param string $sNodeClassAlias Current node class alias
|
|
||||||
* @param string $sOQLClassAlias Alias of the class requested in the filter (defaulted to $sClassAlias if null)
|
|
||||||
*/
|
|
||||||
public function __construct($oBuild, $sNodeClass, $sNodeClassAlias, $sOQLClassAlias = null)
|
|
||||||
{
|
|
||||||
$this->sNodeClass = $sNodeClass;
|
|
||||||
if (empty($sNodeClassAlias))
|
|
||||||
{
|
|
||||||
$this->sNodeClassAlias = $oBuild->GetEmptyClassAlias();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->sNodeClassAlias = $sNodeClassAlias;
|
|
||||||
}
|
|
||||||
$this->aJoins = array();
|
|
||||||
$this->aExtKeys = array();
|
|
||||||
if (is_null($sOQLClassAlias))
|
|
||||||
{
|
|
||||||
$this->sOQLClassAlias = $this->sNodeClassAlias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->sOQLClassAlias = $sOQLClassAlias;
|
|
||||||
}
|
|
||||||
$this->oBuild = $oBuild;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* clone without joins
|
|
||||||
*
|
|
||||||
* @return \OQLClassNode
|
|
||||||
*/
|
|
||||||
public function CloneNode()
|
|
||||||
{
|
|
||||||
return new self($this->oBuild, $this->sNodeClass, $this->sNodeClassAlias, $this->sOQLClassAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddExternalKey($sKeyAttCode)
|
|
||||||
{
|
|
||||||
if (!isset($this->aExtKeys[$sKeyAttCode]))
|
|
||||||
{
|
|
||||||
$this->aExtKeys[$sKeyAttCode] = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddExternalField($sKeyAttCode, $sFieldAttCode, $oAttDef)
|
|
||||||
{
|
|
||||||
$this->AddExternalKey($sKeyAttCode);
|
|
||||||
$this->aExtKeys[$sKeyAttCode][$sFieldAttCode] = $oAttDef;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function AddInnerJoin($oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true)
|
|
||||||
{
|
|
||||||
$this->AddJoin(OQLJoin::JOIN_INNER, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddLeftJoin($oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true)
|
|
||||||
{
|
|
||||||
$this->AddJoin(OQLJoin::JOIN_LEFT, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddInnerJoinTree($oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true, $iOperatorCode = TREE_OPERATOR_BELOW, $bInvertOnClause = false)
|
|
||||||
{
|
|
||||||
$this->AddJoin(OQLJoin::JOIN_INNER_TREE, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound, $iOperatorCode, $bInvertOnClause);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function AddJoin($sJoinType, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true, $sTreeOperator = null, $bInvertOnClause = false)
|
|
||||||
{
|
|
||||||
$oOQLJoin = new OQLJoin($this->oBuild, $sJoinType, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound, $sTreeOperator,
|
|
||||||
$bInvertOnClause);
|
|
||||||
$this->AddOQLJoin($sLeftField, $oOQLJoin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $sLeftField
|
|
||||||
* @param OQLJoin $oOQLJoin
|
|
||||||
*/
|
|
||||||
public function AddOQLJoin($sLeftField, $oOQLJoin)
|
|
||||||
{
|
|
||||||
// Record Left join field expression
|
|
||||||
// (right join field expression is recorded in OQLJoin)
|
|
||||||
$sJoinFieldName = $this->sNodeClassAlias.'.'.$sLeftField;
|
|
||||||
$this->oBuild->m_oQBExpressions->AddJoinField($sJoinFieldName, new FieldExpression($sLeftField, $this->sNodeClassAlias));
|
|
||||||
|
|
||||||
$this->aJoins[$sLeftField][] = $oOQLJoin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function DisplayHtml()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function RenderDebug()
|
|
||||||
{
|
|
||||||
$sOQL = "SELECT `{$this->sNodeClassAlias}` FROM `{$this->sNodeClass}` AS `{$this->sNodeClassAlias}`";
|
|
||||||
foreach ($this->aJoins as $aJoins)
|
|
||||||
{
|
|
||||||
foreach ($aJoins as $oJoin)
|
|
||||||
{
|
|
||||||
$sOQL .= "{$oJoin->RenderDebug($this->sNodeClassAlias)}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $sOQL;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetExternalKeys()
|
|
||||||
{
|
|
||||||
return $this->aExtKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function HasExternalKey($sAttCode)
|
|
||||||
{
|
|
||||||
return array_key_exists($sAttCode, $this->aExtKeys);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetExternalKey($sAttCode)
|
|
||||||
{
|
|
||||||
return $this->aExtKeys[$sAttCode];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetNodeClass()
|
|
||||||
{
|
|
||||||
return $this->sNodeClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetNodeClassAlias()
|
|
||||||
{
|
|
||||||
return $this->sNodeClassAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function GetOQLClassAlias()
|
|
||||||
{
|
|
||||||
return $this->sOQLClassAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetJoins()
|
|
||||||
{
|
|
||||||
return $this->aJoins;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function RemoveJoin($sLeftKey, $index)
|
|
||||||
{
|
|
||||||
unset($this->aJoins[$sLeftKey][$index]);
|
|
||||||
if (empty($this->aJoins[$sLeftKey]))
|
|
||||||
{
|
|
||||||
unset($this->aJoins[$sLeftKey]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class OQLJoin
|
|
||||||
{
|
|
||||||
const JOIN_INNER = 'inner';
|
|
||||||
const JOIN_LEFT = 'left';
|
|
||||||
const JOIN_INNER_TREE = 'inner_tree';
|
|
||||||
|
|
||||||
private $sJoinType;
|
|
||||||
/** @var \OQLClassNode */
|
|
||||||
private $oOQLClassNode;
|
|
||||||
|
|
||||||
private $bOutbound;
|
|
||||||
private $sLeftField;
|
|
||||||
private $sRightField;
|
|
||||||
private $sTreeOperator;
|
|
||||||
private $bInvertOnClause;
|
|
||||||
private $oBuild;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OQLJoin constructor.
|
|
||||||
*
|
|
||||||
* @param QueryBuilderContext $oBuild
|
|
||||||
* @param string $sJoinType
|
|
||||||
* @param OQLClassNode $oOQLClassNode
|
|
||||||
* @param string $sLeftField
|
|
||||||
* @param string $sRightField
|
|
||||||
* @param bool $bOutbound
|
|
||||||
* @param string $sTreeOperator
|
|
||||||
* @param bool $bInvertOnClause
|
|
||||||
*/
|
|
||||||
public function __construct($oBuild, $sJoinType, $oOQLClassNode, $sLeftField, $sRightField, $bOutbound = true, $sTreeOperator = null, $bInvertOnClause = false)
|
|
||||||
{
|
|
||||||
// Record right join field expression
|
|
||||||
// (left join field expression is recorded in OQLClassNode)
|
|
||||||
$sJoinFieldName = $oOQLClassNode->GetNodeClassAlias().'.'.$sRightField;
|
|
||||||
$oBuild->m_oQBExpressions->AddJoinField($sJoinFieldName, new FieldExpression($sRightField, $oOQLClassNode->GetNodeClassAlias()));
|
|
||||||
|
|
||||||
$this->sJoinType = $sJoinType;
|
|
||||||
$this->oOQLClassNode = $oOQLClassNode;
|
|
||||||
$this->sLeftField = $sLeftField;
|
|
||||||
$this->sRightField = $sRightField;
|
|
||||||
$this->sTreeOperator = $sTreeOperator;
|
|
||||||
$this->bInvertOnClause = $bInvertOnClause;
|
|
||||||
$this->bOutbound = $bOutbound;
|
|
||||||
$this->oBuild = $oBuild;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function NewOQLJoinWithClassNode($oOQLClassNode)
|
|
||||||
{
|
|
||||||
return new self($this->oBuild, $this->sJoinType, $oOQLClassNode, $this->sLeftField, $this->sRightField, $this->bOutbound,
|
|
||||||
$this->sTreeOperator, $this->bInvertOnClause);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param QueryBuilderContext $oBuild
|
|
||||||
* @param SQLObjectQuery $oBaseSQLQuery
|
|
||||||
* @param SQLObjectQuery $oJoinedSQLQuery
|
|
||||||
*/
|
|
||||||
public function AddToSQLObjectQuery($oBuild, $oBaseSQLQuery, $oJoinedSQLQuery)
|
|
||||||
{
|
|
||||||
// Translate the fields before copy to SQL
|
|
||||||
$sLeft = $oBaseSQLQuery->GetTableAlias().'.'.$this->sLeftField;
|
|
||||||
$oLeftField = $oBuild->m_oQBExpressions->GetJoinField($sLeft);
|
|
||||||
if ($oLeftField)
|
|
||||||
{
|
|
||||||
$sSQLLeft = $oLeftField->GetName();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sSQLLeft = "no_field_found_for_$sLeft";
|
|
||||||
}
|
|
||||||
$sRight = $oJoinedSQLQuery->GetTableAlias().'.'.$this->sRightField;
|
|
||||||
$oRightField = $oBuild->m_oQBExpressions->GetJoinField($sRight);
|
|
||||||
if ($oRightField)
|
|
||||||
{
|
|
||||||
$sSQLRight = $oRightField->GetName();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sSQLRight = "no_field_found_for_$sRight";
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($this->sJoinType)
|
|
||||||
{
|
|
||||||
case self::JOIN_INNER:
|
|
||||||
$oBaseSQLQuery->AddInnerJoin($oJoinedSQLQuery, $sSQLLeft, $sSQLRight);
|
|
||||||
break;
|
|
||||||
case self::JOIN_LEFT:
|
|
||||||
$oBaseSQLQuery->AddLeftJoin($oJoinedSQLQuery, $sSQLLeft, $sSQLRight);
|
|
||||||
break;
|
|
||||||
case self::JOIN_INNER_TREE:
|
|
||||||
$sLeftFieldLeft = $sSQLLeft.'_left';
|
|
||||||
$sLeftFieldRight = $sSQLLeft.'_right';
|
|
||||||
$sRightFieldLeft = $sSQLRight.'_left';
|
|
||||||
$sRightFieldRight = $sSQLRight.'_right';
|
|
||||||
$sRightTableAlias = $this->oOQLClassNode->GetNodeClassAlias();
|
|
||||||
$oBaseSQLQuery->AddInnerJoinTree($oJoinedSQLQuery, $sLeftFieldLeft, $sLeftFieldRight, $sRightFieldLeft, $sRightFieldRight, $sRightTableAlias, $this->sTreeOperator, $this->bInvertOnClause);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function RenderDebug($sClassAlias, $sPrefix = " ")
|
|
||||||
{
|
|
||||||
$sType = strtoupper($this->sJoinType);
|
|
||||||
$sOQL = "\n{$sPrefix}{$sType} JOIN `{$this->oOQLClassNode->GetNodeClass()}` AS `{$this->oOQLClassNode->GetNodeClassAlias()}`";
|
|
||||||
$sOQL .= "\n{$sPrefix} ON `{$sClassAlias}`.`{$this->sLeftField}` = `{$this->oOQLClassNode->GetNodeClassAlias()}`.`{$this->sRightField}`";
|
|
||||||
$sPrefix .= " ";
|
|
||||||
foreach ($this->oOQLClassNode->GetJoins() as $aJoins)
|
|
||||||
{
|
|
||||||
foreach ($aJoins as $oJoin)
|
|
||||||
{
|
|
||||||
$sOQL .= " {$oJoin->RenderDebug($this->oOQLClassNode->GetNodeClassAlias(), $sPrefix)}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $sOQL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \OQLClassNode
|
|
||||||
*/
|
|
||||||
public function GetOOQLClassNode()
|
|
||||||
{
|
|
||||||
return $this->oOQLClassNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function IsOutbound()
|
|
||||||
{
|
|
||||||
return $this->bOutbound;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function GetRightField()
|
|
||||||
{
|
|
||||||
return $this->sRightField;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,378 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
class OQLClassTreeBuilder
|
|
||||||
{
|
|
||||||
/** @var \DBObjectSearch */
|
|
||||||
private $oDBObjetSearch;
|
|
||||||
/** @var OQLClassNode */
|
|
||||||
private $oOQLClassNode;
|
|
||||||
/** @var \QueryBuilderContext */
|
|
||||||
private $oBuild;
|
|
||||||
|
|
||||||
private $sClass;
|
|
||||||
private $sClassAlias;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OQLClassTreeBuilder constructor.
|
|
||||||
*
|
|
||||||
* @param \DBObjectSearch $oDBObjetSearch
|
|
||||||
* @param \QueryBuilderContext $oBuild
|
|
||||||
*/
|
|
||||||
public function __construct($oDBObjetSearch, $oBuild)
|
|
||||||
{
|
|
||||||
$this->oBuild = $oBuild;
|
|
||||||
$this->oDBObjetSearch = $oDBObjetSearch;
|
|
||||||
$this->sClass = $oDBObjetSearch->GetFirstJoinedClass();
|
|
||||||
$this->sClassAlias = $oDBObjetSearch->GetFirstJoinedClassAlias();
|
|
||||||
if (empty($this->sClassAlias))
|
|
||||||
{
|
|
||||||
$this->sClassAlias = $oBuild->GetEmptyClassAlias();
|
|
||||||
}
|
|
||||||
$this->oOQLClassNode = new OQLClassNode($oBuild, $this->sClass, $this->sClassAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Develop OQL.
|
|
||||||
* Add joins from OQL (outgoing and incoming)
|
|
||||||
* Add joins for polymorphic expressions (expressions using derived classes
|
|
||||||
* instead of ancestor classes, i.e. friendly name and obsolescence flag)
|
|
||||||
* Add joins for expected external keys and external fields
|
|
||||||
* Behave recursively to build a tree of OQL class node
|
|
||||||
*
|
|
||||||
* @return \OQLClassNode
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function DevelopOQLClassNode()
|
|
||||||
{
|
|
||||||
$this->AddExternalKeysFromSearch();
|
|
||||||
$aPolymorphicJoinAlias = $this->TranslatePolymorphicExpressions();
|
|
||||||
$this->AddExpectedExternalFields();
|
|
||||||
|
|
||||||
$this->JoinClassesForExternalKeys();
|
|
||||||
$this->JoinClassesReferencedBy();
|
|
||||||
$this->JoinClassesForPolymorphicExpressions($aPolymorphicJoinAlias);
|
|
||||||
|
|
||||||
// That's all... cross fingers and we'll get some working query
|
|
||||||
|
|
||||||
return $this->oOQLClassNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all Ext keys used by the filter
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private function AddExternalKeysFromSearch()
|
|
||||||
{
|
|
||||||
foreach ($this->oDBObjetSearch->GetCriteria_PointingTo() as $sKeyAttCode => $aPointingTo)
|
|
||||||
{
|
|
||||||
if (array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo))
|
|
||||||
{
|
|
||||||
$this->oOQLClassNode->AddExternalKey($sKeyAttCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @return array of classes to join for polymorphic expressions
|
|
||||||
*
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private function TranslatePolymorphicExpressions()
|
|
||||||
{
|
|
||||||
// array of (attcode => fieldexpression)
|
|
||||||
$aExpectedAttributes = $this->oBuild->m_oQBExpressions->GetUnresolvedFields($this->sClassAlias);
|
|
||||||
|
|
||||||
$aPolymorphicJoinAlias = array(); // array of (subclass => alias)
|
|
||||||
foreach ($aExpectedAttributes as $sExpectedAttCode => $oExpression)
|
|
||||||
{
|
|
||||||
if (!MetaModel::IsValidAttCode($this->sClass, $sExpectedAttCode))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($this->sClass, $sExpectedAttCode);
|
|
||||||
if ($oAttDef->IsBasedOnOQLExpression())
|
|
||||||
{
|
|
||||||
// To optimize: detect a restriction on child classes in the condition expression
|
|
||||||
// e.g. SELECT FunctionalCI WHERE finalclass IN ('Server', 'VirtualMachine')
|
|
||||||
$oExpression = DBObjectSearch::GetPolymorphicExpression($this->sClass, $sExpectedAttCode);
|
|
||||||
|
|
||||||
$aRequiredFields = array();
|
|
||||||
$oExpression->GetUnresolvedFields('', $aRequiredFields);
|
|
||||||
$aTranslateFields = array();
|
|
||||||
foreach ($aRequiredFields as $sSubClass => $aFields)
|
|
||||||
{
|
|
||||||
foreach ($aFields as $sAttCode => $oField)
|
|
||||||
{
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode);
|
|
||||||
if ($oAttDef->IsExternalKey())
|
|
||||||
{
|
|
||||||
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
|
|
||||||
if (MetaModel::IsParentClass($sClassOfAttribute, $this->sClass))
|
|
||||||
{
|
|
||||||
$this->oOQLClassNode->AddExternalKey($sAttCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif ($oAttDef->IsExternalField())
|
|
||||||
{
|
|
||||||
$sKeyAttCode = $oAttDef->GetKeyAttCode();
|
|
||||||
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode);
|
|
||||||
if (MetaModel::IsParentClass($sClassOfAttribute, $this->sClass))
|
|
||||||
{
|
|
||||||
$this->oOQLClassNode->AddExternalField($sKeyAttCode, $sAttCode, $oAttDef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MetaModel::IsParentClass($sClassOfAttribute, $this->sClass))
|
|
||||||
{
|
|
||||||
// The attribute is part of the standard query
|
|
||||||
//
|
|
||||||
$sAliasForAttribute = $this->sClassAlias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The attribute will be available from an additional outer join
|
|
||||||
// For each subclass (table) one single join is enough
|
|
||||||
//
|
|
||||||
if (!array_key_exists($sClassOfAttribute, $aPolymorphicJoinAlias))
|
|
||||||
{
|
|
||||||
$sAliasForAttribute = $this->oBuild->GenerateClassAlias($this->sClassAlias.'_poly_'.$sClassOfAttribute,
|
|
||||||
$sClassOfAttribute);
|
|
||||||
$aPolymorphicJoinAlias[$sClassOfAttribute] = $sAliasForAttribute;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sAliasForAttribute = $aPolymorphicJoinAlias[$sClassOfAttribute];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$aTranslateFields[$sSubClass][$sAttCode] = new FieldExpression($sAttCode, $sAliasForAttribute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$oExpression = $oExpression->Translate($aTranslateFields, false);
|
|
||||||
|
|
||||||
$aTranslateNow = array();
|
|
||||||
$aTranslateNow[$this->sClassAlias][$sExpectedAttCode] = $oExpression;
|
|
||||||
$this->oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aPolymorphicJoinAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the ext fields used in the select (external keys may be created for that)
|
|
||||||
*
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private function AddExpectedExternalFields()
|
|
||||||
{
|
|
||||||
// array of (attcode => fieldexpression)
|
|
||||||
$aExpectedAttributes = $this->oBuild->m_oQBExpressions->GetUnresolvedFields($this->sClassAlias);
|
|
||||||
|
|
||||||
foreach (MetaModel::ListAttributeDefs($this->sClass) as $sAttCode => $oAttDef)
|
|
||||||
{
|
|
||||||
if ($oAttDef->IsExternalField())
|
|
||||||
{
|
|
||||||
if (array_key_exists($sAttCode, $aExpectedAttributes))
|
|
||||||
{
|
|
||||||
// Add the external attribute
|
|
||||||
$sKeyAttCode = $oAttDef->GetKeyAttCode();
|
|
||||||
$this->oOQLClassNode->AddExternalField($sKeyAttCode, $sAttCode, $oAttDef);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add joins for external keys/fields
|
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
private function JoinClassesForExternalKeys()
|
|
||||||
{
|
|
||||||
// Get filters from the search outgoing joins
|
|
||||||
$aAllPointingTo = $this->oDBObjetSearch->GetCriteria_PointingTo();
|
|
||||||
|
|
||||||
// Add filters from external keys
|
|
||||||
foreach (array_keys($this->oOQLClassNode->GetExternalKeys()) as $sKeyAttCode)
|
|
||||||
{
|
|
||||||
if (!MetaModel::IsValidAttCode($this->sClass, $sKeyAttCode))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
} // Not defined in the class, skip it
|
|
||||||
$oKeyAttDef = MetaModel::GetAttributeDef($this->sClass, $sKeyAttCode);
|
|
||||||
$aPointingTo = isset($aAllPointingTo[$sKeyAttCode]) ? $aAllPointingTo[$sKeyAttCode] : array();
|
|
||||||
if (!array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo))
|
|
||||||
{
|
|
||||||
// The join was not explicitly defined in the filter,
|
|
||||||
// we need to do it now
|
|
||||||
$sKeyClass = $oKeyAttDef->GetTargetClass();
|
|
||||||
$sKeyClassAlias = $this->oBuild->GenerateClassAlias($sKeyClass.'_'.$sKeyAttCode, $sKeyClass);
|
|
||||||
$oExtFilter = new DBObjectSearch($sKeyClass, $sKeyClassAlias);
|
|
||||||
|
|
||||||
$aAllPointingTo[$sKeyAttCode][TREE_OPERATOR_EQUALS][$sKeyClassAlias] = $oExtFilter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$oQBContextExpressions = $this->oBuild->m_oQBExpressions;
|
|
||||||
foreach ($aAllPointingTo as $sKeyAttCode => $aPointingTo)
|
|
||||||
{
|
|
||||||
foreach ($aPointingTo as $iOperatorCode => $aFilter)
|
|
||||||
{
|
|
||||||
foreach ($aFilter as $oExtFilter)
|
|
||||||
{
|
|
||||||
if (!MetaModel::IsValidAttCode($this->sClass, $sKeyAttCode))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
} // Not defined in the class, skip it
|
|
||||||
// The aliases should not conflict because normalization occurred while building the filter
|
|
||||||
$oKeyAttDef = MetaModel::GetAttributeDef($this->sClass, $sKeyAttCode);
|
|
||||||
$sKeyClass = $oExtFilter->GetFirstJoinedClass();
|
|
||||||
$sKeyClassAlias = $oExtFilter->GetFirstJoinedClassAlias();
|
|
||||||
|
|
||||||
// Note: there is no search condition in $oExtFilter, because normalization did merge the condition onto the top of the filter tree
|
|
||||||
|
|
||||||
if ($iOperatorCode == TREE_OPERATOR_EQUALS)
|
|
||||||
{
|
|
||||||
if ($this->oOQLClassNode->HasExternalKey($sKeyAttCode))
|
|
||||||
{
|
|
||||||
// Specify expected attributes for the target class query
|
|
||||||
// ... and use the current alias !
|
|
||||||
$aTranslateNow = array(); // Translation for external fields - must be performed before the join is done (recursion...)
|
|
||||||
foreach ($this->oOQLClassNode->GetExternalKey($sKeyAttCode) as $sAttCode => $oAtt)
|
|
||||||
{
|
|
||||||
$oExtAttDef = $oAtt->GetExtAttDef();
|
|
||||||
if ($oExtAttDef->IsBasedOnOQLExpression())
|
|
||||||
{
|
|
||||||
$sExtAttCode = $oExtAttDef->GetCode();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sExtAttCode = $oAtt->GetExtAttCode();
|
|
||||||
}
|
|
||||||
// Translate mainclass.extfield => remoteclassalias.remotefieldcode
|
|
||||||
$aTranslateNow[$this->sClassAlias][$sAttCode] = new FieldExpression($sExtAttCode, $sKeyClassAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($oKeyAttDef instanceof AttributeObjectKey)
|
|
||||||
{
|
|
||||||
// Add the condition: `$sTargetAlias`.$sClassAttCode IN (subclasses of $sKeyClass')
|
|
||||||
$sClassAttCode = $oKeyAttDef->Get('class_attcode');
|
|
||||||
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sKeyClass,
|
|
||||||
ENUM_CHILD_CLASSES_ALL));
|
|
||||||
$oClassExpr = new FieldExpression($sClassAttCode, $this->sClassAlias);
|
|
||||||
$oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
|
|
||||||
$oQBContextExpressions->AddCondition($oClassRestriction);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate prior to recursing
|
|
||||||
//
|
|
||||||
$oQBContextExpressions->Translate($aTranslateNow, false);
|
|
||||||
$sExternalKeyField = 'id';
|
|
||||||
|
|
||||||
$oOQLClassTreeBuilder = new OQLClassTreeBuilder($oExtFilter, $this->oBuild);
|
|
||||||
$oSelectExtKey = $oOQLClassTreeBuilder->DevelopOQLClassNode();
|
|
||||||
|
|
||||||
if ($oKeyAttDef->IsNullAllowed())
|
|
||||||
{
|
|
||||||
$this->oOQLClassNode->AddLeftJoin($oSelectExtKey, $sKeyAttCode, $sExternalKeyField, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$this->oOQLClassNode->AddInnerJoin($oSelectExtKey, $sKeyAttCode, $sExternalKeyField, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif (MetaModel::GetAttributeOrigin($sKeyClass, $sKeyAttCode) == $this->sClass)
|
|
||||||
{
|
|
||||||
$sExternalKeyField = $sKeyAttCode;
|
|
||||||
|
|
||||||
$oOQLClassTreeBuilder = new OQLClassTreeBuilder($oExtFilter, $this->oBuild);
|
|
||||||
$oSelectExtKey = $oOQLClassTreeBuilder->DevelopOQLClassNode();
|
|
||||||
|
|
||||||
|
|
||||||
$this->oOQLClassNode->AddInnerJoinTree($oSelectExtKey, $sKeyAttCode, $sExternalKeyField, true, $iOperatorCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter on objects referencing me
|
|
||||||
*
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private function JoinClassesReferencedBy()
|
|
||||||
{
|
|
||||||
foreach ($this->oDBObjetSearch->GetCriteria_ReferencedBy() as $sForeignClass => $aReferences)
|
|
||||||
{
|
|
||||||
foreach ($aReferences as $sForeignExtKeyAttCode => $aFiltersByOperator)
|
|
||||||
{
|
|
||||||
foreach ($aFiltersByOperator as $iOperatorCode => $aFilters)
|
|
||||||
{
|
|
||||||
foreach ($aFilters as $oForeignFilter)
|
|
||||||
{
|
|
||||||
$oForeignKeyAttDef = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
|
||||||
|
|
||||||
$sForeignClassAlias = $oForeignFilter->GetFirstJoinedClassAlias();
|
|
||||||
|
|
||||||
if ($oForeignKeyAttDef instanceof AttributeObjectKey)
|
|
||||||
{
|
|
||||||
$sClassAttCode = $oForeignKeyAttDef->Get('class_attcode');
|
|
||||||
|
|
||||||
// Add the condition: `$sForeignClassAlias`.$sClassAttCode IN (subclasses of $sClass')
|
|
||||||
$oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($this->sClass,
|
|
||||||
ENUM_CHILD_CLASSES_ALL));
|
|
||||||
$oClassExpr = new FieldExpression($sClassAttCode, $sForeignClassAlias);
|
|
||||||
$oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr);
|
|
||||||
$this->oBuild->m_oQBExpressions->AddCondition($oClassRestriction);
|
|
||||||
}
|
|
||||||
|
|
||||||
$oOQLClassTreeBuilder = new OQLClassTreeBuilder($oForeignFilter, $this->oBuild);
|
|
||||||
$oSelectForeign = $oOQLClassTreeBuilder->DevelopOQLClassNode();
|
|
||||||
|
|
||||||
if ($iOperatorCode == TREE_OPERATOR_EQUALS)
|
|
||||||
{
|
|
||||||
$this->oOQLClassNode->AddInnerJoin($oSelectForeign, 'id', $sForeignExtKeyAttCode, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Hierarchical key
|
|
||||||
$this->oOQLClassNode->AddInnerJoinTree($oSelectForeign, $sForeignExtKeyAttCode, $sForeignExtKeyAttCode, false, $iOperatorCode, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Additional JOINS for polymorphic expressions (friendlyname, obsolescenceflag...)
|
|
||||||
*
|
|
||||||
* @param array $aPolymorphicJoinAlias
|
|
||||||
*
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private function JoinClassesForPolymorphicExpressions($aPolymorphicJoinAlias)
|
|
||||||
{
|
|
||||||
foreach ($aPolymorphicJoinAlias as $sSubClass => $sSubClassAlias)
|
|
||||||
{
|
|
||||||
$oSubClassFilter = new DBObjectSearch($sSubClass, $sSubClassAlias);
|
|
||||||
$oOQLClassTreeBuilder = new OQLClassTreeBuilder($oSubClassFilter, $this->oBuild);
|
|
||||||
$oSelectPoly = $oOQLClassTreeBuilder->DevelopOQLClassNode();
|
|
||||||
$this->oOQLClassNode->AddLeftJoin($oSelectPoly, 'id', 'id', true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
class OQLClassTreeOptimizer
|
|
||||||
{
|
|
||||||
/** @var OQLClassNode */
|
|
||||||
private $oOQLClassNode;
|
|
||||||
/** @var QueryBuilderContext */
|
|
||||||
private $oBuild;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OQLClassTreeOptimizer constructor.
|
|
||||||
*
|
|
||||||
* @param OQLClassNode $oOQLClassNode
|
|
||||||
* @param QueryBuilderContext $oBuild
|
|
||||||
*/
|
|
||||||
public function __construct($oOQLClassNode, $oBuild)
|
|
||||||
{
|
|
||||||
$this->oOQLClassNode = $oOQLClassNode;
|
|
||||||
$this->oBuild = $oBuild;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prune the unnecessary joins
|
|
||||||
*/
|
|
||||||
public function OptimizeClassTree()
|
|
||||||
{
|
|
||||||
$this->PruneJoins($this->oOQLClassNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param OQLClassNode $oCurrentClassNode
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function PruneJoins($oCurrentClassNode)
|
|
||||||
{
|
|
||||||
$aExpectedAttributes = $this->oBuild->m_oQBExpressions->GetExpectedFields($oCurrentClassNode->GetNodeClassAlias());
|
|
||||||
$bCanBeRemoved = empty($aExpectedAttributes);
|
|
||||||
|
|
||||||
foreach ($oCurrentClassNode->GetJoins() as $sLeftKey => $aJoins)
|
|
||||||
{
|
|
||||||
foreach ($aJoins as $index => $oJoin)
|
|
||||||
{
|
|
||||||
if ($this->PruneJoins($oJoin->GetOOQLClassNode()))
|
|
||||||
{
|
|
||||||
if ($oJoin->IsOutbound())
|
|
||||||
{
|
|
||||||
// The join is not used, remove from tree
|
|
||||||
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Inbound joins cannot be removed
|
|
||||||
$bCanBeRemoved = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This join is used, so the current node cannot be removed
|
|
||||||
$bCanBeRemoved = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $bCanBeRemoved;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
// Copyright (C) 2010-2017 Combodo SARL
|
||||||
* Copyright (C) 2013-2019 Combodo SARL
|
//
|
||||||
*
|
// This file is part of iTop.
|
||||||
* This file is part of iTop.
|
//
|
||||||
*
|
// iTop is free software; you can redistribute it and/or modify
|
||||||
* iTop is free software; you can redistribute it and/or modify
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
* 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
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
// (at your option) any later version.
|
||||||
* (at your option) any later version.
|
//
|
||||||
*
|
// iTop is distributed in the hope that it will be useful,
|
||||||
* iTop is distributed in the hope that it will be useful,
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// GNU Affero General Public License for more details.
|
||||||
* GNU Affero General Public License for more details.
|
//
|
||||||
*
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
|
||||||
|
|
||||||
require_once('dbobjectiterator.php');
|
require_once('dbobjectiterator.php');
|
||||||
|
|
||||||
@@ -108,10 +107,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \DBObjectSearch
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function GetFilter()
|
public function GetFilter()
|
||||||
{
|
{
|
||||||
return clone $this->oOriginalSet->GetFilter();
|
return clone $this->oOriginalSet->GetFilter();
|
||||||
@@ -120,10 +115,9 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
/**
|
/**
|
||||||
* Specify the subset of attributes to load (for each class of objects) before performing the SQL query for retrieving the rows from the DB
|
* Specify the subset of attributes to load (for each class of objects) before performing the SQL query for retrieving the rows from the DB
|
||||||
*
|
*
|
||||||
* @param array $aAttToLoad Format: alias => array of attribute_codes
|
* @param hash $aAttToLoad Format: alias => array of attribute_codes
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
*/
|
||||||
public function OptimizeColumnLoad($aAttToLoad)
|
public function OptimizeColumnLoad($aAttToLoad)
|
||||||
{
|
{
|
||||||
@@ -188,11 +182,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \MySQLException
|
|
||||||
*/
|
|
||||||
protected function LoadOriginalIds()
|
protected function LoadOriginalIds()
|
||||||
{
|
{
|
||||||
if ($this->aOriginalObjects === null)
|
if ($this->aOriginalObjects === null)
|
||||||
@@ -228,12 +217,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
|
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
|
||||||
*
|
|
||||||
* @return array
|
* @return array
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
protected function GetArrayOfIndex()
|
protected function GetArrayOfIndex()
|
||||||
{
|
{
|
||||||
@@ -305,9 +289,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
* The total number of objects in the collection
|
* The total number of objects in the collection
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \MySQLException
|
|
||||||
*/
|
*/
|
||||||
public function Count()
|
public function Count()
|
||||||
{
|
{
|
||||||
@@ -319,8 +300,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
/**
|
/**
|
||||||
* Position the cursor to the given 0-based position
|
* Position the cursor to the given 0-based position
|
||||||
*
|
*
|
||||||
* @param int $iPosition
|
* @param $iPosition
|
||||||
*
|
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @internal param int $iRow
|
* @internal param int $iRow
|
||||||
*/
|
*/
|
||||||
@@ -344,9 +324,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
* Fetch the object at the current position in the collection and move the cursor to the next position.
|
* Fetch the object at the current position in the collection and move the cursor to the next position.
|
||||||
*
|
*
|
||||||
* @return DBObject|null The fetched object or null when at the end
|
* @return DBObject|null The fetched object or null when at the end
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \MySQLException
|
|
||||||
*/
|
*/
|
||||||
public function Fetch()
|
public function Fetch()
|
||||||
{
|
{
|
||||||
@@ -363,14 +340,8 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current element
|
* Return the current element
|
||||||
*
|
|
||||||
* @link http://php.net/manual/en/iterator.current.php
|
* @link http://php.net/manual/en/iterator.current.php
|
||||||
* @return mixed Can return any type.
|
* @return mixed Can return any type.
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \MissingQueryArgument
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @throws \MySQLHasGoneAwayException
|
|
||||||
*/
|
*/
|
||||||
public function current()
|
public function current()
|
||||||
{
|
{
|
||||||
@@ -400,12 +371,8 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Move forward to next element
|
* Move forward to next element
|
||||||
*
|
|
||||||
* @link http://php.net/manual/en/iterator.next.php
|
* @link http://php.net/manual/en/iterator.next.php
|
||||||
* @return void Any returned value is ignored.
|
* @return void Any returned value is ignored.
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \MySQLException
|
|
||||||
*/
|
*/
|
||||||
public function next()
|
public function next()
|
||||||
{
|
{
|
||||||
@@ -444,13 +411,9 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if current position is valid
|
* Checks if current position is valid
|
||||||
*
|
|
||||||
* @link http://php.net/manual/en/iterator.valid.php
|
* @link http://php.net/manual/en/iterator.valid.php
|
||||||
* @return boolean The return value will be casted to boolean and then evaluated.
|
* @return boolean The return value will be casted to boolean and then evaluated.
|
||||||
* Returns true on success or false on failure.
|
* Returns true on success or false on failure.
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \MySQLException
|
|
||||||
*/
|
*/
|
||||||
public function valid()
|
public function valid()
|
||||||
{
|
{
|
||||||
@@ -463,12 +426,8 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Rewind the Iterator to the first element
|
* Rewind the Iterator to the first element
|
||||||
*
|
|
||||||
* @link http://php.net/manual/en/iterator.rewind.php
|
* @link http://php.net/manual/en/iterator.rewind.php
|
||||||
* @return void Any returned value is ignored.
|
* @return void Any returned value is ignored.
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \MySQLException
|
|
||||||
*/
|
*/
|
||||||
public function rewind()
|
public function rewind()
|
||||||
{
|
{
|
||||||
@@ -480,9 +439,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
reset($this->aModified);
|
reset($this->aModified);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function HasDelta()
|
public function HasDelta()
|
||||||
{
|
{
|
||||||
return $this->bHasDelta;
|
return $this->bHasDelta;
|
||||||
@@ -490,9 +446,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This method has been designed specifically for AttributeLinkedSet:Equals and as such it assumes that the passed argument is a clone of this.
|
* This method has been designed specifically for AttributeLinkedSet:Equals and as such it assumes that the passed argument is a clone of this.
|
||||||
*
|
* @param ormLinkSet $oFellow
|
||||||
* @param \ormLinkSet $oFellow
|
|
||||||
*
|
|
||||||
* @return bool|null
|
* @return bool|null
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
@@ -519,12 +473,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
return $bRet;
|
return $bRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \iDBObjectSetIterator $oFellow
|
|
||||||
*
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function UpdateFromCompleteList(iDBObjectSetIterator $oFellow)
|
public function UpdateFromCompleteList(iDBObjectSetIterator $oFellow)
|
||||||
{
|
{
|
||||||
if ($oFellow === $this)
|
if ($oFellow === $this)
|
||||||
@@ -563,7 +511,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
$this->aPreserved = ($this->aOriginalObjects === null) ? array() : $this->aOriginalObjects;
|
$this->aPreserved = ($this->aOriginalObjects === null) ? array() : $this->aOriginalObjects;
|
||||||
$this->bHasDelta = false;
|
$this->bHasDelta = false;
|
||||||
|
|
||||||
/** @var \AttributeLinkedSet|\AttributeLinkedSetIndirect $oAttDef */
|
/** @var AttributeLinkedSet $oAttDef */
|
||||||
$oAttDef = MetaModel::GetAttributeDef($this->sHostClass, $this->sAttCode);
|
$oAttDef = MetaModel::GetAttributeDef($this->sHostClass, $this->sAttCode);
|
||||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||||
$sAdditionalKey = null;
|
$sAdditionalKey = null;
|
||||||
@@ -572,7 +520,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
$sAdditionalKey = $oAttDef->GetExtKeyToRemote();
|
$sAdditionalKey = $oAttDef->GetExtKeyToRemote();
|
||||||
}
|
}
|
||||||
// Compare both collections by iterating the whole sets, order them, a build a fingerprint based on meaningful data (what make the difference)
|
// Compare both collections by iterating the whole sets, order them, a build a fingerprint based on meaningful data (what make the difference)
|
||||||
/** @var \DBObject $oLink */
|
|
||||||
$oComparator = new DBObjectSetComparator($this, $oFellow, array($sExtKeyToMe), $sAdditionalKey);
|
$oComparator = new DBObjectSetComparator($this, $oFellow, array($sExtKeyToMe), $sAdditionalKey);
|
||||||
$aChanges = $oComparator->GetDifferences();
|
$aChanges = $oComparator->GetDifferences();
|
||||||
foreach ($aChanges['added'] as $oLink)
|
foreach ($aChanges['added'] as $oLink)
|
||||||
@@ -615,21 +562,10 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param DBObject $oHostObject
|
* @param DBObject $oHostObject
|
||||||
*
|
|
||||||
* @throws \ArchivedObjectException
|
|
||||||
* @throws \CoreCannotSaveObjectException
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreUnexpectedValue
|
|
||||||
* @throws \CoreWarning
|
|
||||||
* @throws \DeleteException
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @throws \MySQLHasGoneAwayException
|
|
||||||
* @throws \OQLException
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
*/
|
||||||
public function DBWrite(DBObject $oHostObject)
|
public function DBWrite(DBObject $oHostObject)
|
||||||
{
|
{
|
||||||
/** @var \AttributeLinkedSet|\AttributeLinkedSetIndirect $oAttDef */
|
/** @var AttributeLinkedSet $oAttDef */
|
||||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oHostObject), $this->sAttCode);
|
$oAttDef = MetaModel::GetAttributeDef(get_class($oHostObject), $this->sAttCode);
|
||||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||||
$sExtKeyToRemote = $oAttDef->IsIndirect() ? $oAttDef->GetExtKeyToRemote() : 'n/a';
|
$sExtKeyToRemote = $oAttDef->IsIndirect() ? $oAttDef->GetExtKeyToRemote() : 'n/a';
|
||||||
@@ -782,24 +718,13 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
$oMtx->Unlock();
|
$oMtx->Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param bool $bShowObsolete
|
|
||||||
*
|
|
||||||
* @return \DBObjectSet
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \CoreWarning
|
|
||||||
* @throws \MySQLException
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function ToDBObjectSet($bShowObsolete = true)
|
public function ToDBObjectSet($bShowObsolete = true)
|
||||||
{
|
{
|
||||||
/** @var \AttributeLinkedSet|\AttributeLinkedSetIndirect $oAttDef */
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($this->sHostClass, $this->sAttCode);
|
$oAttDef = MetaModel::GetAttributeDef($this->sHostClass, $this->sAttCode);
|
||||||
$oLinkSearch = $this->GetFilter();
|
$oLinkSearch = $this->GetFilter();
|
||||||
if ($oAttDef->IsIndirect())
|
if ($oAttDef->IsIndirect())
|
||||||
{
|
{
|
||||||
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
|
$sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
|
||||||
/** @var \AttributeExternalKey $oLinkingAttDef */
|
|
||||||
$oLinkingAttDef = MetaModel::GetAttributeDef($this->sClass, $sExtKeyToRemote);
|
$oLinkingAttDef = MetaModel::GetAttributeDef($this->sClass, $sExtKeyToRemote);
|
||||||
$sTargetClass = $oLinkingAttDef->GetTargetClass();
|
$sTargetClass = $oLinkingAttDef->GetTargetClass();
|
||||||
if (!$bShowObsolete && MetaModel::IsObsoletable($sTargetClass))
|
if (!$bShowObsolete && MetaModel::IsObsoletable($sTargetClass))
|
||||||
@@ -820,7 +745,6 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
|||||||
{
|
{
|
||||||
$oLinkSet->AddObjectArray($this->aAdded);
|
$oLinkSet->AddObjectArray($this->aAdded);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $oLinkSet;
|
return $oLinkSet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +180,7 @@ EOF
|
|||||||
require_once(APPROOT.'application/pdfpage.class.inc.php');
|
require_once(APPROOT.'application/pdfpage.class.inc.php');
|
||||||
$oPage = new PDFPage(Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())), $this->aStatusInfo['page_size'], $this->aStatusInfo['page_orientation']);
|
$oPage = new PDFPage(Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())), $this->aStatusInfo['page_size'], $this->aStatusInfo['page_orientation']);
|
||||||
$oPDF = $oPage->get_tcpdf();
|
$oPDF = $oPage->get_tcpdf();
|
||||||
$oPDF->SetFontSize(8);
|
$oPDF->SetFont('dejavusans', '', 8, '', true);
|
||||||
|
|
||||||
$oPage->add(file_get_contents($this->aStatusInfo['tmp_file']));
|
$oPage->add(file_get_contents($this->aStatusInfo['tmp_file']));
|
||||||
$oPage->add($sData);
|
$oPage->add($sData);
|
||||||
|
|||||||
@@ -31,23 +31,10 @@ class QueryBuilderContext
|
|||||||
protected $m_aModifierProperties;
|
protected $m_aModifierProperties;
|
||||||
protected $m_aSelectedClasses;
|
protected $m_aSelectedClasses;
|
||||||
protected $m_aFilteredTables;
|
protected $m_aFilteredTables;
|
||||||
protected $m_sEmptyClassAlias;
|
|
||||||
|
|
||||||
public $m_oQBExpressions;
|
public $m_oQBExpressions;
|
||||||
|
|
||||||
/**
|
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||||
* QueryBuilderContext constructor.
|
|
||||||
*
|
|
||||||
* @param $oFilter
|
|
||||||
* @param $aModifierProperties
|
|
||||||
* @param array $aGroupByExpr
|
|
||||||
* @param array $aSelectedClasses
|
|
||||||
* @param array $aSelectExpr
|
|
||||||
* @param array $aAttToLoad
|
|
||||||
*
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function __construct($oFilter, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null, $aAttToLoad = null)
|
|
||||||
{
|
{
|
||||||
$this->m_oRootFilter = $oFilter;
|
$this->m_oRootFilter = $oFilter;
|
||||||
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr, $aSelectExpr);
|
$this->m_oQBExpressions = new QueryBuilderExpressions($oFilter, $aGroupByExpr, $aSelectExpr);
|
||||||
@@ -66,37 +53,6 @@ class QueryBuilderContext
|
|||||||
// For the unions, the selected classes can be upper in the hierarchy (lowest common ancestor)
|
// For the unions, the selected classes can be upper in the hierarchy (lowest common ancestor)
|
||||||
$this->m_aSelectedClasses = $aSelectedClasses;
|
$this->m_aSelectedClasses = $aSelectedClasses;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all the attribute of interest
|
|
||||||
foreach ($this->m_aSelectedClasses as $sClassAlias => $sClass)
|
|
||||||
{
|
|
||||||
$sTableAlias = $sClassAlias;
|
|
||||||
if (empty($sTableAlias))
|
|
||||||
{
|
|
||||||
$sTableAlias = $this->GenerateClassAlias("$sClass", $sClass);
|
|
||||||
$this->m_sEmptyClassAlias = $sTableAlias;
|
|
||||||
}
|
|
||||||
// default to the whole list of attributes + the very std id/finalclass
|
|
||||||
$this->m_oQBExpressions->AddSelect($sClassAlias.'id', new FieldExpression('id', $sTableAlias));
|
|
||||||
if (is_null($aAttToLoad) || !array_key_exists($sClassAlias, $aAttToLoad))
|
|
||||||
{
|
|
||||||
$sSelectedClass = $this->GetSelectedClass($sClassAlias);
|
|
||||||
$aAttList = MetaModel::ListAttributeDefs($sSelectedClass);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$aAttList = $aAttToLoad[$sClassAlias];
|
|
||||||
}
|
|
||||||
foreach ($aAttList as $sAttCode => $oAttDef)
|
|
||||||
{
|
|
||||||
if (!$oAttDef->IsScalar())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$oExpression = new FieldExpression($sAttCode, $sTableAlias);
|
|
||||||
$this->m_oQBExpressions->AddSelect($sClassAlias.$sAttCode, $oExpression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetRootFilter()
|
public function GetRootFilter()
|
||||||
@@ -147,14 +103,4 @@ class QueryBuilderContext
|
|||||||
{
|
{
|
||||||
return $this->m_aFilteredTables;
|
return $this->m_aFilteredTables;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function GetEmptyClassAlias()
|
|
||||||
{
|
|
||||||
return $this->m_sEmptyClassAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,218 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
class QueryBuilderExpressions
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var Expression
|
|
||||||
*/
|
|
||||||
protected $m_oConditionExpr;
|
|
||||||
/**
|
|
||||||
* @var Expression[]
|
|
||||||
*/
|
|
||||||
protected $m_aSelectExpr;
|
|
||||||
/**
|
|
||||||
* @var Expression[]
|
|
||||||
*/
|
|
||||||
protected $m_aGroupByExpr;
|
|
||||||
/**
|
|
||||||
* @var Expression[]
|
|
||||||
*/
|
|
||||||
protected $m_aJoinFields;
|
|
||||||
/**
|
|
||||||
* @var string[]
|
|
||||||
*/
|
|
||||||
protected $m_aClassIds;
|
|
||||||
|
|
||||||
public function __construct(DBObjectSearch $oSearch, $aGroupByExpr = null, $aSelectExpr = null)
|
|
||||||
{
|
|
||||||
$this->m_oConditionExpr = $oSearch->GetCriteria();
|
|
||||||
if (!$oSearch->GetShowObsoleteData())
|
|
||||||
{
|
|
||||||
foreach ($oSearch->GetSelectedClasses() as $sAlias => $sClass)
|
|
||||||
{
|
|
||||||
if (MetaModel::IsObsoletable($sClass))
|
|
||||||
{
|
|
||||||
$oNotObsolete = new BinaryExpression(new FieldExpression('obsolescence_flag', $sAlias), '=', new ScalarExpression(0));
|
|
||||||
$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oNotObsolete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->m_aSelectExpr = is_null($aSelectExpr) ? array() : $aSelectExpr;
|
|
||||||
$this->m_aGroupByExpr = $aGroupByExpr;
|
|
||||||
$this->m_aJoinFields = array();
|
|
||||||
$this->m_aJoinFields = array();
|
|
||||||
|
|
||||||
$this->m_aClassIds = array();
|
|
||||||
foreach ($oSearch->GetJoinedClasses() as $sClassAlias => $sClass)
|
|
||||||
{
|
|
||||||
$this->m_aClassIds[$sClassAlias] = new FieldExpression('id', $sClassAlias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetSelect()
|
|
||||||
{
|
|
||||||
return $this->m_aSelectExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetGroupBy()
|
|
||||||
{
|
|
||||||
return $this->m_aGroupByExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetCondition()
|
|
||||||
{
|
|
||||||
return $this->m_oConditionExpr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Expression|mixed
|
|
||||||
*/
|
|
||||||
public function PopJoinField()
|
|
||||||
{
|
|
||||||
return array_pop($this->m_aJoinFields);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $sAttAlias
|
|
||||||
* @param Expression $oExpression
|
|
||||||
*/
|
|
||||||
public function AddSelect($sAttAlias, Expression $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_aSelectExpr[$sAttAlias] = $oExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Expression $oExpression
|
|
||||||
*/
|
|
||||||
public function AddCondition(Expression $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oExpression);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Expression $oExpression
|
|
||||||
*/
|
|
||||||
public function PushJoinField(Expression $oExpression)
|
|
||||||
{
|
|
||||||
array_push($this->m_aJoinFields, $oExpression);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function AddJoinField($sName, Expression $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_aJoinFields[$sName] = $oExpression;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function GetJoinField($sName)
|
|
||||||
{
|
|
||||||
return isset($this->m_aJoinFields[$sName]) ? $this->m_aJoinFields[$sName] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get tables representing the queried objects
|
|
||||||
* Could be further optimized: when the first join is an outer join, then the rest can be omitted
|
|
||||||
*
|
|
||||||
* @param array $aTables
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function GetMandatoryTables(&$aTables = null)
|
|
||||||
{
|
|
||||||
if (is_null($aTables))
|
|
||||||
{
|
|
||||||
$aTables = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->m_aClassIds as $sClass => $oExpression)
|
|
||||||
{
|
|
||||||
$oExpression->CollectUsedParents($aTables);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $aTables;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $sAlias
|
|
||||||
*
|
|
||||||
* @return FieldExpression[] of unresolved fields
|
|
||||||
*/
|
|
||||||
public function GetUnresolvedFields($sAlias)
|
|
||||||
{
|
|
||||||
$aUnresolved = $this->GetExpectedFields($sAlias);
|
|
||||||
foreach ($this->m_aJoinFields as $oExpression)
|
|
||||||
{
|
|
||||||
$oExpression->GetUnresolvedFields($sAlias, $aUnresolved);
|
|
||||||
}
|
|
||||||
return $aUnresolved;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get Expected fields from Select and Conditions
|
|
||||||
* (Joins are excluded)
|
|
||||||
*
|
|
||||||
* @param string $sAlias
|
|
||||||
*
|
|
||||||
* @return FieldExpression[]
|
|
||||||
*/
|
|
||||||
public function GetExpectedFields($sAlias)
|
|
||||||
{
|
|
||||||
$aUnresolved = array();
|
|
||||||
$this->m_oConditionExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
|
||||||
|
|
||||||
foreach ($this->m_aSelectExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
|
||||||
}
|
|
||||||
if (!empty($this->m_aGroupByExpr))
|
|
||||||
{
|
|
||||||
foreach ($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aUnresolved;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true)
|
|
||||||
{
|
|
||||||
$this->m_oConditionExpr = $this->m_oConditionExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
foreach ($this->m_aSelectExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$this->m_aSelectExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
}
|
|
||||||
if ($this->m_aGroupByExpr)
|
|
||||||
{
|
|
||||||
foreach ($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$this->m_aGroupByExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($this->m_aJoinFields as $index => $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_aJoinFields[$index] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
}
|
|
||||||
foreach ($this->m_aClassIds as $sClass => $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_aClassIds[$sClass] = $oExpression->Translate($aTranslationData, $bMatchAll, $bMarkFieldsAsResolved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function RenameParam($sOldName, $sNewName)
|
|
||||||
{
|
|
||||||
$this->m_oConditionExpr->RenameParam($sOldName, $sNewName);
|
|
||||||
foreach ($this->m_aSelectExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$this->m_aSelectExpr[$sColAlias] = $oExpr->RenameParam($sOldName, $sNewName);
|
|
||||||
}
|
|
||||||
if ($this->m_aGroupByExpr)
|
|
||||||
{
|
|
||||||
foreach ($this->m_aGroupByExpr as $sColAlias => $oExpr)
|
|
||||||
{
|
|
||||||
$this->m_aGroupByExpr[$sColAlias] = $oExpr->RenameParam($sOldName, $sNewName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($this->m_aJoinFields as $index => $oExpression)
|
|
||||||
{
|
|
||||||
$this->m_aJoinFields[$index] = $oExpression->RenameParam($sOldName, $sNewName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +1,43 @@
|
|||||||
<?php
|
<?php
|
||||||
|
// Copyright (C) 2015-2017 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/>
|
||||||
/**
|
/**
|
||||||
* Copyright (C) 2013-2019 Combodo SARL
|
* Data structures (i.e. PHP classes) to manage "graphs"
|
||||||
*
|
*
|
||||||
* This file is part of iTop.
|
* @copyright Copyright (C) 2015-2017 Combodo SARL
|
||||||
*
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
* 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
|
* Example:
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* require_once('../approot.inc.php');
|
||||||
* (at your option) any later version.
|
* require_once(APPROOT.'application/startup.inc.php');
|
||||||
*
|
* require_once(APPROOT.'core/simplegraph.class.inc.php');
|
||||||
* iTop is distributed in the hope that it will be useful,
|
*
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* $oGraph = new SimpleGraph();
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
*
|
||||||
* GNU Affero General Public License for more details.
|
* $oNode1 = new GraphNode($oGraph, 'Source1');
|
||||||
*
|
* $oNode2 = new GraphNode($oGraph, 'Sink');
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* $oEdge1 = new GraphEdge($oGraph, 'flow1', $oNode1, $oNode2);
|
||||||
|
* $oNode3 = new GraphNode($oGraph, 'Source2');
|
||||||
|
* $oEdge2 = new GraphEdge($oGraph, 'flow2', $oNode3, $oNode2);
|
||||||
|
* $oEdge2 = new GraphEdge($oGraph, 'flow3', $oNode2, $oNode3);
|
||||||
|
* $oEdge2 = new GraphEdge($oGraph, 'flow4', $oNode1, $oNode3);
|
||||||
|
*
|
||||||
|
* echo $oGraph->DumpAsHtmlImage(); // requires graphviz
|
||||||
|
* echo $oGraph->DumpAsHtmlText();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -363,11 +363,11 @@ class SQLObjectQuery extends SQLQuery
|
|||||||
$sCountFields = implode(', ', $aCountFields);
|
$sCountFields = implode(', ', $aCountFields);
|
||||||
// Count can be limited for performance reason, in this case the total amount is not important,
|
// Count can be limited for performance reason, in this case the total amount is not important,
|
||||||
// we only need to know if the number of entries is greater than a certain amount.
|
// we only need to know if the number of entries is greater than a certain amount.
|
||||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep DISTINCT $sCountFields $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _alderaan_";
|
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep DISTINCT $sCountFields $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _tatooine_";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _alderaan_";
|
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _tatooine_";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,299 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Class SQLObjectQueryBuilder
|
|
||||||
*
|
|
||||||
* @copyright Copyright (C) 2010-2019 Combodo SARL
|
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
class SQLObjectQueryBuilder
|
|
||||||
{
|
|
||||||
/** @var \DBObjectSearch */
|
|
||||||
private $oDBObjetSearch;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SQLObjectQueryBuilder constructor.
|
|
||||||
*
|
|
||||||
* @param \DBObjectSearch $oDBObjetSearch
|
|
||||||
*/
|
|
||||||
public function __construct($oDBObjetSearch)
|
|
||||||
{
|
|
||||||
$this->oDBObjetSearch = $oDBObjetSearch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $aAttToLoad
|
|
||||||
* @param bool $bGetCount
|
|
||||||
* @param array $aModifierProperties
|
|
||||||
* @param array $aGroupByExpr
|
|
||||||
* @param array $aSelectedClasses
|
|
||||||
* @param array $aSelectExpr
|
|
||||||
*
|
|
||||||
* @return null|SQLObjectQuery
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function BuildSQLQueryStruct($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
|
||||||
{
|
|
||||||
if ($bGetCount || !is_null($aGroupByExpr))
|
|
||||||
{
|
|
||||||
// Avoid adding all the fields for counts or "group by" requests
|
|
||||||
$aAttToLoad = array();
|
|
||||||
foreach ($this->oDBObjetSearch->GetSelectedClasses() as $sClassAlias => $sClass)
|
|
||||||
{
|
|
||||||
$aAttToLoad[$sClassAlias] = array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$oBuild = new QueryBuilderContext($this->oDBObjetSearch, $aModifierProperties, $aGroupByExpr, $aSelectedClasses, $aSelectExpr, $aAttToLoad);
|
|
||||||
$oSQLQuery = $this->MakeSQLObjectQueryRoot($oBuild, array(), $aGroupByExpr, $aSelectExpr);
|
|
||||||
|
|
||||||
return $oSQLQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return \SQLObjectQuery|null
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function MakeSQLObjectDeleteQuery()
|
|
||||||
{
|
|
||||||
$aModifierProperties = MetaModel::MakeModifierProperties($this->oDBObjetSearch);
|
|
||||||
$aAttToLoad = array($this->oDBObjetSearch->GetClassAlias() => array());
|
|
||||||
$oBuild = new QueryBuilderContext($this->oDBObjetSearch, $aModifierProperties, null, null, null, $aAttToLoad);
|
|
||||||
$oSQLQuery = $this->MakeSQLObjectQueryRoot($oBuild, array());
|
|
||||||
return $oSQLQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $aValues an array of $sAttCode => $value
|
|
||||||
*
|
|
||||||
* @return \SQLObjectQuery|null
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function MakeSQLObjectUpdateQuery($aValues)
|
|
||||||
{
|
|
||||||
$aModifierProperties = MetaModel::MakeModifierProperties($this->oDBObjetSearch);
|
|
||||||
$aRequested = array(); // Requested attributes are the updated attributes
|
|
||||||
foreach ($aValues as $sAttCode => $value)
|
|
||||||
{
|
|
||||||
$aRequested[$sAttCode] = MetaModel::GetAttributeDef($this->oDBObjetSearch->GetClass(), $sAttCode);
|
|
||||||
}
|
|
||||||
$aAttToLoad = array($this->oDBObjetSearch->GetClassAlias() => $aRequested);
|
|
||||||
$oBuild = new QueryBuilderContext($this->oDBObjetSearch, $aModifierProperties, null, null, null, $aAttToLoad);
|
|
||||||
$oSQLQuery = $this->MakeSQLObjectQueryRoot($oBuild, $aValues);
|
|
||||||
return $oSQLQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \QueryBuilderContext $oBuild
|
|
||||||
*
|
|
||||||
* @return \OQLClassNode
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private function GetOQLClassTree($oBuild)
|
|
||||||
{
|
|
||||||
$oOQLClassTreeBuilder = new OQLClassTreeBuilder($this->oDBObjetSearch, $oBuild);
|
|
||||||
$oOQLClassNode = $oOQLClassTreeBuilder->DevelopOQLClassNode();
|
|
||||||
$oOQLClassTreeOptimizer = new OQLClassTreeOptimizer($oOQLClassNode, $oBuild);
|
|
||||||
$oOQLClassTreeOptimizer->OptimizeClassTree();
|
|
||||||
$oOQLActualClassTreeResolver = new OQLActualClassTreeResolver($oOQLClassNode, $oBuild);
|
|
||||||
$oOQLClassNode = $oOQLActualClassTreeResolver->Resolve();
|
|
||||||
|
|
||||||
return $oOQLClassNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param \QueryBuilderContext $oBuild The builder will be unusable after that
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
public function DebugOQLClassTree($oBuild)
|
|
||||||
{
|
|
||||||
return $this->GetOQLClassTree($oBuild)->RenderDebug();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \QueryBuilderContext $oBuild
|
|
||||||
* @param array $aValues
|
|
||||||
* @param array $aGroupByExpr
|
|
||||||
* @param array $aSelectExpr
|
|
||||||
*
|
|
||||||
* @return null|SQLObjectQuery
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private function MakeSQLObjectQueryRoot($oBuild, $aValues = array(), $aGroupByExpr = null, $aSelectExpr = null)
|
|
||||||
{
|
|
||||||
$oOQLClassNode = $this->GetOQLClassTree($oBuild);
|
|
||||||
|
|
||||||
$oSQLQuery = $this->MakeSQLObjectQuery($oBuild, $oOQLClassNode, $aValues);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add SQL Level additional information
|
|
||||||
*/
|
|
||||||
$oSQLQuery->SetCondition($oBuild->m_oQBExpressions->GetCondition());
|
|
||||||
|
|
||||||
if (is_array($aGroupByExpr))
|
|
||||||
{
|
|
||||||
$aCols = $oBuild->m_oQBExpressions->GetGroupBy();
|
|
||||||
$oSQLQuery->SetGroupBy($aCols);
|
|
||||||
$oSQLQuery->SetSelect($aCols);
|
|
||||||
|
|
||||||
if (!empty($aSelectExpr))
|
|
||||||
{
|
|
||||||
// Get the fields corresponding to the select expressions
|
|
||||||
foreach($oBuild->m_oQBExpressions->GetSelect() as $sAlias => $oExpr)
|
|
||||||
{
|
|
||||||
if (key_exists($sAlias, $aSelectExpr))
|
|
||||||
{
|
|
||||||
$oSQLQuery->AddSelect($sAlias, $oExpr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oSQLQuery->SetSelect($oBuild->m_oQBExpressions->GetSelect());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter for archive flag
|
|
||||||
// Filter tables as late as possible: do not interfere with the optimization process
|
|
||||||
$aMandatoryTables = $oBuild->m_oQBExpressions->GetMandatoryTables();
|
|
||||||
foreach ($oBuild->GetFilteredTables() as $sTableAlias => $aConditions)
|
|
||||||
{
|
|
||||||
if ($aMandatoryTables && array_key_exists($sTableAlias, $aMandatoryTables))
|
|
||||||
{
|
|
||||||
foreach ($aConditions as $oCondition)
|
|
||||||
{
|
|
||||||
$oSQLQuery->AddCondition($oCondition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $oSQLQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \QueryBuilderContext $oBuild
|
|
||||||
* @param \OQLClassNode $oOQLClassNode
|
|
||||||
* @param array $aValues
|
|
||||||
*
|
|
||||||
* @return \SQLObjectQuery
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private function MakeSQLObjectQuery($oBuild, $oOQLClassNode, $aValues)
|
|
||||||
{
|
|
||||||
$oSQLQuery = $this->MakeSQLObjectQueryNode($oBuild, $oOQLClassNode, $aValues);
|
|
||||||
|
|
||||||
return $oSQLQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param \QueryBuilderContext $oBuild
|
|
||||||
* @param \OQLClassNode $oOQLClassNode
|
|
||||||
* @param array $aValues
|
|
||||||
*
|
|
||||||
* @return \SQLObjectQuery
|
|
||||||
* @throws \CoreException
|
|
||||||
*/
|
|
||||||
private function MakeSQLObjectQueryNode($oBuild, $oOQLClassNode, $aValues)
|
|
||||||
{
|
|
||||||
$sClass = $oOQLClassNode->GetNodeClass();
|
|
||||||
$sTable = MetaModel::DBGetTable($sClass);
|
|
||||||
$sClassAlias = $oOQLClassNode->GetNodeClassAlias();
|
|
||||||
$sSelectedClassAlias = $oOQLClassNode->GetOQLClassAlias();
|
|
||||||
$bIsOnQueriedClass = array_key_exists($sSelectedClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
|
|
||||||
$aExpectedAttributes = $oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias);
|
|
||||||
$oSelectedIdField = null;
|
|
||||||
|
|
||||||
$aTranslation = array();
|
|
||||||
$aUpdateValues = array();
|
|
||||||
$oIdField = new FieldExpressionResolved(MetaModel::DBGetKey($sClass), $sClassAlias);
|
|
||||||
$aTranslation[$sClassAlias]['id'] = $oIdField;
|
|
||||||
if ($bIsOnQueriedClass)
|
|
||||||
{
|
|
||||||
// Add this field to the list of queried fields (required for the COUNT to work fine)
|
|
||||||
$oSelectedIdField = $oIdField;
|
|
||||||
foreach ($aExpectedAttributes as $sAttCode => $oExpression)
|
|
||||||
{
|
|
||||||
if (!array_key_exists($sAttCode, $aValues))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
|
||||||
// Skip this attribute if not made of SQL columns
|
|
||||||
if (count($oAttDef->GetSQLExpressions()) == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foreach ($oAttDef->GetSQLValues($aValues[$sAttCode]) as $sColumn => $sValue)
|
|
||||||
{
|
|
||||||
$aUpdateValues[$sColumn] = $sValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$oBaseSQLQuery = new SQLObjectQuery($sTable, $sClassAlias, array(), $bIsOnQueriedClass, $aUpdateValues, $oSelectedIdField);
|
|
||||||
|
|
||||||
foreach ($aExpectedAttributes as $sAttCode => $oExpression)
|
|
||||||
{
|
|
||||||
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
|
||||||
foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
|
|
||||||
{
|
|
||||||
if (!empty($sColId))
|
|
||||||
{
|
|
||||||
// Multi column attributes
|
|
||||||
$oBuild->m_oQBExpressions->AddSelect($sSelectedClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias));
|
|
||||||
}
|
|
||||||
$oFieldSQLExp = new FieldExpressionResolved($sSQLExpr, $sClassAlias);
|
|
||||||
/**
|
|
||||||
* @var string $sPluginClass
|
|
||||||
* @var iQueryModifier $oQueryModifier
|
|
||||||
*/
|
|
||||||
foreach (MetaModel::EnumPlugins('iQueryModifier') as $sPluginClass => $oQueryModifier)
|
|
||||||
{
|
|
||||||
$oFieldSQLExp = $oQueryModifier->GetFieldExpression($oBuild, $sClass, $sAttCode, $sColId, $oFieldSQLExp, $oBaseSQLQuery);
|
|
||||||
}
|
|
||||||
$aTranslation[$sClassAlias][$sAttCode.$sColId] = $oFieldSQLExp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Translate the selected columns
|
|
||||||
//
|
|
||||||
$oBuild->m_oQBExpressions->Translate($aTranslation, false);
|
|
||||||
|
|
||||||
// Filter out archived records
|
|
||||||
//
|
|
||||||
if (MetaModel::IsArchivable($sClass))
|
|
||||||
{
|
|
||||||
if (!$oBuild->GetRootFilter()->GetArchiveMode())
|
|
||||||
{
|
|
||||||
$oNotArchived = new BinaryExpression(new FieldExpressionResolved('archive_flag', $sClassAlias), '=', new ScalarExpression(0));
|
|
||||||
$oBuild->AddFilteredTable($sClassAlias, $oNotArchived);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add Joins
|
|
||||||
$aJoins = $oOQLClassNode->GetJoins();
|
|
||||||
foreach ($aJoins as $aJoin)
|
|
||||||
{
|
|
||||||
foreach ($aJoin as $oOQLJoin)
|
|
||||||
{
|
|
||||||
$oJoinedClassNode = $oOQLJoin->GetOOQLClassNode();
|
|
||||||
$oJoinedSQLQuery = $this->MakeSQLObjectQueryNode($oBuild, $oJoinedClassNode, $aValues);
|
|
||||||
$oOQLJoin->AddToSQLObjectQuery($oBuild, $oBaseSQLQuery, $oJoinedSQLQuery);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $oBaseSQLQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -116,7 +116,7 @@ class SQLUnionQuery extends SQLQuery
|
|||||||
{
|
{
|
||||||
$sSelects = '('.implode(" $sLimit)$sLineSep UNION$sLineSep(", $aSelects)." $sLimit)";
|
$sSelects = '('.implode(" $sLimit)$sLineSep UNION$sLineSep(", $aSelects)." $sLimit)";
|
||||||
$sFrom = "($sLineSep$sSelects$sLineSep) as __selects__";
|
$sFrom = "($sLineSep$sSelects$sLineSep) as __selects__";
|
||||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep) AS _union_alderaan_";
|
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep 1 $sLineSep FROM $sFrom$sLineSep) AS _union_tatooine_";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ abstract class TagSetFieldData extends cmdbAbstractObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check that the code is not a MySQL stop word
|
// Check that the code is not a MySQL stop word
|
||||||
$sSQL = "SELECT value FROM information_schema.INNODB_FT_DEFAULT_STOPWORD";
|
$sSQL = "SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$aResults = CMDBSource::QueryToArray($sSQL);
|
$aResults = CMDBSource::QueryToArray($sSQL);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user