mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-20 16:34:11 +01:00
Compare commits
385 Commits
3.0.0-beta
...
3.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
827a108a38 | ||
|
|
4b50f5e1db | ||
|
|
5b742c97c9 | ||
|
|
16a2137777 | ||
|
|
e8316782aa | ||
|
|
edcf9e6a1e | ||
|
|
7db97c6804 | ||
|
|
f039d54a51 | ||
|
|
dab0e372d0 | ||
|
|
dc8c6ed7a9 | ||
|
|
18e341b0b7 | ||
|
|
2722f305e0 | ||
|
|
469e2e6e0e | ||
|
|
05301d4191 | ||
|
|
3df5febd3e | ||
|
|
dfd1d5fe35 | ||
|
|
d289457c0c | ||
|
|
f52b3bff0d | ||
|
|
b6b17733bf | ||
|
|
fc2b00699e | ||
|
|
b00b08d08b | ||
|
|
c02ea05883 | ||
|
|
61a0028d02 | ||
|
|
c1c2d027c3 | ||
|
|
5269096ecd | ||
|
|
e6511e049a | ||
|
|
74fbd12709 | ||
|
|
4db5d4c08d | ||
|
|
c40394638a | ||
|
|
cc2862837a | ||
|
|
e27c6b1cd7 | ||
|
|
ae00686e58 | ||
|
|
7934f9b9dc | ||
|
|
7f2eef4a24 | ||
|
|
8a65a592f3 | ||
|
|
7d6b019cfa | ||
|
|
5e48400cb1 | ||
|
|
252562ace9 | ||
|
|
c9c32b0de1 | ||
|
|
770ac8ffe5 | ||
|
|
db137d3816 | ||
|
|
77768e8400 | ||
|
|
5621eb2191 | ||
|
|
2bf970932e | ||
|
|
b6fac4b411 | ||
|
|
d8a77c22a3 | ||
|
|
b75a495336 | ||
|
|
ed3c387712 | ||
|
|
bbadc1f0be | ||
|
|
a141db4737 | ||
|
|
cb67dd1f1c | ||
|
|
af653608ef | ||
|
|
9dac061b04 | ||
|
|
ccf1bba235 | ||
|
|
312a5b246b | ||
|
|
ddd6bf22af | ||
|
|
903de43589 | ||
|
|
2d67594ccf | ||
|
|
0c7eee7f9c | ||
|
|
65f9f86bcc | ||
|
|
efaf53e568 | ||
|
|
81a2a9278c | ||
|
|
e15d4bfab6 | ||
|
|
e23c41232d | ||
|
|
e25d070a38 | ||
|
|
499c97b914 | ||
|
|
c472b8ad3b | ||
|
|
94ceb98b9e | ||
|
|
b1de8683f0 | ||
|
|
cff2105908 | ||
|
|
e11d5c142c | ||
|
|
f77b3bedaf | ||
|
|
3654df1cae | ||
|
|
f3d2e89c68 | ||
|
|
dd46536f7c | ||
|
|
e249907a9c | ||
|
|
4ded943a43 | ||
|
|
bd52f4fefb | ||
|
|
f57785e422 | ||
|
|
77880c3675 | ||
|
|
bb369e68b5 | ||
|
|
2a2d68e204 | ||
|
|
b49fbf1a97 | ||
|
|
308bff7875 | ||
|
|
9444e61856 | ||
|
|
2f114701a1 | ||
|
|
5128db05cf | ||
|
|
9675202bb2 | ||
|
|
cbe42cd727 | ||
|
|
3559425fc1 | ||
|
|
a246528eec | ||
|
|
7fbbf1088a | ||
|
|
fa4a5e2990 | ||
|
|
b662a7e3c6 | ||
|
|
cdc452282e | ||
|
|
d8c4251c03 | ||
|
|
72d1ab5cbc | ||
|
|
18b8e7093a | ||
|
|
aedb9ccbba | ||
|
|
67fa156c0e | ||
|
|
9437e689fd | ||
|
|
a79459bc53 | ||
|
|
500bd15843 | ||
|
|
3e8dd2f4a5 | ||
|
|
d0fade9ce1 | ||
|
|
51a49dfce8 | ||
|
|
db8462ccc9 | ||
|
|
066b71686d | ||
|
|
be633001a5 | ||
|
|
b0904cabfd | ||
|
|
84426c6634 | ||
|
|
9d19c189e6 | ||
|
|
91d030933b | ||
|
|
d0f9868a19 | ||
|
|
2a913cd484 | ||
|
|
dbaf924171 | ||
|
|
1b2d75efd6 | ||
|
|
d66a1e8c10 | ||
|
|
a5ad981d70 | ||
|
|
8adf743cc7 | ||
|
|
4e544a8eab | ||
|
|
75450ded1d | ||
|
|
2b97fed5db | ||
|
|
62d8a2ba1f | ||
|
|
fe9a442500 | ||
|
|
11d2991286 | ||
|
|
bcca6ac720 | ||
|
|
abb63182e3 | ||
|
|
4409162eb7 | ||
|
|
0dc3d249da | ||
|
|
3598da8808 | ||
|
|
a9b30e160f | ||
|
|
249fa6ca93 | ||
|
|
50d73e7fca | ||
|
|
1b9e67dc6e | ||
|
|
ca2124130f | ||
|
|
fffd4cf962 | ||
|
|
4a6b351c37 | ||
|
|
206143824b | ||
|
|
90c866f696 | ||
|
|
0bbdbdb1cd | ||
|
|
395c9c288b | ||
|
|
60a17c9a65 | ||
|
|
2de6ba2827 | ||
|
|
2beb795f9a | ||
|
|
6847d8a5c7 | ||
|
|
f6885567eb | ||
|
|
4b888a3805 | ||
|
|
07f470024a | ||
|
|
4c69865480 | ||
|
|
06985d3cf2 | ||
|
|
ab40c67678 | ||
|
|
a286564345 | ||
|
|
c9e592c07d | ||
|
|
5bcdcb52b2 | ||
|
|
456283866c | ||
|
|
821c14ee86 | ||
|
|
30f1ad8da8 | ||
|
|
6d0b979bcd | ||
|
|
e16425ab8a | ||
|
|
2e426d373d | ||
|
|
a1a38ca224 | ||
|
|
05830de4e7 | ||
|
|
2e8920f3d1 | ||
|
|
4450c28295 | ||
|
|
aa43201cbe | ||
|
|
3d7df7600f | ||
|
|
aba0d4144c | ||
|
|
fe82f54066 | ||
|
|
2d44d9d1c6 | ||
|
|
8c03525fbb | ||
|
|
79c17970f8 | ||
|
|
04f7660267 | ||
|
|
8c7f7abaab | ||
|
|
e963a6741f | ||
|
|
e8d314e1f6 | ||
|
|
ebe493f7f7 | ||
|
|
e29f1825be | ||
|
|
2df8bb1707 | ||
|
|
27995c14bf | ||
|
|
890d0f64ce | ||
|
|
a29bb83a20 | ||
|
|
413820a22b | ||
|
|
860472beea | ||
|
|
dd4283199e | ||
|
|
e147aa31aa | ||
|
|
b8aa3dfb0c | ||
|
|
83e2dbc30a | ||
|
|
f63dad7a3a | ||
|
|
c75bb6b892 | ||
|
|
15ffb82257 | ||
|
|
5e2ae18570 | ||
|
|
070a23e61b | ||
|
|
0ab6655ae7 | ||
|
|
322371dd9f | ||
|
|
b8e974cfd1 | ||
|
|
e03eb1e279 | ||
|
|
6d2b75df9e | ||
|
|
ab1b6837a2 | ||
|
|
5c02b5c38c | ||
|
|
dd486dd015 | ||
|
|
b7e8c0de8a | ||
|
|
32dae59b9b | ||
|
|
5540988001 | ||
|
|
f8c59e6171 | ||
|
|
ab23ea1da0 | ||
|
|
3d49cd199a | ||
|
|
ee848129b7 | ||
|
|
63448276cc | ||
|
|
d5af6e9061 | ||
|
|
2fc3e7ad38 | ||
|
|
465ed58471 | ||
|
|
fac454595a | ||
|
|
89ea4adbce | ||
|
|
427f107ddf | ||
|
|
59d674d744 | ||
|
|
54db7243bf | ||
|
|
e6d2b0bc18 | ||
|
|
9e1f5a1b63 | ||
|
|
5b42b8983a | ||
|
|
5bae964064 | ||
|
|
ef9c18e393 | ||
|
|
ed43d00afe | ||
|
|
908a48e0a1 | ||
|
|
9b854dbcc7 | ||
|
|
7757f1f2d2 | ||
|
|
5e2f8a4ea6 | ||
|
|
62f7eca0a8 | ||
|
|
820f2cbed6 | ||
|
|
d03718681f | ||
|
|
a353317746 | ||
|
|
723eb90160 | ||
|
|
0e14be8b15 | ||
|
|
ef6d7925fc | ||
|
|
ec8c2ca122 | ||
|
|
ebe50b319a | ||
|
|
48b1a15bf7 | ||
|
|
5102400be4 | ||
|
|
4bde828dbb | ||
|
|
e0929f4d0d | ||
|
|
ef1903dabe | ||
|
|
e53a45ec5d | ||
|
|
0811fd4aa7 | ||
|
|
cdf5789d62 | ||
|
|
8f2b5ad8e2 | ||
|
|
df49e9c3b5 | ||
|
|
6915b0b824 | ||
|
|
0b0c99c935 | ||
|
|
b9a68f4aeb | ||
|
|
c611b11981 | ||
|
|
25763c2d2e | ||
|
|
c8f3d23d30 | ||
|
|
88fda1466e | ||
|
|
8a7f0d346d | ||
|
|
3e5440aa3b | ||
|
|
6f08038f34 | ||
|
|
216e62c448 | ||
|
|
f4856150ed | ||
|
|
d73d39e71a | ||
|
|
6abdabd197 | ||
|
|
020460d048 | ||
|
|
347cbca5cf | ||
|
|
8ea5be4ead | ||
|
|
b3f827ed5e | ||
|
|
eaf8a187aa | ||
|
|
34f64c61f6 | ||
|
|
1d28bbe3f4 | ||
|
|
54c89dcbb5 | ||
|
|
06dac2417a | ||
|
|
83125d9ae1 | ||
|
|
20f4419062 | ||
|
|
0f4ca4237d | ||
|
|
bc2c12a8c3 | ||
|
|
8154e718a1 | ||
|
|
9ec6c2098b | ||
|
|
4305e40ae5 | ||
|
|
0f149ed852 | ||
|
|
d5f9ca9c4b | ||
|
|
198c63dd79 | ||
|
|
96ae91494b | ||
|
|
ceaa98f4ef | ||
|
|
af4b9aaa52 | ||
|
|
d2662b608b | ||
|
|
591d189a33 | ||
|
|
36bea54bac | ||
|
|
b5369a0c03 | ||
|
|
1c11cef2f8 | ||
|
|
d8bd5528d3 | ||
|
|
9ed8cf7970 | ||
|
|
be24d409ed | ||
|
|
64d91b194f | ||
|
|
2bc61caab1 | ||
|
|
8f0a5fcaf9 | ||
|
|
b6df73bdcc | ||
|
|
836a35d0dd | ||
|
|
c7a7bfcd68 | ||
|
|
e7e09b5023 | ||
|
|
3f24b80043 | ||
|
|
fe3512cb5f | ||
|
|
2a32c5691b | ||
|
|
dce244f5aa | ||
|
|
63bd1643ce | ||
|
|
3c84a74ea4 | ||
|
|
d9870c2513 | ||
|
|
7f0493c91d | ||
|
|
26e32b1759 | ||
|
|
1b1a6321d7 | ||
|
|
0eba00259a | ||
|
|
a7a9e5f0eb | ||
|
|
bf4835eec0 | ||
|
|
9fbc631b07 | ||
|
|
c6cb7c41cd | ||
|
|
0cb1583688 | ||
|
|
370b42d1fd | ||
|
|
0da8c761c7 | ||
|
|
8c39374abb | ||
|
|
eb239843aa | ||
|
|
e38ca54691 | ||
|
|
94d99a4109 | ||
|
|
f39e801719 | ||
|
|
645f20742c | ||
|
|
d86065b4a6 | ||
|
|
8af54efd44 | ||
|
|
c96ee4fafc | ||
|
|
ced4d1c5f1 | ||
|
|
6d3e8df3e4 | ||
|
|
963fae243c | ||
|
|
5a09365221 | ||
|
|
49c5f75c6c | ||
|
|
bd67bc8838 | ||
|
|
c9aa693872 | ||
|
|
776c03ef6a | ||
|
|
ae6f8fba5c | ||
|
|
a139dc7e6e | ||
|
|
7d46626d9c | ||
|
|
9ea25188ba | ||
|
|
fadafa8267 | ||
|
|
e0d6bc18be | ||
|
|
2ae01c19e1 | ||
|
|
a5e2831e31 | ||
|
|
e86454ff74 | ||
|
|
62be3f9f58 | ||
|
|
cddbe27182 | ||
|
|
1f56e39577 | ||
|
|
a35c80de57 | ||
|
|
4b8ef4f919 | ||
|
|
c3d23981fb | ||
|
|
9c6d8253f4 | ||
|
|
4d6a8a76aa | ||
|
|
8fa6ae6703 | ||
|
|
fdc987f367 | ||
|
|
be9bb10606 | ||
|
|
11d3d5af49 | ||
|
|
b630492d7a | ||
|
|
1db32c6dee | ||
|
|
b1597f7d90 | ||
|
|
e2904fb0ee | ||
|
|
8dbbc9a124 | ||
|
|
4297b854e1 | ||
|
|
c53c1d5b9c | ||
|
|
a3db7f0e83 | ||
|
|
157c031236 | ||
|
|
78b1ee04e8 | ||
|
|
30da7d2ea4 | ||
|
|
d467cb5c79 | ||
|
|
9dcb789cfd | ||
|
|
aac504ec0b | ||
|
|
a7a7ce77fb | ||
|
|
6caf78fdcd | ||
|
|
5680d9579c | ||
|
|
ec47645ef7 | ||
|
|
a70acf29ae | ||
|
|
90906912cc | ||
|
|
3064d868b7 | ||
|
|
f226916bb8 | ||
|
|
86b03b9e92 | ||
|
|
9811d6f8ea | ||
|
|
0b4d4764bd | ||
|
|
580deb655d | ||
|
|
5e2bfdf660 | ||
|
|
ec1dcc8df6 | ||
|
|
47ed863da9 | ||
|
|
88290f9e91 | ||
|
|
cfdbc8ae62 | ||
|
|
aaa8f6d311 |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 2.1 MiB |
@@ -11,7 +11,7 @@ tab_width = 4
|
||||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = false
|
||||
ij_formatter_tags_enabled = true
|
||||
ij_smart_tabs = false
|
||||
ij_visual_guides = 300
|
||||
ij_wrap_on_typing = true
|
||||
@@ -78,7 +78,7 @@ ij_editorconfig_space_before_colon = false
|
||||
ij_editorconfig_space_before_comma = false
|
||||
ij_editorconfig_spaces_around_assignment_operators = true
|
||||
|
||||
[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.rng, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul, phpunit.xml.dist}]
|
||||
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}]
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
ij_smart_tabs = true
|
||||
@@ -280,16 +280,17 @@ ij_javascript_while_brace_force = always
|
||||
ij_javascript_while_on_new_line = false
|
||||
ij_javascript_wrap_comments = false
|
||||
|
||||
[{*.ctp, *.hphp, *.inc, *.module, *.php, *.php4, *.php5, *.phtml}]
|
||||
[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}]
|
||||
indent_style = tab
|
||||
ij_continuation_indent_size = 4
|
||||
ij_smart_tabs = true
|
||||
ij_wrap_on_typing = false
|
||||
ij_php_align_assignments = false
|
||||
ij_php_align_class_constants = false
|
||||
ij_php_align_class_constants = true
|
||||
ij_php_align_group_field_declarations = false
|
||||
ij_php_align_inline_comments = false
|
||||
ij_php_align_key_value_pairs = true
|
||||
ij_php_align_match_arm_bodies = false
|
||||
ij_php_align_multiline_array_initializer_expression = true
|
||||
ij_php_align_multiline_binary_operation = false
|
||||
ij_php_align_multiline_chained_methods = false
|
||||
@@ -298,6 +299,7 @@ ij_php_align_multiline_for = true
|
||||
ij_php_align_multiline_parameters = false
|
||||
ij_php_align_multiline_parameters_in_calls = false
|
||||
ij_php_align_multiline_ternary_operation = false
|
||||
ij_php_align_named_arguments = false
|
||||
ij_php_align_phpdoc_comments = false
|
||||
ij_php_align_phpdoc_param_names = false
|
||||
ij_php_anonymous_brace_style = end_of_line
|
||||
@@ -417,6 +419,7 @@ ij_php_see_weight = 3
|
||||
ij_php_since_weight = 28
|
||||
ij_php_sort_phpdoc_elements = true
|
||||
ij_php_space_after_colon = true
|
||||
ij_php_space_after_colon_in_enum_backed_type = true
|
||||
ij_php_space_after_colon_in_named_argument = true
|
||||
ij_php_space_after_colon_in_return_type = true
|
||||
ij_php_space_after_comma = true
|
||||
@@ -431,6 +434,7 @@ ij_php_space_before_catch_parentheses = true
|
||||
ij_php_space_before_class_left_brace = true
|
||||
ij_php_space_before_closure_left_parenthesis = true
|
||||
ij_php_space_before_colon = true
|
||||
ij_php_space_before_colon_in_enum_backed_type = false
|
||||
ij_php_space_before_colon_in_named_argument = false
|
||||
ij_php_space_before_colon_in_return_type = false
|
||||
ij_php_space_before_comma = false
|
||||
@@ -466,6 +470,7 @@ ij_php_spaces_around_equality_operators = true
|
||||
ij_php_spaces_around_logical_operators = true
|
||||
ij_php_spaces_around_multiplicative_operators = true
|
||||
ij_php_spaces_around_null_coalesce_operator = true
|
||||
ij_php_spaces_around_pipe_in_union_type = false
|
||||
ij_php_spaces_around_relational_operators = true
|
||||
ij_php_spaces_around_shift_operators = true
|
||||
ij_php_spaces_around_unary_operator = false
|
||||
@@ -540,7 +545,6 @@ ij_html_space_after_tag_name = false
|
||||
ij_html_space_around_equality_in_attribute = false
|
||||
ij_html_space_inside_empty_tag = false
|
||||
ij_html_text_wrap = normal
|
||||
ij_html_uniform_ident = false
|
||||
|
||||
[{*.markdown,*.md}]
|
||||
ij_visual_guides = none
|
||||
|
||||
@@ -157,4 +157,4 @@ Stickers' design might change from one year to another. For the first year we wa
|
||||
* Beta tester: Test and give feedback on beta releases
|
||||
* Extension developer: Develop and publish an extension
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -99,9 +99,11 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Lucas, Jonathan
|
||||
- Malik, Remie
|
||||
- Mindêllo de Andrade, Lucas (a.k.a @rokam)
|
||||
- Raenker, Martin
|
||||
- Rosenke, Stephan
|
||||
- Seki, Shoji
|
||||
- Shilov, Vladimir
|
||||
- Stukalov, Ilya (a.k.a @ilya-stukalov)
|
||||
- Tulio, Marco
|
||||
- Turrubiates, Miguel
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights,grant_by_profile",
|
||||
"category" => "addon/userrights,grant_by_profile,filter",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
@@ -219,7 +219,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights,grant_by_profile",
|
||||
"category" => "addon/userrights,grant_by_profile,filter",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => array("userlogin", "profile"),
|
||||
"state_attcode" => "",
|
||||
@@ -610,30 +610,115 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
{
|
||||
$this->LoadCache();
|
||||
|
||||
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, UR_ACTION_READ);
|
||||
if ($aObjectPermissions['permission'] == UR_ALLOWED_NO)
|
||||
// Let us pass an administrator for bypassing the grant matrix check in order to test this method without the need to set up a complex profile
|
||||
// In the nominal case Administrators never end up here (since they completely bypass GetSelectFilter)
|
||||
if (!static::IsAdministrator($oUser) && (MetaModel::HasCategory($sClass, 'silo') || MetaModel::HasCategory($sClass, 'bizmodel')))
|
||||
{
|
||||
return false;
|
||||
// N°4354 - Categories 'silo' and 'bizmodel' do check the grant matrix. Whereas 'filter' always allows to read (but the result can be filtered)
|
||||
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, UR_ACTION_READ);
|
||||
if ($aObjectPermissions['permission'] == UR_ALLOWED_NO)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine how to position the objects of this class
|
||||
//
|
||||
$oFilter = true;
|
||||
$aConditions = array();
|
||||
|
||||
// Determine if this class is part of a silo and build the filter for it
|
||||
$sAttCode = self::GetOwnerOrganizationAttCode($sClass);
|
||||
if (is_null($sAttCode))
|
||||
if (!is_null($sAttCode))
|
||||
{
|
||||
// No filtering for this object
|
||||
return true;
|
||||
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
|
||||
if (count($aUserOrgs) > 0)
|
||||
{
|
||||
$oFilter = $this->MakeSelectFilter($sClass, $aUserOrgs, $aSettings, $sAttCode);
|
||||
}
|
||||
// else: No org means 'any org'
|
||||
}
|
||||
// Position the user
|
||||
//
|
||||
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
|
||||
if (count($aUserOrgs) == 0)
|
||||
// else: No silo for this class
|
||||
|
||||
// Specific conditions to hide, for non-administrators, the Administrator Users, the Administrator Profile and related links
|
||||
// Note: when logged as an administrator, GetSelectFilter is completely bypassed.
|
||||
if ($this->AdministratorsAreHidden())
|
||||
{
|
||||
// No org means 'any org'
|
||||
return true;
|
||||
if ($sClass == 'URP_Profiles')
|
||||
{
|
||||
$oExpression = new FieldExpression('id', $sClass);
|
||||
$oScalarExpr = new ScalarExpression(1);
|
||||
|
||||
$aConditions[] = new BinaryExpression($oExpression, '!=', $oScalarExpr);
|
||||
}
|
||||
else if (($sClass == 'URP_UserProfile') || ($sClass == 'User') || (is_subclass_of($sClass, 'User')))
|
||||
{
|
||||
$aAdministrators = $this->GetAdministrators();
|
||||
if (count($aAdministrators) > 0)
|
||||
{
|
||||
$sAttCode = ($sClass == 'URP_UserProfile') ? 'userid' : 'id';
|
||||
$oExpression = new FieldExpression($sAttCode, $sClass);
|
||||
$oListExpr = ListExpression::FromScalars($aAdministrators);
|
||||
$aConditions[] = new BinaryExpression($oExpression, 'NOT IN', $oListExpr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->MakeSelectFilter($sClass, $aUserOrgs, $aSettings, $sAttCode);
|
||||
// Handling of the added conditions
|
||||
if (count($aConditions) > 0)
|
||||
{
|
||||
if($oFilter === true)
|
||||
{
|
||||
// No 'silo' filter, let's build a clean one
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
}
|
||||
|
||||
// Add the conditions to the filter
|
||||
foreach($aConditions as $oCondition)
|
||||
{
|
||||
$oFilter->AddConditionExpression($oCondition);
|
||||
}
|
||||
}
|
||||
|
||||
return $oFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve (and memoize) the list of administrator accounts.
|
||||
* Note that there should always be at least one administrator account
|
||||
* @return number[]
|
||||
*/
|
||||
private function GetAdministrators()
|
||||
{
|
||||
static $aAdministrators = null;
|
||||
|
||||
if ($aAdministrators === null)
|
||||
{
|
||||
// Find all administrators
|
||||
$aAdministrators = array();
|
||||
$oAdministratorsFilter = new DBObjectSearch('User');
|
||||
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
|
||||
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
|
||||
$oScalarExpr = new ScalarExpression(1);
|
||||
$oCondition = new BinaryExpression($oExpression, '=', $oScalarExpr);
|
||||
$oLnkFilter->AddConditionExpression($oCondition);
|
||||
$oAdministratorsFilter->AddCondition_ReferencedBy($oLnkFilter, 'userid');
|
||||
$oAdministratorsFilter->AllowAllData(true); // Mandatory to prevent infinite recursion !!
|
||||
$oSet = new DBObjectSet($oAdministratorsFilter);
|
||||
$oSet->OptimizeColumnLoad(array('User' => array('login')));
|
||||
while($oUser = $oSet->Fetch())
|
||||
{
|
||||
$aAdministrators[] = $oUser->GetKey();
|
||||
}
|
||||
}
|
||||
return $aAdministrators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not to hide the 'Administrator' profile and the administrator accounts
|
||||
* @return boolean
|
||||
*/
|
||||
private function AdministratorsAreHidden()
|
||||
{
|
||||
return ((bool)MetaModel::GetConfig()->Get('security.hide_administrators'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1101,7 +1101,9 @@ class JSButtonItem extends JSPopupMenuItem
|
||||
* @api
|
||||
* @package Extensibility
|
||||
* @since 2.0
|
||||
* @deprecated since 3.0.0 use iPageUIBlockExtension instead
|
||||
* @deprecated 3.0.0 If you need to include:
|
||||
* * JS/CSS files/snippets, use {@see \iBackofficeLinkedScriptsExtension}, {@see \iBackofficeLinkedStylesheetsExtension}, etc instead
|
||||
* * HTML (and optionally JS/CSS), use {@see \iPageUIBlockExtension} to manipulate {@see \Combodo\iTop\Application\UI\Base\UIBlock} instead
|
||||
*/
|
||||
interface iPageUIExtension
|
||||
{
|
||||
@@ -1252,6 +1254,119 @@ abstract class AbstractPageUIBlockExtension implements iPageUIBlockExtension
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add script (JS) files to the backoffice pages
|
||||
*
|
||||
* @see \iTopWebPage::$a_linked_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeLinkedScriptsExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_linked_scripts Each script will be included using this property
|
||||
* @return array An array of absolute URLs to the files to include
|
||||
*/
|
||||
public function GetLinkedScriptsAbsUrls(): array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages' head.
|
||||
* Will be executed first, BEFORE the DOM interpretation.
|
||||
*
|
||||
* @see \iTopWebPage::$a_early_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeEarlyScriptExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_early_scripts
|
||||
* @return string
|
||||
*/
|
||||
public function GetEarlyScript(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages that will be executed immediately, without waiting for the DOM to be ready.
|
||||
*
|
||||
* @see \iTopWebPage::$a_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeScriptExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_scripts
|
||||
* @return string
|
||||
*/
|
||||
public function GetScript(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages that will be executed right when the DOM is ready.
|
||||
*
|
||||
* @see \iTopWebPage::$a_init_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeInitScriptExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_init_scripts
|
||||
* @return string
|
||||
*/
|
||||
public function GetInitScript(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages that will be executed slightly AFTER the DOM is ready (just after the init. scripts).
|
||||
*
|
||||
* @see \iTopWebPage::$a_ready_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeReadyScriptExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_ready_scripts
|
||||
* @return string
|
||||
*/
|
||||
public function GetReadyScript(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add stylesheets (CSS) to the backoffice pages
|
||||
*
|
||||
* @see \iTopWebPage::$a_linked_stylesheets
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeLinkedStylesheetsExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_linked_stylesheets
|
||||
* @return array An array of absolute URLs to the files to include
|
||||
*/
|
||||
public function GetLinkedStylesheetsAbsUrls(): array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline style (CSS) to the backoffice pages' head.
|
||||
*
|
||||
* @see \iTopWebPage::$a_styles
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeStyleExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_styles
|
||||
* @return string
|
||||
*/
|
||||
public function GetStyle(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any enhanced portal page
|
||||
*
|
||||
|
||||
@@ -10,6 +10,7 @@ use Combodo\iTop\Application\Search\SearchForm;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\Button;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSection;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||
@@ -27,7 +28,8 @@ use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockF
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\SelectUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\Title;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\JsPopoverMenuItem;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityPanel;
|
||||
@@ -69,16 +71,42 @@ require_once(APPROOT.'sources/application/search/criterionconversion/criterionto
|
||||
*/
|
||||
abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
/** @var string ENUM_OBJECT_MODE_VIEW */
|
||||
public const ENUM_OBJECT_MODE_VIEW = 'view';
|
||||
/** @var string ENUM_OBJECT_MODE_EDIT */
|
||||
public const ENUM_OBJECT_MODE_EDIT = 'edit';
|
||||
/** @var string ENUM_OBJECT_MODE_CREATE */
|
||||
public const ENUM_OBJECT_MODE_CREATE = 'create';
|
||||
/** @var string ENUM_OBJECT_MODE_STIMULUS */
|
||||
public const ENUM_OBJECT_MODE_STIMULUS = 'stimulus';
|
||||
/** @var string ENUM_OBJECT_MODE_PRINT */
|
||||
public const ENUM_OBJECT_MODE_PRINT = 'print';
|
||||
/**
|
||||
* @var string
|
||||
* @see static::$sDisplayMode
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_DISPLAY_MODE_VIEW = 'view';
|
||||
/**
|
||||
* @var string
|
||||
* @see static::$sDisplayMode
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_DISPLAY_MODE_EDIT = 'edit';
|
||||
/**
|
||||
* @var string
|
||||
* @see static::$sDisplayMode
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_DISPLAY_MODE_CREATE = 'create';
|
||||
/**
|
||||
* @var string
|
||||
* @see static::$sDisplayMode
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_DISPLAY_MODE_STIMULUS = 'stimulus';
|
||||
/**
|
||||
* @var string
|
||||
* @see static::$sDisplayMode
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_DISPLAY_MODE_PRINT = 'print';
|
||||
/**
|
||||
* @var string
|
||||
* @see static::$sDisplayMode
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_DISPLAY_MODE_BULK_EDIT = self::ENUM_DISPLAY_MODE_EDIT;
|
||||
|
||||
// N°3750 rendering used
|
||||
/** @var string */
|
||||
@@ -113,10 +141,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
public const ENUM_INPUT_TYPE_LINKEDSET = 'linkedset';
|
||||
|
||||
/**
|
||||
* @var string DEFAULT_OBJECT_MODE
|
||||
* @var string DEFAULT_DISPLAY_MODE
|
||||
* @see static::$sDisplayMode
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const DEFAULT_OBJECT_MODE = self::ENUM_OBJECT_MODE_VIEW;
|
||||
public const DEFAULT_DISPLAY_MODE = self::ENUM_DISPLAY_MODE_VIEW;
|
||||
|
||||
/**
|
||||
* @var string Prefix for tags in the subtitle, allows to identify them more easily
|
||||
@@ -127,6 +156,11 @@ 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 static $iGlobalFormId = 1;
|
||||
/**
|
||||
* @var string Mode in which the object is displayed {@see static::ENUM_DISPLAY_MODE_VIEW}, ...)
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected $sDisplayMode;
|
||||
protected $aFieldsMap;
|
||||
|
||||
/**
|
||||
@@ -153,28 +187,55 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
|
||||
{
|
||||
parent::__construct($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
|
||||
$this->sDisplayMode = static::DEFAULT_DISPLAY_MODE;
|
||||
$this->bAllowWrite = false;
|
||||
$this->bAllowDelete = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the allowed object modes
|
||||
* Return the allowed display modes
|
||||
*
|
||||
* @see static::ENUM_OBJECT_MODE_XXX
|
||||
* @see static::ENUM_DISPLAY_MODE_XXX
|
||||
*
|
||||
* @return string[]
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function EnumObjectModes(): array
|
||||
public static function EnumDisplayModes(): array
|
||||
{
|
||||
return [
|
||||
static::ENUM_OBJECT_MODE_VIEW,
|
||||
static::ENUM_OBJECT_MODE_EDIT,
|
||||
static::ENUM_OBJECT_MODE_CREATE,
|
||||
static::ENUM_OBJECT_MODE_STIMULUS,
|
||||
static::ENUM_DISPLAY_MODE_VIEW,
|
||||
static::ENUM_DISPLAY_MODE_EDIT,
|
||||
static::ENUM_DISPLAY_MODE_CREATE,
|
||||
static::ENUM_DISPLAY_MODE_STIMULUS,
|
||||
static::ENUM_DISPLAY_MODE_PRINT,
|
||||
static::ENUM_DISPLAY_MODE_BULK_EDIT,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see static::$sDisplayMode
|
||||
* @return string
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetDisplayMode(): string
|
||||
{
|
||||
return $this->sDisplayMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sMode
|
||||
*
|
||||
* @see static::$sDisplayMode
|
||||
* @return $this
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetDisplayMode(string $sMode)
|
||||
{
|
||||
$this->sDisplayMode = $sMode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns what will be the next ID for the forms
|
||||
*/
|
||||
@@ -283,8 +344,7 @@ JS
|
||||
* To insert something IN the panel, we now need to add UIBlocks in either the "subtitle" or "toolbar" sections of the array that will be returned.
|
||||
*
|
||||
* @param \WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
* @param string $sMode Mode in which the object is displayed (see static::ENUM_OBJECT_MODE_XXX)
|
||||
* @param bool $bEditMode Note that this parameter is no longer used in this method. Use {@see static::$sDisplayMode} instead
|
||||
*
|
||||
* @return array UIBlocks to be inserted in the "subtitle" and the "toolbar" sections of the ObjectDetails block. eg. ['subtitle' => [<BLOCK1>, <BLOCK2>], 'toolbar' => [<BLOCK3>]]
|
||||
*
|
||||
@@ -294,10 +354,10 @@ JS
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
* @todo 3.0.0: This has to be discussed within the R&D when I come back in 10 days
|
||||
*
|
||||
* @since 3.0.0 $bEditMode is deprecated and no longer used
|
||||
*/
|
||||
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false, $sMode = self::ENUM_OBJECT_MODE_VIEW)
|
||||
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
$aHeaderBlocks = [
|
||||
'subtitle' => [],
|
||||
@@ -325,7 +385,7 @@ JS
|
||||
$oPage->AddSessionMessages($sMessageKey, $aRanks, $aMessages);
|
||||
}
|
||||
|
||||
if (!$oPage->IsPrintableVersion() && ($sMode === static::ENUM_OBJECT_MODE_VIEW)) {
|
||||
if (!$oPage->IsPrintableVersion() && ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_VIEW)) {
|
||||
// action menu
|
||||
$oSingletonFilter = new DBObjectSearch(get_class($this));
|
||||
$oSingletonFilter->AddCondition('id', $this->GetKey(), '=');
|
||||
@@ -477,12 +537,14 @@ HTML
|
||||
* Display properties tab of an object
|
||||
*
|
||||
* @param \WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
* @param bool $bEditMode Note that this parameter is no longer used in this method. Use {@see static::$sDisplayMode} instead
|
||||
* @param string $sPrefix
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 3.0.0 $bEditMode is deprecated and no longer used
|
||||
*/
|
||||
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
{
|
||||
@@ -555,7 +617,7 @@ HTML
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
* @param bool $bEditMode Note that this parameter is no longer used in this method. Use {@see static::$sDisplayMode} instead
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
@@ -565,6 +627,8 @@ HTML
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 3.0.0 $bEditMode is deprecated and no longer used
|
||||
*/
|
||||
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
@@ -582,12 +646,16 @@ HTML
|
||||
foreach($aList as $sAttCode) {
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef instanceof AttributeDashboard) {
|
||||
$sHostContainerInEditionUrlParam = ($bEditMode) ? '&host_container_in_edition=true' : '';
|
||||
$oPage->AddAjaxTab($oAttDef->GetLabel(),
|
||||
utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=dashboard&class='.get_class($this).'&id='.$this->GetKey().'&attcode='.$oAttDef->GetCode().$sHostContainerInEditionUrlParam,
|
||||
true,
|
||||
'Class:'.$sClass.'/Attribute:'.$sAttCode,
|
||||
AjaxTab::ENUM_TAB_PLACEHOLDER_DASHBOARD);
|
||||
if (!$this->IsNew()) {
|
||||
$sHostContainerInEditionUrlParam = ($bEditMode) ? '&host_container_in_edition=true' : '';
|
||||
$oPage->AddAjaxTab($oAttDef->GetLabel(),
|
||||
utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=dashboard&class='.get_class($this).'&id='.$this->GetKey().'&attcode='.$oAttDef->GetCode().$sHostContainerInEditionUrlParam,
|
||||
true,
|
||||
'Class:'.$sClass.'/Attribute:'.$sAttCode,
|
||||
AjaxTab::ENUM_TAB_PLACEHOLDER_DASHBOARD);
|
||||
// Add graphs dependencies
|
||||
WebResourcesHelper::EnableC3JSToWebPage($oPage);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -782,33 +850,35 @@ HTML
|
||||
|
||||
$oPage->SetCurrentTab('');
|
||||
|
||||
// Look for any trigger that considers this object as "In Scope"
|
||||
// If any trigger has been found then display a tab with notifications
|
||||
//
|
||||
$aTriggers = $this->GetRelatedTriggersIDs();
|
||||
if (count($aTriggers) > 0) {
|
||||
$iId = $this->GetKey();
|
||||
$aParams = array('triggers' => $aTriggers, 'id' => $iId);
|
||||
$aNotifSearches = array();
|
||||
$iNotifsCount = 0;
|
||||
$aNotificationClasses = MetaModel::EnumChildClasses('EventNotification', ENUM_CHILD_CLASSES_EXCLUDETOP);
|
||||
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]->SetInternalParams($aParams);
|
||||
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], array());
|
||||
$iNotifsCount += $oNotifSet->Count();
|
||||
}
|
||||
// Display notifications regarding the object: on block per subclass to have the interesting columns
|
||||
$sCount = ($iNotifsCount > 0) ? ' ('.$iNotifsCount.')' : '';
|
||||
$oPage->SetCurrentTab('UI:NotificationsTab', Dict::S('UI:NotificationsTab').$sCount);
|
||||
if (!$this->IsNew()) {
|
||||
// Look for any trigger that considers this object as "In Scope"
|
||||
// If any trigger has been found then display a tab with notifications
|
||||
//
|
||||
$aTriggers = $this->GetRelatedTriggersIDs();
|
||||
if (count($aTriggers) > 0) {
|
||||
$iId = $this->GetKey();
|
||||
$aParams = array('triggers' => $aTriggers, 'id' => $iId);
|
||||
$aNotifSearches = array();
|
||||
$iNotifsCount = 0;
|
||||
$aNotificationClasses = MetaModel::EnumChildClasses('EventNotification');
|
||||
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]->SetInternalParams($aParams);
|
||||
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], array());
|
||||
$iNotifsCount += $oNotifSet->Count();
|
||||
}
|
||||
// Display notifications regarding the object: on block per subclass to have the interesting columns
|
||||
$sCount = ($iNotifsCount > 0) ? ' ('.$iNotifsCount.')' : '';
|
||||
$oPage->SetCurrentTab('UI:NotificationsTab', Dict::S('UI:NotificationsTab').$sCount);
|
||||
|
||||
foreach($aNotificationClasses as $sNotifClass) {
|
||||
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sNotifClass, false));
|
||||
$oClassIcon->SetDescription(MetaModel::GetName($sNotifClass))->AddCSSClass('ibo-block-list--medallion');
|
||||
$oPage->AddUiBlock($oClassIcon);
|
||||
foreach ($aNotificationClasses as $sNotifClass) {
|
||||
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sNotifClass, false));
|
||||
$oClassIcon->SetDescription(MetaModel::GetName($sNotifClass))->AddCSSClass('ibo-block-list--medallion');
|
||||
$oPage->AddUiBlock($oClassIcon);
|
||||
|
||||
$oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false);
|
||||
$oBlock->Display($oPage, 'notifications_'.$sNotifClass, array('menu' => false));
|
||||
$oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false);
|
||||
$oBlock->Display($oPage, 'notifications_'.$sNotifClass, array('menu' => false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -822,11 +892,17 @@ HTML
|
||||
*/
|
||||
public function GetRelatedTriggersIDs(): array
|
||||
{
|
||||
$oTriggerSet = new CMDBObjectSet(new DBObjectSearch('Trigger'));
|
||||
$aTriggers = [];
|
||||
while ($oTrigger = $oTriggerSet->Fetch()) {
|
||||
if ($oTrigger->IsInScope($this)) {
|
||||
$aTriggers[] = $oTrigger->GetKey();
|
||||
// Request only "leaf" classes to avoid reloads
|
||||
$aTriggerClasses = MetaModel::EnumChildClasses('Trigger');
|
||||
foreach ($aTriggerClasses as $sTriggerClass) {
|
||||
if (MetaModel::IsLeafClass($sTriggerClass)) {
|
||||
$oTriggerSet = new CMDBObjectSet(new DBObjectSearch($sTriggerClass));
|
||||
while ($oTrigger = $oTriggerSet->Fetch()) {
|
||||
if ($oTrigger->IsInScope($this)) {
|
||||
$aTriggers[] = $oTrigger->GetKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -835,7 +911,7 @@ HTML
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
* @param bool $bEditMode Note that this parameter is no longer used in this method. Use {@see static::$sDisplayMode} instead
|
||||
* @param string $sPrefix
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
@@ -847,6 +923,8 @@ HTML
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 3.0.0 $bEditMode is deprecated and no longer used
|
||||
*/
|
||||
public function GetBareProperties(WebPage $oPage, $bEditMode, $sPrefix, $aExtraParams = array())
|
||||
{
|
||||
@@ -1028,8 +1106,7 @@ HTML
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param bool $bEditMode Note that this parameter is no longer used in ths method, $sMode is used instead, but we cannot remove it as it part of the base interface (iDisplay)...
|
||||
* @param string $sMode Mode in which the object will be displayed (see static::ENUM_OBJECT_MODE_XXX)
|
||||
* @param bool $bEditMode Note that this parameter is no longer used in this method, {@see static::$sDisplayMode} is used instead, but we cannot remove it as it part of the base interface (iDisplay)...
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
@@ -1040,8 +1117,10 @@ HTML
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*
|
||||
* @since 3.0.0 $bEditMode is deprecated and no longer used
|
||||
*/
|
||||
public function DisplayDetails(WebPage $oPage, $bEditMode = false, $sMode = self::ENUM_OBJECT_MODE_VIEW)
|
||||
public function DisplayDetails(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
// N°3786: As this can now be call recursively from the self::ReloadAndDisplay(), we need to make sure we don't fall into an infinite loop
|
||||
static $bBlockReentrance = false;
|
||||
@@ -1049,15 +1128,12 @@ HTML
|
||||
$sClass = get_class($this);
|
||||
$iKey = $this->GetKey();
|
||||
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_VIEW)
|
||||
{
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_VIEW) {
|
||||
// The concurrent access lock makes sense only for already existing objects
|
||||
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
||||
if ($LockEnabled)
|
||||
{
|
||||
if ($LockEnabled) {
|
||||
$aLockInfo = iTopOwnershipLock::IsLocked($sClass, $iKey);
|
||||
if ($aLockInfo['locked'] === true && $aLockInfo['owner']->GetKey() == UserRights::GetUserId() && $bBlockReentrance === false)
|
||||
{
|
||||
if ($aLockInfo['locked'] === true && $aLockInfo['owner']->GetKey() == UserRights::GetUserId() && $bBlockReentrance === false) {
|
||||
// If the object is locked by the current user, it's worth trying again, since
|
||||
// the lock may be released by 'onunload' which is called AFTER loading the current page.
|
||||
//$bTryAgain = $oOwner->GetKey() == UserRights::GetUserId();
|
||||
@@ -1070,11 +1146,14 @@ HTML
|
||||
}
|
||||
|
||||
// Object's details
|
||||
$oObjectDetails = ObjectFactory::MakeDetails($this);
|
||||
$oObjectDetails = ObjectFactory::MakeDetails($this, $this->GetDisplayMode());
|
||||
if ($oPage->IsPrintableVersion()) {
|
||||
$oObjectDetails->SetIsHeaderVisibleOnScroll(false);
|
||||
}
|
||||
|
||||
// Note: DisplayBareHeader is called before adding $oObjectDetails to the page, so it can inject HTML before it through $oPage.
|
||||
/** @var \iTopWebPage $oPage */
|
||||
$aHeadersBlocks = $this->DisplayBareHeader($oPage, $bEditMode, $sMode);
|
||||
$aHeadersBlocks = $this->DisplayBareHeader($oPage, $bEditMode);
|
||||
if (false === empty($aHeadersBlocks['subtitle'])) {
|
||||
$oObjectDetails->AddSubTitleBlocks($aHeadersBlocks['subtitle']);
|
||||
}
|
||||
@@ -1189,6 +1268,25 @@ HTML
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param \DBObjectSet $oSet
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @return \Combodo\iTop\Application\UI\Base\Layout\UIContentBlock|string
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
* @throws \ReflectionException
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetDisplaySetBlock(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
|
||||
{
|
||||
if ($oPage->IsPrintableVersion() || $oPage->is_pdf()) {
|
||||
@@ -2056,10 +2154,8 @@ HTML;
|
||||
$sHours = "<input class=\"ibo-input ibo-input-duration\" title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[h]{$sNameSuffix}\" value=\"{$aVal['hours']}\" id=\"{$iId}_h\"/>";
|
||||
$sMinutes = "<input class=\"ibo-input ibo-input-duration\" title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[m]{$sNameSuffix}\" value=\"{$aVal['minutes']}\" id=\"{$iId}_m\"/>";
|
||||
$sSeconds = "<input class=\"ibo-input ibo-input-duration\" title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[s]{$sNameSuffix}\" value=\"{$aVal['seconds']}\" id=\"{$iId}_s\"/>";
|
||||
$sHidden = "<input type=\"hidden\" id=\"{$iId}\" value=\"".htmlentities($value, ENT_QUOTES,
|
||||
'UTF-8')."\"/>";
|
||||
$sHTMLValue = Dict::Format('UI:DurationForm_Days_Hours_Minutes_Seconds', $sDays, $sHours, $sMinutes,
|
||||
$sSeconds).$sHidden." ".$sValidationSpan.$sReloadSpan;
|
||||
$sHidden = "<input type=\"hidden\" id=\"{$iId}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\"/>";
|
||||
$sHTMLValue = Dict::Format('UI:DurationForm_Days_Hours_Minutes_Seconds', $sDays, $sHours, $sMinutes, $sSeconds).$sHidden." ".$sValidationSpan.$sReloadSpan;
|
||||
$oPage->add_ready_script("$('#{$iId}').on('update', function(evt, sFormId) { return ToggleDurationField('$iId'); });");
|
||||
break;
|
||||
|
||||
@@ -2068,7 +2164,7 @@ HTML;
|
||||
$aEventsList[] = 'validate';
|
||||
$aEventsList[] = 'keyup';
|
||||
$aEventsList[] = 'change';
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_password\"><input title=\"$sHelpText\" type=\"password\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($value,
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_password ibo-input-wrapper ibo-input-password-wrapper\" data-validation=\"untouched\"><input class=\"ibo-input ibo-input-password\" title=\"$sHelpText\" type=\"password\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($value,
|
||||
ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/></div>{$sValidationSpan}{$sReloadSpan}";
|
||||
break;
|
||||
|
||||
@@ -2097,7 +2193,11 @@ HTML;
|
||||
$sStyle = 'style="'.implode('; ', $aStyles).'"';
|
||||
}
|
||||
|
||||
$aTextareaCssClasses = [];
|
||||
|
||||
if ($oAttDef->GetEditClass() == 'OQLExpression') {
|
||||
$aTextareaCssClasses[] = 'ibo-query-oql';
|
||||
$aTextareaCssClasses[] = 'ibo-is-code';
|
||||
// N°3227 button to open predefined queries dialog
|
||||
$sPredefinedBtnId = 'predef_btn_'.$sFieldPrefix.$sAttCode.$sNameSuffix;
|
||||
$sSearchQueryLbl = Dict::S('UI:Edit:SearchQuery');
|
||||
@@ -2167,14 +2267,16 @@ JS
|
||||
} else {
|
||||
$sAdditionalStuff = '';
|
||||
}
|
||||
|
||||
// Ok, the text area is drawn here
|
||||
$sTextareCssClassesAsString = implode(' ', $aTextareaCssClasses);
|
||||
$sHTMLValue = <<<HTML
|
||||
{$sAdditionalStuff}
|
||||
<div class="field_input_zone field_input_text ibo-input-wrapper ibo-input-text-wrapper" data-validation="untouched">
|
||||
<div class="f_i_text_header">
|
||||
<span class="fullscreen_button" title="{$sFullscreenLabelForHtml}"></span>
|
||||
</div>
|
||||
<textarea class="ibo-input ibo-input-text" title="{$sHelpText}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}" rows="8" cols="40" id="{$iId}" {$sStyle} >{$sEditValueForHtml}</textarea>
|
||||
<textarea class="ibo-input ibo-input-text {$sTextareCssClassesAsString}" title="{$sHelpText}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}" rows="8" cols="40" id="{$iId}" {$sStyle} >{$sEditValueForHtml}</textarea>
|
||||
</div>
|
||||
{$sValidationSpan}{$sReloadSpan}
|
||||
HTML;
|
||||
@@ -2318,7 +2420,6 @@ EOF
|
||||
<span class="fas fa-trash"></span>
|
||||
</button>
|
||||
</div>
|
||||
<br/>
|
||||
<input title="{$sHelpText}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[fcontents]" type="file" id="file_{$iId}" onChange="UpdateFileName('{$iId}', this.value)"/>
|
||||
{$sValidationSpan}{$sReloadSpan}
|
||||
HTML;
|
||||
@@ -2666,10 +2767,11 @@ JS
|
||||
$sOwnershipToken = null;
|
||||
$iKey = $this->GetKey();
|
||||
$sClass = get_class($this);
|
||||
$sMode = ($iKey > 0) ? static::ENUM_OBJECT_MODE_EDIT : static::ENUM_OBJECT_MODE_CREATE;
|
||||
|
||||
$this->SetDisplayMode(($iKey > 0) ? static::ENUM_DISPLAY_MODE_EDIT : static::ENUM_DISPLAY_MODE_CREATE);
|
||||
$sDisplayMode = $this->GetDisplayMode();
|
||||
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT)
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT)
|
||||
{
|
||||
// The concurrent access lock makes sense only for already existing objects
|
||||
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
||||
@@ -2719,7 +2821,7 @@ JS
|
||||
if (isset($aExtraParams['custom_button'])) {
|
||||
$sApplyButton = $aExtraParams['custom_button'];
|
||||
} else {
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT) {
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
$sApplyButton = Dict::S('UI:Button:Apply');
|
||||
} else {
|
||||
$sApplyButton = Dict::S('UI:Button:Create');
|
||||
@@ -2729,7 +2831,7 @@ JS
|
||||
if (isset($aExtraParams['custom_operation'])) {
|
||||
$sOperation = $aExtraParams['custom_operation'];
|
||||
} else {
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT) {
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
$sOperation = 'apply_modify';
|
||||
} else {
|
||||
$sOperation = 'apply_new';
|
||||
@@ -2744,7 +2846,7 @@ JS
|
||||
->SetOnSubmitJsCode("return OnSubmit('form_{$this->m_iFormId}');");
|
||||
$oContentBlock->AddSubBlock($oForm);
|
||||
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT) {
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
// The object already exists in the database, it's a modification
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
|
||||
}
|
||||
@@ -2759,7 +2861,11 @@ JS
|
||||
// TODO 3.0.0: Is this (the if condition, not the code inside) still necessary?
|
||||
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container']) {
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $this->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
|
||||
if ($this->GetDisplayMode() == static::ENUM_DISPLAY_MODE_CREATE) {
|
||||
$oPage->set_title(Dict::Format('UI:CreationPageTitle_Class', $sClassLabel)); // Set title will take care of the encoding
|
||||
} else {
|
||||
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $this->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
|
||||
}
|
||||
}
|
||||
|
||||
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
|
||||
@@ -2773,24 +2879,41 @@ JS
|
||||
|
||||
$aTransitions = $this->EnumTransitions();
|
||||
if (!isset($aExtraParams['custom_operation']) && count($aTransitions)) {
|
||||
// transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
|
||||
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
|
||||
$oSetToCheckRights = DBObjectSet::FromObject($this);
|
||||
|
||||
$oTransitionPopoverMenu = new PopoverMenu();
|
||||
$sTPMSectionId = 'transitions';
|
||||
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
|
||||
$aStimuli = Metamodel::EnumStimuli($sClass);
|
||||
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
|
||||
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
|
||||
switch ($iActionAllowed) {
|
||||
case UR_ALLOWED_YES:
|
||||
// Button to be displayed on its own on large screens
|
||||
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
|
||||
$oButton->AddCSSClass('action');
|
||||
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
|
||||
$oToolbarButtons->AddSubBlock($oButton);
|
||||
|
||||
// Button to be displayed in a grouped button on smaller screens
|
||||
$oTPMPopupMenuItem = new JSPopupMenuItem('next_action--'.$oButton->GetId(), $oButton->GetLabel(), "$(`#{$oButton->GetId()}`).trigger(`click`);");
|
||||
$oTransitionPopoverMenu->AddItem($sTPMSectionId, new JsPopoverMenuItem($oTPMPopupMenuItem));
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// If there are some allowed transitions, build the grouped button
|
||||
if ($oTransitionPopoverMenu->HasItems()) {
|
||||
$oApplyForButtonGroup = ButtonUIBlockFactory::MakeForPrimaryAction($oApplyButton->GetLabel(), null, null, true);
|
||||
$oApplyAndTransitionsButtonGroup = ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu($oApplyForButtonGroup, $oTransitionPopoverMenu)
|
||||
->SetIsHidden(true);
|
||||
$oToolbarButtons->AddSubBlock($oApplyAndTransitionsButtonGroup);
|
||||
}
|
||||
}
|
||||
|
||||
$sStatesSelection = '';
|
||||
@@ -2848,7 +2971,7 @@ EOF
|
||||
$oObjectDetails->SetIcon($sClassIcon);
|
||||
$oToolbarButtons->AddCSSClass('ibo-toolbar--button');
|
||||
} else {
|
||||
$oObjectDetails = ObjectFactory::MakeDetails($this, $sMode);
|
||||
$oObjectDetails = ObjectFactory::MakeDetails($this, $this->GetDisplayMode());
|
||||
$oToolbarButtons->AddCSSClass('ibo-toolbar-top');
|
||||
$oObjectDetails->AddToolbarBlock($oToolbarButtons);
|
||||
}
|
||||
@@ -2879,7 +3002,7 @@ EOF
|
||||
if (!is_array($aFieldsMap)) {
|
||||
$aFieldsMap = array();
|
||||
}
|
||||
if ($sMode === static::ENUM_OBJECT_MODE_EDIT) {
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
$aFieldsMap['id'] = $sPrefix.'_id';
|
||||
}
|
||||
// Now display the relations, one tab per relation
|
||||
@@ -3070,6 +3193,7 @@ EOF
|
||||
} else {
|
||||
$oObj = clone $oSourceObject;
|
||||
}
|
||||
$oObj->SetDisplayMode(static::ENUM_DISPLAY_MODE_CREATE);
|
||||
|
||||
// Pre-fill the object with default values, when there is only on possible choice
|
||||
// AND the field is mandatory (otherwise there is always the possiblity to let it empty)
|
||||
@@ -3125,10 +3249,10 @@ EOF
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param string $sStimulus
|
||||
* @param null $aPrefillFormParam
|
||||
* @param bool $bDisplayBareProperties Whether to display the object details or not
|
||||
* @param \WebPage $oPage
|
||||
* @param string $sStimulus
|
||||
* @param array|null $aPrefillFormParam
|
||||
* @param bool $bDisplayBareProperties Whether to display the object details or not
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
@@ -3142,9 +3266,11 @@ EOF
|
||||
*/
|
||||
public function DisplayStimulusForm(WebPage $oPage, $sStimulus, $aPrefillFormParam = null, $bDisplayBareProperties = true)
|
||||
{
|
||||
$this->SetDisplayMode(static::ENUM_DISPLAY_MODE_STIMULUS);
|
||||
|
||||
$sClass = get_class($this);
|
||||
$iKey = $this->GetKey();
|
||||
$sMode = static::ENUM_OBJECT_MODE_STIMULUS;
|
||||
$sDisplayMode = $this->GetDisplayMode();
|
||||
$iTransactionId = utils::GetNewTransactionId();
|
||||
|
||||
$aTransitions = $this->EnumTransitions();
|
||||
@@ -3296,11 +3422,11 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
if ($bExistFieldToDisplay) {
|
||||
if ($bExistFieldToDisplay || MetaModel::GetConfig()->Get('force_transition_confirmation')) {
|
||||
$oPage->set_title($sActionLabel);
|
||||
$oPage->add(<<<HTML
|
||||
<!-- Beginning of object-transition -->
|
||||
<div class="object-transition" data-object-class="$sClass" data-object-id="$iKey" data-object-mode="$sMode" data-object-current-state="$sCurrentState" data-object-target-state="$sTargetState">
|
||||
<div class="object-transition" data-object-class="$sClass" data-object-id="$iKey" data-object-mode="$sDisplayMode" data-object-current-state="$sCurrentState" data-object-target-state="$sTargetState">
|
||||
HTML
|
||||
);
|
||||
|
||||
@@ -3326,8 +3452,10 @@ HTML
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('ownership_token', utils::HtmlEntities($sOwnershipToken)));
|
||||
}
|
||||
|
||||
// Note: Remove the table if we want fields to occupy the whole width of the container
|
||||
$sHtml = '<table><tr><td>';
|
||||
// Note: Remove the table if we want fields to occupy the whole width of the container, BUT with today's layout, fields' label will occupy way too much space. This should be part of the field layout rework.
|
||||
// Note 2: The hardcoded width allows the fields to be a bit wider (useful for long values) while still working on different screen sizes
|
||||
// Note 3: The inline style is not ideal but we are still wondring how transition form should be displayed
|
||||
$sHtml = '<table style="width: min(100%, 35rem); margin-bottom: 12px;"><tr><td>';
|
||||
$sHtml .= $oPage->GetDetails($aDetails);
|
||||
$sHtml .= '</td></tr></table>';
|
||||
|
||||
@@ -3335,8 +3463,10 @@ HTML
|
||||
$sHtml .= $oAppContext->GetForForm();
|
||||
$oForm->AddHtml($sHtml);
|
||||
|
||||
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel');
|
||||
$oCancelButton->SetOnClickJsCode("BackToDetails('{$sClass}', '{$this->GetKey()}', '', '{$sOwnershipToken}');");
|
||||
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel')
|
||||
// Action type is changed on purpose so the button is more visible in the form.
|
||||
->SetActionType(Button::ENUM_ACTION_TYPE_REGULAR)
|
||||
->SetOnClickJsCode("BackToDetails('{$sClass}', '{$this->GetKey()}', '', '{$sOwnershipToken}');");
|
||||
$oForm->AddSubBlock($oCancelButton);
|
||||
|
||||
$oSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction($sActionLabel, 'submit', 'submit', true);
|
||||
@@ -3513,7 +3643,7 @@ HTML;
|
||||
$sDownloadUrl = $oDocument->GetDownloadURL(get_class($this), $this->GetKey(), $sAttCode);
|
||||
|
||||
$sDisplayValue = <<<HTML
|
||||
{$sFieldAsHtml}<br>
|
||||
{$sFieldAsHtml}
|
||||
<a href="{$sDisplayUrl}" target="_blank">{$sDisplayLabel}</a> / <a href="{$sDownloadUrl}">{$sDownloadLabel}</a>
|
||||
HTML;
|
||||
} else {
|
||||
@@ -4698,7 +4828,9 @@ HTML
|
||||
|
||||
$aFieldsMap[$sAttCode] = $sInputId;
|
||||
|
||||
$oFieldset = FieldSetUIBlockFactory::MakeStandard($sAttLabel);
|
||||
$sCommentAsHtml = ($sComment != '') ? ' <div class="ibo-field--comments">'.$sComment.'</div>' : '';
|
||||
|
||||
$oFieldset = FieldSetUIBlockFactory::MakeStandard($sAttLabel.$sCommentAsHtml);
|
||||
$oPage->AddSubBlock($oFieldset);
|
||||
|
||||
$oDivField = FieldUIBlockFactory::MakeLarge("");
|
||||
@@ -4712,10 +4844,9 @@ HTML
|
||||
$oDivField->AddDataAttribute("attribute-flag-must-prompt", $sAttMetaDataFlagMustPrompt);
|
||||
$oDivField->AddDataAttribute("attribute-flag-slave", false);
|
||||
$oFieldset->AddSubBlock($oDivField);
|
||||
|
||||
$sCommentAsHtml = ($sComment != '') ? '<span>'.$sComment.'</span><br/>' : '';
|
||||
//$oDivField->SetComments($sComment);
|
||||
$sFieldAsHtml = self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs);
|
||||
$sHTMLValue = $sCommentAsHtml.$sFieldAsHtml;
|
||||
$sHTMLValue = $sFieldAsHtml;
|
||||
$oDivField->AddSubBlock(new Html($sHTMLValue));
|
||||
}
|
||||
}
|
||||
@@ -4853,6 +4984,7 @@ HTML
|
||||
// Now create an object that has values for the homogeneous values only
|
||||
/** @var \cmdbAbstractObject $oDummyObj */
|
||||
$oDummyObj = new $sClass(); // @@ What if the class is abstract ?
|
||||
$oDummyObj->SetDisplayMode(static::ENUM_DISPLAY_MODE_BULK_EDIT);
|
||||
$aComments = array();
|
||||
function MyComparison($a, $b) // Sort descending
|
||||
{
|
||||
@@ -5164,7 +5296,7 @@ EOF
|
||||
if ($bPreview) {
|
||||
if (count($aObjects) == 1) {
|
||||
$oObj = $aObjects[0];
|
||||
$sTitle = Dict::Format('UI:Delete:ConfirmDeletionOf_Name', $oObj->GetName());
|
||||
$sTitle = Dict::Format('UI:Delete:ConfirmDeletionOf_Name', $oObj->GetRawName());
|
||||
} else {
|
||||
$sTitle = Dict::Format('UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class', count($aObjects),
|
||||
MetaModel::GetName($sClass));
|
||||
@@ -5178,6 +5310,7 @@ EOF
|
||||
foreach ($aDeletes as $iId => $aData) {
|
||||
$oToDelete = $aData['to_delete'];
|
||||
$bAutoDel = (($aData['mode'] == DEL_SILENT) || ($aData['mode'] == DEL_AUTO));
|
||||
$sRowCssClass = '';
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
if ($bAutoDel)
|
||||
@@ -5197,6 +5330,7 @@ EOF
|
||||
$sConsequence = Dict::Format('UI:Delete:MustBeDeletedManuallyButNotPossible',
|
||||
$aData['issue']);
|
||||
}
|
||||
$sRowCssClass = 'ibo-is-alert';
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5214,9 +5348,11 @@ EOF
|
||||
else
|
||||
{
|
||||
$sConsequence = Dict::S('UI:Delete:MustBeDeletedManually');
|
||||
$sRowCssClass = 'ibo-is-warning';
|
||||
}
|
||||
}
|
||||
$aDisplayData[] = array(
|
||||
'@class' => $sRowCssClass,
|
||||
'class' => MetaModel::GetName(get_class($oToDelete)),
|
||||
'object' => $oToDelete->GetHyperLink(),
|
||||
'consequence' => $sConsequence,
|
||||
@@ -5228,9 +5364,11 @@ EOF
|
||||
foreach($aToUpdate as $iId => $aData)
|
||||
{
|
||||
$oToUpdate = $aData['to_reset'];
|
||||
$sRowCssClass = '';
|
||||
if (array_key_exists('issue', $aData))
|
||||
{
|
||||
$sConsequence = Dict::Format('UI:Delete:CannotUpdateBecause_Issue', $aData['issue']);
|
||||
$sRowCssClass = 'ibo-is-alert';
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -5238,6 +5376,7 @@ EOF
|
||||
$aData['attributes_list']);
|
||||
}
|
||||
$aDisplayData[] = array(
|
||||
'@class' => $sRowCssClass,
|
||||
'class' => MetaModel::GetName(get_class($oToUpdate)),
|
||||
'object' => $oToUpdate->GetHyperLink(),
|
||||
'consequence' => $sConsequence,
|
||||
@@ -5281,13 +5420,16 @@ EOF
|
||||
|
||||
if ($oDeletionPlan->FoundStopper()) {
|
||||
if ($oDeletionPlan->FoundSecurityIssue()) {
|
||||
$oP->p(Dict::S('UI:Delete:SorryDeletionNotAllowed'));
|
||||
} elseif ($oDeletionPlan->FoundManualOperation()) {
|
||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
} else // $bFoundManualOp
|
||||
{
|
||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
$oFailAlertBlock = AlertUIBlockFactory::MakeForDanger('', Dict::S('UI:Delete:SorryDeletionNotAllowed'));
|
||||
$oFailAlertBlock->SetIsClosable(false);
|
||||
$oP->AddUiBlock($oFailAlertBlock);
|
||||
}
|
||||
else {
|
||||
$oWarningAlertBlock = AlertUIBlockFactory::MakeForWarning('', Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
$oWarningAlertBlock->SetIsClosable(false);
|
||||
$oP->AddUiBlock($oWarningAlertBlock);
|
||||
}
|
||||
|
||||
$oForm = FormUIBlockFactory::MakeStandard('');
|
||||
$oP->AddSubBlock($oForm);
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::ReadParam('transaction_id', '', false, 'transaction_id')));
|
||||
@@ -5321,6 +5463,7 @@ EOF
|
||||
'menu' => false,
|
||||
'surround_with_panel' => true,
|
||||
'panel_title' => $sSubtitle,
|
||||
'panel_title_is_html' => true,
|
||||
'panel_icon' => MetaModel::GetClassIcon($sClass, false),
|
||||
'panel_class' => $sClass,
|
||||
)));
|
||||
@@ -5354,7 +5497,7 @@ EOF
|
||||
//
|
||||
if (count($aObjects) == 1) {
|
||||
$oObj = $aObjects[0];
|
||||
$sTitle = Dict::Format('UI:Title:DeletionOf_Object', $oObj->GetName());
|
||||
$sTitle = Dict::Format('UI:Title:DeletionOf_Object', $oObj->GetRawName());
|
||||
} else {
|
||||
$sTitle = Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass));
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ EOF
|
||||
$oToolbar->AddHtml($sHtml);
|
||||
} else {
|
||||
$oPage->add_script(<<<JS
|
||||
$(".ibo-top-bar--toolbar-dashboard-title").html("$sTitleForHTML").attr("title", "$sTitleForHTML");
|
||||
$(".ibo-top-bar--toolbar-dashboard-title").html("$sTitleForHTML").attr("title", $('<div>').html("$sTitleForHTML").text());
|
||||
JS
|
||||
);
|
||||
}
|
||||
@@ -860,28 +860,29 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
$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
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
|
||||
$oUDSet = new DBObjectSet($oUDSearch);
|
||||
if ($oUDSet->Count() > 0)
|
||||
{
|
||||
if ($oUDSet->Count() > 0) {
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||
$bCustomized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
} else {
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||
}
|
||||
|
||||
if ($sDashboardDefinition !== false)
|
||||
@@ -889,7 +890,7 @@ class RuntimeDashboard extends Dashboard
|
||||
$oDashboard = new RuntimeDashboard($sDashBoardId);
|
||||
$oDashboard->FromXml($sDashboardDefinition);
|
||||
$oDashboard->SetCustomFlag($bCustomized);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFile);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
|
||||
} else {
|
||||
$oDashboard = null;
|
||||
}
|
||||
|
||||
@@ -1008,7 +1008,8 @@ HTML;
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||
@@ -1045,7 +1046,8 @@ HTML;
|
||||
|
||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $sOQL);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||
@@ -1395,7 +1397,8 @@ abstract class DashletGroupBy extends Dashlet
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
try {
|
||||
@@ -1652,7 +1655,8 @@ abstract class DashletGroupBy extends Dashlet
|
||||
|
||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $sOQL);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
if (!is_null($sOQL)) {
|
||||
@@ -2225,7 +2229,8 @@ class DashletHeaderDynamic extends Dashlet
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletHeaderDynamic:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
try
|
||||
|
||||
@@ -258,6 +258,66 @@
|
||||
</argument>
|
||||
</arguments>
|
||||
</method>
|
||||
<method id="SetComputedDate">
|
||||
<arguments>
|
||||
<argument id="1">
|
||||
<type>attcode</type>
|
||||
<mandatory>true</mandatory>
|
||||
<type_restrictions>
|
||||
<operation>allow</operation>
|
||||
<types>
|
||||
<type id="AttributeDate"/>
|
||||
<type id="AttributeDateTime"/>
|
||||
</types>
|
||||
</type_restrictions>
|
||||
</argument>
|
||||
<argument id="2">
|
||||
<type>string</type>
|
||||
<mandatory>false</mandatory>
|
||||
</argument>
|
||||
<argument id="3">
|
||||
<type>attcode</type>
|
||||
<mandatory>false</mandatory>
|
||||
<type_restrictions>
|
||||
<operation>allow</operation>
|
||||
<types>
|
||||
<type id="AttributeDate"/>
|
||||
<type id="AttributeDateTime"/>
|
||||
</types>
|
||||
</type_restrictions>
|
||||
</argument>
|
||||
</arguments>
|
||||
</method>
|
||||
<method id="SetComputedDateIfNull">
|
||||
<arguments>
|
||||
<argument id="1">
|
||||
<type>attcode</type>
|
||||
<mandatory>true</mandatory>
|
||||
<type_restrictions>
|
||||
<operation>allow</operation>
|
||||
<types>
|
||||
<type id="AttributeDate"/>
|
||||
<type id="AttributeDateTime"/>
|
||||
</types>
|
||||
</type_restrictions>
|
||||
</argument>
|
||||
<argument id="2">
|
||||
<type>string</type>
|
||||
<mandatory>false</mandatory>
|
||||
</argument>
|
||||
<argument id="3">
|
||||
<type>attcode</type>
|
||||
<mandatory>false</mandatory>
|
||||
<type_restrictions>
|
||||
<operation>allow</operation>
|
||||
<types>
|
||||
<type id="AttributeDate"/>
|
||||
<type id="AttributeDateTime"/>
|
||||
</types>
|
||||
</type_restrictions>
|
||||
</argument>
|
||||
</arguments>
|
||||
</method>
|
||||
<method id="SetCurrentDate">
|
||||
<arguments>
|
||||
<argument id="1">
|
||||
@@ -409,30 +469,6 @@
|
||||
</argument>
|
||||
</arguments>
|
||||
</method>
|
||||
<method id="PrefillCreationForm">
|
||||
<arguments>
|
||||
<argument id="1">
|
||||
<type>reference</type>
|
||||
<mandatory>true</mandatory>
|
||||
</argument>
|
||||
</arguments>
|
||||
</method>
|
||||
<method id="PrefillTransitionForm">
|
||||
<arguments>
|
||||
<argument id="1">
|
||||
<type>reference</type>
|
||||
<mandatory>true</mandatory>
|
||||
</argument>
|
||||
</arguments>
|
||||
</method>
|
||||
<method id="PrefillSearchForm">
|
||||
<arguments>
|
||||
<argument id="1">
|
||||
<type>reference</type>
|
||||
<mandatory>true</mandatory>
|
||||
</argument>
|
||||
</arguments>
|
||||
</method>
|
||||
</methods>
|
||||
</class>
|
||||
</classes>
|
||||
|
||||
@@ -268,6 +268,8 @@ class DisplayBlock
|
||||
'surround_with_panel',
|
||||
/** string title of panel block */
|
||||
'panel_title',
|
||||
/** string true if panel title should be displayed as html */
|
||||
'panel_title_is_html',
|
||||
/** string class for panel block style */
|
||||
'panel_class',
|
||||
/** string class for panel block style */
|
||||
@@ -467,10 +469,8 @@ class DisplayBlock
|
||||
$oHtml->AddSubBlock($this->GetRenderContent($oPage, $aExtraParams, $sId));
|
||||
} catch (Exception $e) {
|
||||
if (UserRights::IsAdministrator()) {
|
||||
$sExceptionContent = <<<HTML
|
||||
Exception thrown:<br>
|
||||
<code>{$e->getMessage()}</code>
|
||||
HTML;
|
||||
$sExceptionContent = 'Exception thrown:<br><code>'.utils::Sanitize($e->getMessage(), '', utils::ENUM_SANITIZATION_FILTER_STRING).'</code>';
|
||||
|
||||
$oExceptionAlert = AlertUIBlockFactory::MakeForFailure('Cannot display results', $sExceptionContent);
|
||||
$oHtml->AddSubBlock($oExceptionAlert);
|
||||
}
|
||||
@@ -1015,7 +1015,7 @@ JS
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
|
||||
$aValues = $oAttDef->GetAllowedValues();
|
||||
foreach ($aStates as $sStateValue) {
|
||||
$aStateLabels[$sStateValue] = $aValues[$sStateValue];
|
||||
$aStateLabels[$sStateValue] = $aValues[$sStateValue] ?? '';
|
||||
$aCounts[$sStateValue] = (array_key_exists($sStateValue, $aCountsQueryResults))
|
||||
? $aCountsQueryResults[$sStateValue]
|
||||
: 0;
|
||||
@@ -1043,7 +1043,7 @@ JS
|
||||
$sCountLabel = $aCount['label'];
|
||||
$oPill = PillFactory::MakeForState($sClass, $sStateValue)
|
||||
->SetTooltip($sStateLabel)
|
||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">$sStateLabel</span>");
|
||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">".utils::HtmlEntities($sStateLabel)."</span>");
|
||||
if ($sHyperlink != '-') {
|
||||
$oPill->SetUrl($sHyperlink);
|
||||
}
|
||||
@@ -2322,13 +2322,25 @@ class MenuBlock extends DisplayBlock
|
||||
$sIconClass = 'fas fa-share-alt';
|
||||
$sLabel = '';
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isset($aAction['icon_class']) && (strlen($aAction['icon_class']) > 0)) {
|
||||
$sIconClass = $aAction['icon_class'];
|
||||
$sLabel = '';
|
||||
}
|
||||
}
|
||||
|
||||
$sTarget = isset($aAction['target']) ? $aAction['target'] : '';
|
||||
$oActionButton = ButtonUIBlockFactory::MakeLinkNeutral($sUrl, $sLabel, $sIconClass, $sTarget, $sActionId);
|
||||
$oActionButton = ButtonUIBlockFactory::MakeLinkNeutral($sUrl, $sLabel, $sIconClass, $sTarget, utils::Sanitize($sActionId, '', utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER));
|
||||
// ResourceId should not be sanitized
|
||||
$oActionButton->AddDataAttribute('resource-id', $sActionId);
|
||||
$oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
|
||||
if (empty($sLabel)) {
|
||||
$oActionButton->SetTooltip(Dict::S($sActionId));
|
||||
if (empty($aAction['tooltip'])) {
|
||||
$oActionButton->SetTooltip(Dict::S($sActionId));
|
||||
} else {
|
||||
$oActionButton->SetTooltip($aAction['tooltip']);
|
||||
}
|
||||
}
|
||||
$oActionsToolbar->AddSubBlock($oActionButton);
|
||||
}
|
||||
|
||||
@@ -234,9 +234,9 @@ class DesignerForm
|
||||
.$this->EndRow();
|
||||
|
||||
if (is_null($aRow['label'])) {
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_value ibo-field--value" colspan="2">'.$aRow['value'];
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_value" colspan="2">'.$aRow['value'];
|
||||
} else {
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_label ibo-field--label">'.$aRow['label'].'</td><td class="prop_value ibo-field--value">'.$aRow['value'];
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_label">'.$aRow['label'].'</td><td class="prop_value">'.$aRow['value'];
|
||||
}
|
||||
if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField)) {
|
||||
$sReturn .= $sValidationFields;
|
||||
@@ -354,7 +354,7 @@ EOF
|
||||
<<<EOF
|
||||
$('#$sDialogId').dialog({
|
||||
height: 'auto',
|
||||
maxHeight: $(window).height() - 8,
|
||||
maxHeight: $(window).height() * 0.9,
|
||||
width: $iDialogWidth,
|
||||
modal: true,
|
||||
autoOpen: $sAutoOpen,
|
||||
@@ -710,7 +710,10 @@ class DesignerFormField
|
||||
$this->bMandatory = false;
|
||||
$this->bReadOnly = false;
|
||||
$this->bAutoApply = false;
|
||||
$this->aCSSClasses = array('ibo-input');
|
||||
$this->aCSSClasses = [];
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input';
|
||||
}
|
||||
$this->bDisplayed = true;
|
||||
$this->aWidgetExtraParams = array();
|
||||
}
|
||||
@@ -1065,7 +1068,10 @@ class DesignerLongTextField extends DesignerTextField
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$this->aCSSClasses[] = 'ibo-input-text';
|
||||
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input-text';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1199,7 +1205,10 @@ class DesignerComboField extends DesignerFormField
|
||||
$this->bMultipleSelection = false;
|
||||
$this->bOtherChoices = false;
|
||||
$this->sNullLabel = Dict::S('UI:SelectOne');
|
||||
$this->aCSSClasses[] = 'ibo-input-select';
|
||||
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input-select';
|
||||
}
|
||||
|
||||
$this->bAutoApply = true;
|
||||
$this->bSorted = true; // Sorted by default
|
||||
@@ -1356,7 +1365,9 @@ class DesignerBooleanField extends DesignerFormField
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$this->bAutoApply = true;
|
||||
$this->aCSSClasses[] = 'ibo-input-checkbox';
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input-checkbox';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1503,7 +1514,8 @@ class DesignerIconSelectionField extends DesignerFormField
|
||||
$sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
|
||||
if (!$this->IsReadOnly()) {
|
||||
$sDefaultValue = ($this->defaultValue !== '') ? $this->defaultValue : $this->aAllowedValues[$idx]['value'];
|
||||
$sValue = "<span class=\"ibo-input-select-wrapper\"><input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$sDefaultValue}\"/></span>";
|
||||
$sCSSClasses = ContextTag::Check(ContextTag::TAG_CONSOLE) ? 'class="ibo-input-select-wrapper"' : '';
|
||||
$sValue = "<span $sCSSClasses><input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$sDefaultValue}\"/></span>";
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
|
||||
@@ -1681,7 +1693,9 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
$this->defaultRealValue = $defaultValue;
|
||||
$this->aSubForms = array();
|
||||
$this->bSorted = true;
|
||||
$this->aCSSClasses[] = 'ibo-input-select';
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input-select';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -126,7 +126,7 @@ class QueryOQL extends Query
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
{
|
||||
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
|
||||
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-queryoql'); $('[data-attribute-code=\"oql\"]').addClass('ibo-queryoql');");
|
||||
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-query-oql ibo-is-code'); $('[data-attribute-code=\"oql\"]').addClass('ibo-query-oql ibo-is-code');");
|
||||
|
||||
if (!$bEditMode) {
|
||||
$sFields = trim($this->Get('fields'));
|
||||
|
||||
@@ -41,29 +41,25 @@ register_shutdown_function(function()
|
||||
$sReservedMemory = null;
|
||||
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
|
||||
{
|
||||
// Remove stack trace from MySQLException
|
||||
// Remove stack trace from MySQLException (since 2.7.2 see N°3174)
|
||||
$sMessage = $err['message'];
|
||||
if (strpos($sMessage, 'MySQLException') !== false)
|
||||
{
|
||||
if (strpos($sMessage, 'MySQLException') !== false) {
|
||||
$iStackTracePos = strpos($sMessage, 'Stack trace:');
|
||||
if ($iStackTracePos !== false)
|
||||
{
|
||||
if ($iStackTracePos !== false) {
|
||||
$sMessage = substr($sMessage, 0, $iStackTracePos);
|
||||
}
|
||||
}
|
||||
IssueLog::error($sMessage, null, $err);
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false)
|
||||
{
|
||||
// Log additional info but message from $err (since 2.7.6 N°4174)
|
||||
$aErrToLog = $err;
|
||||
unset($aErrToLog['message']);
|
||||
IssueLog::error($sMessage, null, $aErrToLog);
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false) {
|
||||
$sLimit = ini_get('memory_limit');
|
||||
echo "<p>iTop: Allowed memory size of $sLimit exhausted, contact your administrator to increase 'memory_limit' in php.ini</p>\n";
|
||||
}
|
||||
elseif (strpos($err['message'], 'Maximum execution time') !== false)
|
||||
{
|
||||
} elseif (strpos($err['message'], 'Maximum execution time') !== false) {
|
||||
$sLimit = ini_get('max_execution_time');
|
||||
echo "<p>iTop: Maximum execution time of $sLimit exceeded, contact your administrator to increase 'max_execution_time' in php.ini</p>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
echo "<p>iTop: An error occurred, check server error log for more information.</p>\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,8 +27,6 @@ use Combodo\iTop\Application\Helper\Session;
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
class privUITransaction
|
||||
{
|
||||
/**
|
||||
@@ -100,10 +98,12 @@ class privUITransaction
|
||||
}
|
||||
|
||||
/**
|
||||
* The original (and by default) mechanism for storing transaction information
|
||||
* as an array in the _SESSION variable
|
||||
* @see \Combodo\iTop\Application\Helper\Session
|
||||
* The original mechanism for storing transaction information as an array in the $_SESSION variable
|
||||
*
|
||||
* Warning, since 2.6.0 the session is regenerated on each login (see PR #20) !
|
||||
* Also, we saw some problems when using memcached as the PHP session implementation (see N°1835)
|
||||
*
|
||||
* @see \Combodo\iTop\Application\Helper\Session
|
||||
*/
|
||||
class privUITransactionSession
|
||||
{
|
||||
@@ -197,8 +197,31 @@ class privUITransactionSession
|
||||
class privUITransactionFile
|
||||
{
|
||||
/**
|
||||
* @return int
|
||||
* @throws \SecurityException if no connected user
|
||||
*
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
|
||||
*/
|
||||
private static function GetCurrentUserId() {
|
||||
$iCurrentUserId = UserRights::GetConnectedUserId();
|
||||
if ('' === $iCurrentUserId) {
|
||||
throw new SecurityException('Cannot creation transaction_id when no user logged');
|
||||
}
|
||||
|
||||
return $iCurrentUserId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new transaction id, store it in the session and return its id
|
||||
*
|
||||
* @param void
|
||||
*
|
||||
* @return int The new transaction identifier
|
||||
*
|
||||
* @throws \SecurityException
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 2.6.5 2.7.6 3.0.0 security hardening + throws SecurityException if no user logged
|
||||
*/
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
@@ -215,24 +238,36 @@ class privUITransactionFile
|
||||
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_writable(APPROOT.'data/transactions'))
|
||||
{
|
||||
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
|
||||
}
|
||||
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
|
||||
* the session so that another call to IsTransactionValid for the same transaction id
|
||||
* will return false
|
||||
*
|
||||
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
|
||||
* @param bool $bRemoveTransaction True if the transaction must be removed
|
||||
*
|
||||
* @return bool True if the transaction is valid, false otherwise
|
||||
*
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
|
||||
*/
|
||||
public static function IsTransactionValid($id, $bRemoveTransaction = true)
|
||||
{
|
||||
@@ -246,53 +281,53 @@ class privUITransactionFile
|
||||
|
||||
clearstatcache(true, $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);
|
||||
if (!$bResult)
|
||||
{
|
||||
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
|
||||
}
|
||||
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
}
|
||||
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the transaction specified by its id
|
||||
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
|
||||
* @return 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)
|
||||
{
|
||||
$bSuccess = true;
|
||||
$sFilepath = APPROOT.'data/transactions/'.$id;
|
||||
clearstatcache(true, $sFilepath);
|
||||
if(!file_exists($sFilepath))
|
||||
{
|
||||
$bSuccess = false;
|
||||
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$bResult = static::IsTransactionValid($id, true);
|
||||
if (false === $bResult) {
|
||||
self::Error("RemoveTransaction: Transaction '$id' is invalid. Pending transactions:\n"
|
||||
.implode("\n", self::GetPendingTransactions()));
|
||||
return false;
|
||||
}
|
||||
$bSuccess = @unlink($sFilepath);
|
||||
if (!$bSuccess)
|
||||
{
|
||||
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Info('RemoveTransaction: OK '.$id);
|
||||
}
|
||||
return $bSuccess;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -365,22 +400,35 @@ class privUITransactionFile
|
||||
{
|
||||
self::Write('Error | '.$sText);
|
||||
}
|
||||
|
||||
|
||||
protected static function IsLogEnabled() {
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
if (is_null($oConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$bLogTransactions = $oConfig->Get('log_transactions');
|
||||
if (true === $bLogTransactions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function Write($sText)
|
||||
{
|
||||
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
|
||||
if ($bLogEnabled)
|
||||
{
|
||||
if (false === static::IsLogEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
|
||||
if ($hLogFile !== false)
|
||||
{
|
||||
if ($hLogFile !== false) {
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
fflush($hLogFile);
|
||||
flock($hLogFile, LOCK_UN);
|
||||
fclose($hLogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,13 @@ use Twig_SimpleFilter;
|
||||
use Twig_SimpleFunction;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
* Class TwigExtension
|
||||
*
|
||||
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
|
||||
* @package Combodo\iTop
|
||||
* @deprecated 3.1.0 N°4034
|
||||
*/
|
||||
class TwigExtension
|
||||
{
|
||||
/**
|
||||
@@ -107,6 +114,14 @@ class TwigExtension
|
||||
return $oConfig->Get($sParamName);
|
||||
}));
|
||||
|
||||
// Function to get a module setting
|
||||
// Usage in twig: {{ get_module_setting(<MODULE_CODE>, <PROPERTY_CODE> [, <DEFAULT_VALUE>]) }}
|
||||
// since 3.0.0, but see N°4034 for upcoming evolutions in the 3.1
|
||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_module_setting', function (string $sModuleCode, string $sPropertyCode, $defaultValue = null) {
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
return $oConfig->GetModuleSetting($sModuleCode, $sPropertyCode, $defaultValue);
|
||||
}));
|
||||
|
||||
// Function to get the URL of a static page in a module
|
||||
// Usage in twig: {{ get_static_page_module_url('itop-my-module', 'path-to-my-page') }}
|
||||
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_static_page_module_url', function($sModuleName, $sPage)
|
||||
|
||||
@@ -196,7 +196,7 @@ class UIExtKeyWidget
|
||||
|
||||
$bIsAutocomplete = $oAllowedValues->CountExceeds($iMaxComboLength);
|
||||
$sWrapperCssClass = $bIsAutocomplete ? 'ibo-input-select-autocomplete-wrapper' : 'ibo-input-select-wrapper';
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons $sWrapperCssClass\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\">";
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons $sWrapperCssClass\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\" data-accessibility-selectize-label=\"$sTitle\">";
|
||||
|
||||
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
|
||||
if (!$bIsAutocomplete) {
|
||||
@@ -226,7 +226,8 @@ class UIExtKeyWidget
|
||||
while ($oObj = $oAllowedValues->Fetch()) {
|
||||
$aOption = [];
|
||||
$aOption['value'] = $oObj->GetKey();
|
||||
$aOption['label'] = $oObj->GetName();//.'<span class=\"object-ref-icon fas fa-eye-slash object-obsolete fa-1x fa-fw\"></span>';
|
||||
$aOption['label'] = $oObj->GetName();
|
||||
$aOption['search_label'] = utils::HtmlEntityDecode($oObj->GetName());
|
||||
|
||||
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true')) {
|
||||
// When there is only once choice, select it by default
|
||||
@@ -243,7 +244,7 @@ class UIExtKeyWidget
|
||||
foreach ($aAdditionalField as $sAdditionalField) {
|
||||
array_push($aArguments, $oObj->Get($sAdditionalField));
|
||||
}
|
||||
$aOption['additional_field'] = vsprintf($sFormatAdditionalField, $aArguments);
|
||||
$aOption['additional_field'] = utils::HtmlEntities(vsprintf($sFormatAdditionalField, $aArguments));
|
||||
}
|
||||
if (!empty($sObjectImageAttCode)) {
|
||||
// Try to retrieve image for contact
|
||||
@@ -259,8 +260,8 @@ class UIExtKeyWidget
|
||||
array_push($aOptions, $aOption);
|
||||
}
|
||||
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_DECORATED;
|
||||
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\" tabindex=\"0\"></select>";
|
||||
$sJsonOptions = str_replace('\\', '\\\\', json_encode($aOptions));
|
||||
$sHTMLValue .= "<select class=\"ibo-input-select-placeholder\" title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\" tabindex=\"0\"></select>";
|
||||
$sJsonOptions = str_replace("'", "\'", str_replace('\\', '\\\\', json_encode($aOptions)));
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
@@ -537,7 +538,7 @@ JS
|
||||
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
|
||||
}
|
||||
} else {
|
||||
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<select class=\"ibo-input-select-placeholder\" title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
|
||||
}
|
||||
|
||||
@@ -703,14 +704,14 @@ JS
|
||||
HTML
|
||||
);
|
||||
|
||||
$sDialogTitle = addslashes($sTitle);
|
||||
$sDialogTitleSanitized = utils::HtmlToText($sTitle);
|
||||
$oPage->add_ready_script(<<<JS
|
||||
$('#ac_dlg_{$this->iId}').dialog({
|
||||
width: $(window).width()*0.8,
|
||||
height: $(window).height()*0.8,
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
title: '$sDialogTitleSanitized',
|
||||
resizeStop: oACWidget_{$this->iId}.UpdateSizes,
|
||||
close: oACWidget_{$this->iId}.OnClose,
|
||||
buttons: [
|
||||
@@ -823,9 +824,9 @@ JS
|
||||
|
||||
$aJsonMap = array();
|
||||
foreach ($aValues as $sKey => $aValue) {
|
||||
$aElt = ['value' => $sKey, 'label' => $aValue['label'], 'obsolescence_flag' => $aValue['obsolescence_flag']];
|
||||
$aElt = ['value' => $sKey, 'label' => utils::EscapeHtml($aValue['label']), 'obsolescence_flag' => $aValue['obsolescence_flag']];
|
||||
if ($aValue['additional_field'] != '') {
|
||||
$aElt['additional_field'] = $aValue['additional_field'];
|
||||
$aElt['additional_field'] = utils::EscapeHtml($aValue['additional_field']);
|
||||
}
|
||||
|
||||
if (array_key_exists('initials', $aValue)) {
|
||||
|
||||
@@ -65,7 +65,7 @@ class UIHTMLEditorWidget
|
||||
$sHelpText = $this->m_sHelpText;
|
||||
$sValidationField = $this->m_sValidationField;
|
||||
|
||||
$sHtmlValue = "<div class=\"field_input_zone field_input_html\"><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"$iId\">$sValue</textarea></div>$sValidationField";
|
||||
$sHtmlValue = "<div class=\"field_input_zone field_input_html ibo-input-wrapper\"><textarea class=\"htmlEditor ibo-input-richtext-placeholder\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" id=\"$iId\">$sValue</textarea></div>$sValidationField";
|
||||
|
||||
// Replace the text area with CKEditor
|
||||
// To change the default settings of the editor,
|
||||
|
||||
@@ -59,7 +59,7 @@ class UIPasswordWidget
|
||||
$sConfirmPasswordValue = $aPasswordValues ? $aPasswordValues['confirm'] : '*****';
|
||||
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
|
||||
$sHtmlValue = '';
|
||||
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword ibo-input-wrapper">';
|
||||
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword ibo-input-wrapper ibo-input-one-way-password-wrapper">';
|
||||
$sHtmlValue .= '<input class="ibo-input" type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sHtmlValue .= '<div class="ibo-input-wrapper ibo-input-wrapper--with-buttons"><input class="ibo-input" type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
|
||||
$sHtmlValue .= '<div class="ibo-input-select--action-buttons"><div class="ibo-input-select--action-button ibo-input-select--action-button--create" data-tooltip-content="'.Dict::S('UI:PasswordConfirm').'"><i class="fas fa-question-circle"></i></div></div></div>';
|
||||
|
||||
@@ -1319,6 +1319,16 @@ class utils
|
||||
return Session::Get('itop_env', ITOP_DEFAULT_ENV);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Absolute path to the folder into which the current environment has been compiled.
|
||||
* The corresponding folder is created or cleaned upon code compilation
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCompiledEnvironmentPath(): string
|
||||
{
|
||||
return APPROOT . 'env-' . MetaModel::GetEnvironment() . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string A path to a folder into which any module can store cache data
|
||||
* The corresponding folder is created or cleaned upon code compilation
|
||||
@@ -1327,6 +1337,7 @@ class utils
|
||||
{
|
||||
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string A path to a folder into which any module can store log
|
||||
* @since 2.7.0
|
||||
@@ -2627,7 +2638,9 @@ class utils
|
||||
$aDefaultConf = array(
|
||||
'language'=> $sLanguage,
|
||||
'contentsLanguage' => $sLanguage,
|
||||
'extraPlugins' => 'disabler,codesnippet,mentions,objectshortcut',
|
||||
'extraPlugins' => 'disabler,codesnippet,mentions,objectshortcut,font,uploadimage',
|
||||
'uploadUrl' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
|
||||
'contentsCss' => array(utils::GetAbsoluteUrlAppRoot().'js/ckeditor/contents.css', utils::GetAbsoluteUrlAppRoot().'css/ckeditor/contents.css'),
|
||||
);
|
||||
|
||||
// Mentions
|
||||
@@ -2649,7 +2662,7 @@ class utils
|
||||
}
|
||||
|
||||
// Note: Endpoints are defaults only and should be overloaded by other GUIs such as the end-users portal
|
||||
$sMentionEndpoint = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=cke_mentions&marker='.$sMentionMarker.'&needle={encodedQuery}';
|
||||
$sMentionEndpoint = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=cke_mentions&marker='.urlencode($sMentionMarker).'&needle={encodedQuery}';
|
||||
$sMentionItemUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class='.$sMentionClass.'&id={id}';
|
||||
|
||||
$sMentionItemPictureTemplate = (empty(MetaModel::GetImageAttributeCode($sMentionClass))) ? '' : <<<HTML
|
||||
|
||||
@@ -350,7 +350,7 @@ class WizardHelper
|
||||
*/
|
||||
public function GetJsForUpdateFields()
|
||||
{
|
||||
$sWizardHelperJsVar = ($this->m_aData['m_sWizHelperJsVarName']) ?? 'oWizardHelper'.$this->GetFormPrefix();
|
||||
$sWizardHelperJsVar = (!is_null($this->m_aData['m_sWizHelperJsVarName'])) ? utils::Sanitize($this->m_aData['m_sWizHelperJsVarName'], '', utils::ENUM_SANITIZATION_FILTER_PARAMETER) : 'oWizardHelper'.$this->GetFormPrefix();
|
||||
$sWizardHelperJson = $this->ToJSON();
|
||||
|
||||
return <<<JS
|
||||
|
||||
@@ -3,4 +3,9 @@
|
||||
define('APPROOT', dirname(__FILE__).'/');
|
||||
define('APPCONF', APPROOT.'conf/');
|
||||
|
||||
/**
|
||||
* iTop framework Version
|
||||
*/
|
||||
define('ITOP_DESIGN_LATEST_VERSION', '3.0');
|
||||
|
||||
require_once APPROOT.'bootstrap.inc.php';
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"ext-soap": "*",
|
||||
"combodo/tcpdf": "6.3.5",
|
||||
"nikic/php-parser": "^4.12.0",
|
||||
"pear/archive_tar": "1.4.13",
|
||||
"pear/archive_tar": "1.4.14",
|
||||
"pelago/emogrifier": "3.1.0",
|
||||
"scssphp/scssphp": "1.0.6",
|
||||
"swiftmailer/swiftmailer": "5.4.12",
|
||||
|
||||
12
composer.lock
generated
12
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "fb56686981ee4945791fe5b93735b022",
|
||||
"content-hash": "4c5cdd1e0feb4abaab8f86959ffc7a64",
|
||||
"packages": [
|
||||
{
|
||||
"name": "combodo/tcpdf",
|
||||
@@ -173,16 +173,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pear/archive_tar",
|
||||
"version": "1.4.13",
|
||||
"version": "1.4.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pear/Archive_Tar.git",
|
||||
"reference": "2b87b41178cc6d4ad3cba678a46a1cae49786011"
|
||||
"reference": "4d761c5334c790e45ef3245f0864b8955c562caa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/19bb8e95490d3e3ad92fcac95500ca80bdcc7495",
|
||||
"reference": "19bb8e95490d3e3ad92fcac95500ca80bdcc7495",
|
||||
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/4d761c5334c790e45ef3245f0864b8955c562caa",
|
||||
"reference": "4d761c5334c790e45ef3245f0864b8955c562caa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -249,7 +249,7 @@
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2021-01-18T19:32:54+00:00"
|
||||
"time": "2021-07-20T13:53:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pear/console_getopt",
|
||||
|
||||
@@ -49,7 +49,7 @@ class DbConnectionWrapper
|
||||
return static::$oDbCnxStandard;
|
||||
}
|
||||
|
||||
public static function SetDbConnection(mysqli $oMysqli): void
|
||||
public static function SetDbConnection(?mysqli $oMysqli): void
|
||||
{
|
||||
static::$oDbCnxStandard = $oMysqli;
|
||||
static::SetDbConnectionMockForQuery($oMysqli);
|
||||
@@ -58,9 +58,9 @@ class DbConnectionWrapper
|
||||
/**
|
||||
* Use this to register a mock that will handle {@see mysqli::query()}
|
||||
*
|
||||
* @param \mysqli $oMysqli
|
||||
* @param \mysqli|null $oMysqli
|
||||
*/
|
||||
public static function SetDbConnectionMockForQuery(mysqli $oMysqli): void
|
||||
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli): void
|
||||
{
|
||||
static::$oDbCnxMockableForQuery = $oMysqli;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ abstract class Action extends cmdbAbstractObject
|
||||
"db_table" => "priv_action",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-in-transit.svg'),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
@@ -366,8 +367,7 @@ class ActionEmail extends ActionNotification
|
||||
{
|
||||
$this->m_iRecipients = 0;
|
||||
$this->m_aMailErrors = array();
|
||||
$bRes = false; // until we do succeed in sending the email
|
||||
|
||||
|
||||
// Determine recipients
|
||||
//
|
||||
$sTo = $this->FindRecipients('to', $aContextArgs);
|
||||
@@ -381,29 +381,42 @@ class ActionEmail extends ActionNotification
|
||||
|
||||
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
||||
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
|
||||
|
||||
|
||||
$oObj = $aContextArgs['this->object()'];
|
||||
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetEnvironmentId());
|
||||
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/),
|
||||
MetaModel::GetEnvironmentId());
|
||||
$sReference = '<'.$sMessageId.'>';
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
throw $e;
|
||||
}
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
|
||||
if (!is_null($oLog))
|
||||
{
|
||||
catch (Exception $e) {
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
throw $e;
|
||||
}
|
||||
finally {
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
}
|
||||
|
||||
if (!is_null($oLog)) {
|
||||
// Note: we have to secure this because those values are calculated
|
||||
// inside the try statement, and we would like to keep track of as
|
||||
// many data as we could while some variables may still be undefined
|
||||
if (isset($sTo)) $oLog->Set('to', $sTo);
|
||||
if (isset($sCC)) $oLog->Set('cc', $sCC);
|
||||
if (isset($sBCC)) $oLog->Set('bcc', $sBCC);
|
||||
if (isset($sFrom)) $oLog->Set('from', empty($sFromLabel) ? $sFrom : "$sFromLabel <$sFrom>");
|
||||
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
|
||||
if (isset($sBody)) $oLog->Set('body', $sBody);
|
||||
if (isset($sTo)) {
|
||||
$oLog->Set('to', $sTo);
|
||||
}
|
||||
if (isset($sCC)) {
|
||||
$oLog->Set('cc', $sCC);
|
||||
}
|
||||
if (isset($sBCC)) {
|
||||
$oLog->Set('bcc', $sBCC);
|
||||
}
|
||||
if (isset($sFrom)) {
|
||||
$oLog->Set('from', $sFrom);
|
||||
}
|
||||
if (isset($sSubject)) {
|
||||
$oLog->Set('subject', $sSubject);
|
||||
}
|
||||
if (isset($sBody)) {
|
||||
$oLog->Set('body', $sBody);
|
||||
}
|
||||
}
|
||||
$sStyles = file_get_contents(APPROOT.'css/email.css');
|
||||
$sStyles .= MetaModel::GetConfig()->Get('email_css');
|
||||
|
||||
@@ -157,7 +157,7 @@ abstract class AsyncTask extends DBObject
|
||||
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
|
||||
{
|
||||
$aConfig = $aRetries[get_class($this)];
|
||||
$iRetryDelay = $aConfig['retry_delay'];
|
||||
$iRetryDelay = $aConfig['retry_delay'] ?? $iRetryDelay;
|
||||
}
|
||||
return $iRetryDelay;
|
||||
}
|
||||
@@ -169,11 +169,71 @@ abstract class AsyncTask extends DBObject
|
||||
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
|
||||
{
|
||||
$aConfig = $aRetries[get_class($this)];
|
||||
$iMaxRetries = $aConfig['max_retries'];
|
||||
$iMaxRetries = $aConfig['max_retries'] ?? $iMaxRetries;
|
||||
}
|
||||
return $iMaxRetries;
|
||||
}
|
||||
|
||||
public function IsRetryDelayExponential()
|
||||
{
|
||||
$bExponential = false;
|
||||
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
|
||||
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
|
||||
{
|
||||
$aConfig = $aRetries[get_class($this)];
|
||||
$bExponential = (bool)$aConfig['exponential_delay'] ?? $bExponential;
|
||||
}
|
||||
return $bExponential;
|
||||
}
|
||||
|
||||
public static function CheckRetryConfig(Config $oConfig, $sAsyncTaskClass)
|
||||
{
|
||||
$aMessages = [];
|
||||
$aRetries = $oConfig->Get('async_task_retries');
|
||||
if (is_array($aRetries) && array_key_exists($sAsyncTaskClass, $aRetries))
|
||||
{
|
||||
$aValidKeys = array("retry_delay", "max_retries", "exponential_delay");
|
||||
$aConfig = $aRetries[$sAsyncTaskClass];
|
||||
if (!is_array($aConfig))
|
||||
{
|
||||
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_Keys', $sAsyncTaskClass, implode(', ', $aValidKeys));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($aConfig as $key => $value)
|
||||
{
|
||||
if (!in_array($key, $aValidKeys))
|
||||
{
|
||||
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_InvalidKey_Keys', $sAsyncTaskClass, $key, implode(', ', $aValidKeys));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the delay to wait for the "next retry", based on the given parameters
|
||||
* @param bool $bIsExponential
|
||||
* @param int $iRetryDelay
|
||||
* @param int $iMaxRetries
|
||||
* @param int $iRemainingRetries
|
||||
* @return int
|
||||
*/
|
||||
public static function GetNextRetryDelay($bIsExponential, $iRetryDelay, $iMaxRetries, $iRemainingRetries)
|
||||
{
|
||||
if ($bIsExponential)
|
||||
{
|
||||
$iExponent = $iMaxRetries - $iRemainingRetries;
|
||||
if ($iExponent < 0) $iExponent = 0; // Safety net in case on configuration change in the middle of retries
|
||||
return $iRetryDelay * (2 ** $iExponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
return $iRetryDelay;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to notify people that a task cannot be performed
|
||||
*/
|
||||
@@ -241,25 +301,26 @@ abstract class AsyncTask extends DBObject
|
||||
if ($iRemaining > 0)
|
||||
{
|
||||
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
|
||||
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
|
||||
$iNextRetryDelay = static::GetNextRetryDelay($this->IsRetryDelayExponential(), $iRetryDelay, $this->GetMaxRetries($iErrorCode), $iRemaining);
|
||||
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iNextRetryDelay.'s');
|
||||
if ($this->Get('event_id') != 0)
|
||||
{
|
||||
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
|
||||
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task. Remaining retries: '.$iRemaining.'. Next retry in '.$iRetryDelay.'s'");
|
||||
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task. Remaining retries: $iRemaining. Next retry in {$iNextRetryDelay}s");
|
||||
try
|
||||
{
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oEventLog->Set('message', "Failed to process async task. Remaining retries: '.$iRemaining.'. Next retry in '.$iRetryDelay.'s', more details in the log");
|
||||
$oEventLog->Set('message', "Failed to process async task. Remaining retries: $iRemaining. Next retry in {$iNextRetryDelay}s, more details in the log");
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
}
|
||||
$this->Set('remaining_retries', $iRemaining - 1);
|
||||
$this->Set('status', 'planned');
|
||||
$this->Set('started', null);
|
||||
$this->Set('planned', time() + $iRetryDelay);
|
||||
$this->Set('planned', time() + $iNextRetryDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1793,7 +1793,7 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
public function GetImportColumns()
|
||||
{
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'TEXT';
|
||||
$aColumns[$this->GetCode()] = 'TEXT'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
@@ -4159,6 +4159,7 @@ class AttributeText extends AttributeString
|
||||
{
|
||||
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
|
||||
$sValue = self::RenderWikiHtml($sValue);
|
||||
$sValue = nl2br($sValue);
|
||||
|
||||
return "<div $sStyle>$sValue</div>";
|
||||
}
|
||||
@@ -5994,7 +5995,7 @@ class AttributeDateTime extends AttributeDBField
|
||||
{
|
||||
// Allow an empty string to be a valid value (synonym for "reset")
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'VARCHAR(19)';
|
||||
$aColumns[$this->GetCode()] = 'VARCHAR(19)'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
@@ -6482,7 +6483,7 @@ class AttributeDate extends AttributeDateTime
|
||||
{
|
||||
// Allow an empty string to be a valid value (synonym for "reset")
|
||||
$aColumns = array();
|
||||
$aColumns[$this->GetCode()] = 'VARCHAR(10)';
|
||||
$aColumns[$this->GetCode()] = 'VARCHAR(10)'.CMDBSource::GetSqlStringColumnDefinition();
|
||||
|
||||
return $aColumns;
|
||||
}
|
||||
@@ -8129,6 +8130,25 @@ class AttributeImage extends AttributeBlob
|
||||
return "Image";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see AttributeBlob::MakeRealValue()
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
$oDoc = parent::MakeRealValue($proposedValue, $oHostObj);
|
||||
|
||||
if (($oDoc instanceof ormDocument)
|
||||
&& (false === $oDoc->IsEmpty())
|
||||
&& ($oDoc->GetMimeType() === 'image/svg+xml')) {
|
||||
$sCleanSvg = HTMLSanitizer::Sanitize($oDoc->GetData(), 'svg_sanitizer');
|
||||
$oDoc = new ormDocument($sCleanSvg, $oDoc->GetMimeType(), $oDoc->GetFileName());
|
||||
}
|
||||
|
||||
// The validation of the MIME Type is done by CheckFormat below
|
||||
return $oDoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the supplied ormDocument actually contains an image
|
||||
* {@inheritDoc}
|
||||
@@ -8159,24 +8179,27 @@ class AttributeImage extends AttributeBlob
|
||||
$sRet = '';
|
||||
$bIsCustomImage = false;
|
||||
|
||||
$iMaxWidthPx = $this->Get('display_max_width').'px';
|
||||
$iMaxHeightPx = $this->Get('display_max_height').'px';
|
||||
$iMaxWidth = $this->Get('display_max_width');
|
||||
$sMaxWidthPx = $iMaxWidth.'px';
|
||||
$iMaxHeight = $this->Get('display_max_height');
|
||||
$sMaxHeightPx = $iMaxHeight.'px';
|
||||
|
||||
$sDefaultImageUrl = $this->Get('default_image');
|
||||
if ($sDefaultImageUrl !== null) {
|
||||
$sRet = $this->GetHtmlForImageUrl($sDefaultImageUrl, $iMaxWidthPx, $iMaxHeightPx);
|
||||
$sRet = $this->GetHtmlForImageUrl($sDefaultImageUrl, $sMaxWidthPx, $sMaxHeightPx);
|
||||
}
|
||||
|
||||
$sCustomImageUrl = $this->GetAttributeImageFileUrl($value, $oHostObject);
|
||||
if ($sCustomImageUrl !== null) {
|
||||
$bIsCustomImage = true;
|
||||
$sRet = $this->GetHtmlForImageUrl($sCustomImageUrl, $iMaxWidthPx, $iMaxHeightPx);
|
||||
$sRet = $this->GetHtmlForImageUrl($sCustomImageUrl, $sMaxWidthPx, $sMaxHeightPx);
|
||||
}
|
||||
|
||||
$sCssClasses = 'ibo-input-image--image-view attribute-image';
|
||||
$sCssClasses .= ' '.(($bIsCustomImage) ? 'attribute-image-custom' : 'attribute-image-default');
|
||||
|
||||
return '<div class="'.$sCssClasses.'" style="width: '.$iMaxWidthPx.'; height: '.$iMaxHeightPx.';">'.$sRet.'</div>';
|
||||
// Important: If you change this, mind updating edit_image.js as well
|
||||
return '<div class="'.$sCssClasses.'" style="max-width: '.$sMaxWidthPx.'; max-height: '.$sMaxHeightPx.'; aspect-ratio: '.$iMaxWidth.' / '.$iMaxHeight.'">'.$sRet.'</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -10829,7 +10852,7 @@ class AttributeClassAttCodeSet extends AttributeSet
|
||||
}
|
||||
}
|
||||
|
||||
$sLabelForHtmlAttribute = MetaModel::GetLabel($sAttClass, $sAttCode)." ($sAttCode)";
|
||||
$sLabelForHtmlAttribute = utils::HtmlEntities(MetaModel::GetLabel($sAttClass, $sAttCode)." ($sAttCode)");
|
||||
$aLocalizedValues[] = '<span class="attribute-set-item" data-code="'.$sAttCode.'" data-label="'.$sLabelForHtmlAttribute.'" data-description="" data-tooltip-content="'.$sLabelForHtmlAttribute.'">'.$sAttCode.'</span>';
|
||||
} catch (Exception $e)
|
||||
{
|
||||
@@ -11022,7 +11045,7 @@ class AttributeQueryAttCodeSet extends AttributeSet
|
||||
$aLocalizedValues = array();
|
||||
foreach ($value as $sAttCode) {
|
||||
if (isset($aAllowedAttributes[$sAttCode])) {
|
||||
$sLabelForHtmlAttribute = $aAllowedAttributes[$sAttCode];
|
||||
$sLabelForHtmlAttribute = utils::HtmlEntities($aAllowedAttributes[$sAttCode]);
|
||||
$aLocalizedValues[] = '<span class="attribute-set-item" data-code="'.$sAttCode.'" data-label="'.$sLabelForHtmlAttribute.'" data-description="" data-tooltip-content="'.$sLabelForHtmlAttribute.'">'.$sAttCode.'</span>';
|
||||
}
|
||||
}
|
||||
@@ -11571,19 +11594,20 @@ class AttributeTagSet extends AttributeSet
|
||||
$sTooltipContent = $sTagLabel;
|
||||
$sTooltipHtmlEnabled = 'false';
|
||||
} else {
|
||||
$sTagLabelEscaped = utils::EscapeHtml($sTagLabel);
|
||||
$sTooltipContent = <<<HTML
|
||||
<h4>$sTagLabel</h4>
|
||||
<h4>$sTagLabelEscaped</h4>
|
||||
<div>$sTagDescription</div>
|
||||
HTML;
|
||||
$sTooltipHtmlEnabled = 'true';
|
||||
}
|
||||
$sTooltipContent = utils::EscapeHtml($sTooltipContent);
|
||||
$sTooltipContent = utils::HtmlEntities($sTooltipContent);
|
||||
|
||||
$sHtml .= '<a'.$sLink.' class="attribute-set-item attribute-set-item-'.$sTagCode.'" data-code="'.$sTagCode.'" data-label="'.$sLabelForHtml.'" data-description="'.$sDescriptionForHtml.'" data-tooltip-content="'.$sTooltipContent.'" data-tooltip-html-enabled="'.$sTooltipHtmlEnabled.'">'.$sLabelForHtml.'</a>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= '<span class="attribute-set-item">'.$oTag.'</span>';
|
||||
$sHtml .= '<span class="attribute-set-item">'.utils::EscapeHtml($oTag).'</span>';
|
||||
}
|
||||
}
|
||||
$sHtml .= '</span>';
|
||||
|
||||
@@ -1199,9 +1199,6 @@ EOF
|
||||
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
|
||||
{
|
||||
$('#$sAjaxDivId').html(data);
|
||||
var table = $('#$sAjaxDivId .listResults');
|
||||
table.tableHover(); // hover tables
|
||||
table.tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -147,6 +147,8 @@ class CMDBSource
|
||||
*
|
||||
* @return \mysqli
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @uses IsOpenedDbConnectionUsingTls when asking for a TLS connection, to check if it was really opened using TLS
|
||||
*/
|
||||
public static function GetMysqliInstance(
|
||||
$sDbHost, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCa = null, $bCheckTlsAfterConnection = false
|
||||
@@ -249,41 +251,41 @@ class CMDBSource
|
||||
* parameters were used.<br>
|
||||
* This method can be called to ensure that the DB connection really uses TLS.
|
||||
*
|
||||
* <p>We're using this object connection : {@see self::$m_oMysqli}
|
||||
* <p>We're using our own mysqli instance to do the check as this check is done when creating the mysqli instance : the consumer
|
||||
* might want a dedicated object, and if so we don't want to overwrite the one saved in CMDBSource !<br>
|
||||
* This is the case for example with {@see \iTopMutex} !
|
||||
*
|
||||
* @param \mysqli $oMysqli
|
||||
*
|
||||
* @return boolean true if the connection was really established using TLS
|
||||
* @return boolean true if the connection was really established using TLS, false otherwise
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @used-by GetMysqliInstance
|
||||
* @uses IsMySqlVarNonEmpty
|
||||
* @uses 'ssl_version' MySQL var
|
||||
* @uses 'ssl_cipher' MySQL var
|
||||
*/
|
||||
private static function IsOpenedDbConnectionUsingTls($oMysqli)
|
||||
{
|
||||
if (self::$m_oMysqli == null)
|
||||
{
|
||||
self::$m_oMysqli = $oMysqli;
|
||||
}
|
||||
|
||||
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version');
|
||||
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher');
|
||||
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version', $oMysqli);
|
||||
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher', $oMysqli);
|
||||
|
||||
return ($bNonEmptySslVersionVar && $bNonEmptySslCipherVar);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sVarName
|
||||
* @param mysqli $oMysqli connection to use for the query
|
||||
*
|
||||
* @return bool
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @uses SHOW STATUS queries
|
||||
* @uses 'SHOW SESSION STATUS' queries
|
||||
*/
|
||||
private static function IsMySqlVarNonEmpty($sVarName)
|
||||
private static function IsMySqlVarNonEmpty($sVarName, $oMysqli)
|
||||
{
|
||||
try
|
||||
{
|
||||
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1);
|
||||
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1, $oMysqli);
|
||||
}
|
||||
catch (MySQLQueryHasNoResultException $e)
|
||||
{
|
||||
@@ -838,27 +840,28 @@ class CMDBSource
|
||||
/**
|
||||
* @param string $sSql
|
||||
* @param int $iCol beginning at 0
|
||||
* @param mysqli $oMysqli if not null will query using this connection, otherwise will use {@see GetMySQLiForQuery}
|
||||
*
|
||||
* @return string corresponding cell content on the first line
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLQueryHasNoResultException
|
||||
* @since 2.7.5-2 2.7.6 3.0.0 N°4215 new optional mysqli param
|
||||
*/
|
||||
public static function QueryToScalar($sSql, $iCol = 0)
|
||||
public static function QueryToScalar($sSql, $iCol = 0, $oMysqli = null)
|
||||
{
|
||||
$oMysqliForQuery = $oMysqli ?: DbConnectionWrapper::GetDbConnection(true);
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
|
||||
$oResult = DbConnectionWrapper::GetDbConnection(true)->query($sSql);
|
||||
try {
|
||||
/** @noinspection NullPointerExceptionInspection this shouldn't happen : either cnx is passed or the DB was init */
|
||||
$oResult = $oMysqliForQuery->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
catch (mysqli_sql_exception $e) {
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
if ($oResult === false)
|
||||
{
|
||||
if ($oResult === false) {
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class Config
|
||||
protected $m_aSettings = [
|
||||
'log_level_min' => [
|
||||
'type' => 'array',
|
||||
'description' => 'Optional min log level per channel',
|
||||
'description' => 'Optional min log level, per channel.',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
@@ -115,9 +115,9 @@ class Config
|
||||
],
|
||||
'log_level_min.write_in_db' => [
|
||||
'type' => 'array',
|
||||
'description' => 'Additional configuration that enable "in DB" logs for Exception on compatible code.',
|
||||
'default' => [ 'Exception' => 'Error', ],
|
||||
'value' => [ 'Exception' => 'Error', ],
|
||||
'description' => 'Optional min log level IN DB, per channel.',
|
||||
'default' => '',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
@@ -1070,72 +1070,88 @@ class Config
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'transactions_gc_threshold' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'probability in percent for the garbage collector to be triggered (100 mean always)',
|
||||
'default' => 10, // added in itop 2.7.4, before the GC was always called
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'type' => 'integer',
|
||||
'description' => 'probability in percent for the garbage collector to be triggered (100 mean always)',
|
||||
'default' => 10, // added in itop 2.7.4, before the GC was always called
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'log_transactions' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not to enable the debug log for the transactions.',
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not to enable the debug log for the transactions.',
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'concurrent_lock_enabled' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not to activate the locking mechanism in order to prevent concurrent edition of the same object.',
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not to activate the locking mechanism in order to prevent concurrent edition of the same object.',
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'concurrent_lock_expiration_delay' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Delay (in seconds) for a concurrent lock to expire',
|
||||
'default' => 120,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'type' => 'integer',
|
||||
'description' => 'Delay (in seconds) for a concurrent lock to expire',
|
||||
'default' => 120,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'concurrent_lock_override_profiles' => [
|
||||
'type' => 'array',
|
||||
'description' => 'The list of profiles allowed to "kill" a lock',
|
||||
'default' => ['Administrator'],
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'type' => 'array',
|
||||
'description' => 'The list of profiles allowed to "kill" a lock',
|
||||
'default' => ['Administrator'],
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'force_transition_confirmation' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If set to true force confirmation in all transition even if there is no field to complete',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'html_sanitizer' => [
|
||||
'type' => 'string',
|
||||
'description' => 'The class to use for HTML sanitization: HTMLDOMSanitizer, HTMLPurifierSanitizer or HTMLNullSanitizer',
|
||||
'default' => 'HTMLDOMSanitizer',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'svg_sanitizer' => [
|
||||
'type' => 'string',
|
||||
'description' => 'The class to use for HTML sanitization: HTMLDOMSanitizer, HTMLPurifierSanitizer or HTMLNullSanitizer',
|
||||
'default' => 'HTMLDOMSanitizer',
|
||||
'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' => [
|
||||
'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.',
|
||||
'default' => '250',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'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.',
|
||||
'default' => '250',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'inline_image_max_storage_width' => [
|
||||
'type' => 'integer',
|
||||
'description' => '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',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'type' => 'integer',
|
||||
'description' => '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',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'draft_attachments_lifetime' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Lifetime (in seconds) of drafts\' attachments and inline images: after this duration, the garbage collector will delete them.',
|
||||
'type' => 'integer',
|
||||
'description' => 'Lifetime (in seconds) of drafts\' attachments and inline images: after this duration, the garbage collector will delete them.',
|
||||
'default' => 86400,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
@@ -1327,9 +1343,9 @@ class Config
|
||||
],
|
||||
'mentions.allowed_classes' => [
|
||||
'type' => 'array',
|
||||
'description' => 'Classes which can be mentioned through the autocomplete in the caselogs. Key of the array must be a single character that will trigger the autocomplete, value can be either a DM class or a valid OQL (eg. "@" => "Person", "?" => "SELECT FAQ WHERE status = \'published\'")',
|
||||
'description' => 'Classes which can be mentioned through the autocomplete in the caselogs. Key of the array must be a single character that will trigger the autocomplete, value must be a DM class (eg. "@" => "Person", "?" => "FAQ")',
|
||||
'default' => [
|
||||
'@' => 'SELECT Person WHERE status = \'active\'',
|
||||
'@' => 'Person',
|
||||
],
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
@@ -1463,6 +1479,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.hide_administrators' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'behind_reverse_proxy' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',
|
||||
|
||||
@@ -1470,24 +1470,78 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
public function GetIcon($bImgTag = true)
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
|
||||
if($this->HasHighlightIcon()) {
|
||||
$sIconUrl = MetaModel::GetHighlightScale($sClass)[$this->ComputeHighlightCode()]['icon'];
|
||||
if($bImgTag) {
|
||||
return "<img src=\"$sIconUrl\" style=\"vertical-align:middle\"/>";
|
||||
}
|
||||
else {
|
||||
return $sIconUrl;
|
||||
}
|
||||
}
|
||||
|
||||
// Get object instance own image if it exists
|
||||
$sImageAttCode = MetaModel::GetImageAttributeCode($sClass);
|
||||
$sIconUrl = $this->HasInstanceIcon() ? $this->Get($sImageAttCode)->GetDisplayURL($sClass, $this->GetKey(), $sImageAttCode) : '';
|
||||
if (strlen($sIconUrl) > 0) {
|
||||
if($bImgTag) {
|
||||
return "<img src=\"$sIconUrl\"/>";
|
||||
}
|
||||
else {
|
||||
return $sIconUrl;
|
||||
}
|
||||
}
|
||||
return MetaModel::GetClassIcon(get_class($this), $bImgTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True if the object has an image attribute as semantic attribute and its value is not empty
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function HasInstanceIcon(): bool
|
||||
{
|
||||
$bHasInstanceIcon = false;
|
||||
$sClass = get_class($this);
|
||||
|
||||
if (!$this->IsNew() && MetaModel::HasImageAttributeCode($sClass)) {
|
||||
$sImageAttCode = MetaModel::GetImageAttributeCode($sClass);
|
||||
if (!empty($sImageAttCode)) {
|
||||
/** @var \ormDocument $oImage */
|
||||
$oImage = $this->Get($sImageAttCode);
|
||||
$bHasInstanceIcon = !$oImage->IsEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
return $bHasInstanceIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool True if the class has an highlight icon declared for the current object state
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function HasHighlightIcon(): bool
|
||||
{
|
||||
$bHasHighlightIcon = false;
|
||||
|
||||
$sCode = $this->ComputeHighlightCode();
|
||||
$sClass = get_class($this);
|
||||
|
||||
if($sCode != '')
|
||||
{
|
||||
$aHighlightScale = MetaModel::GetHighlightScale(get_class($this));
|
||||
$aHighlightScale = MetaModel::GetHighlightScale($sClass);
|
||||
if (array_key_exists($sCode, $aHighlightScale))
|
||||
{
|
||||
$sIconUrl = $aHighlightScale[$sCode]['icon'];
|
||||
if($bImgTag)
|
||||
{
|
||||
return "<img src=\"$sIconUrl\" style=\"vertical-align:middle\"/>";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sIconUrl;
|
||||
}
|
||||
$bHasHighlightIcon = true;
|
||||
}
|
||||
}
|
||||
return MetaModel::GetClassIcon(get_class($this), $bImgTag);
|
||||
}
|
||||
|
||||
return $bHasHighlightIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3166,17 +3220,19 @@ abstract class DBObject implements iDisplay
|
||||
foreach ($aMentionedIds as $sMentionedId) {
|
||||
/** @var \DBObject $oMentionedObject */
|
||||
$oMentionedObject = MetaModel::GetObject($sMentionedClass, $sMentionedId);
|
||||
// Important: Here the "$this->object()$" placeholder is actually the mentioned object and not the current object. The current object can be used through the $source->object()$ placeholder.
|
||||
// This is due to the current implementation of triggers, the events will only be visible on the object the trigger's OQL is based on... 😕
|
||||
$aTriggerArgs = $this->ToArgs('source') + array('this->object()' => $oMentionedObject);
|
||||
$aTriggerArgs = $this->ToArgs('this') + array('mentioned->object()' => $oMentionedObject);
|
||||
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sMentionedClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"),
|
||||
array(), $aParams);
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \TriggerOnObjectMention $oTrigger */
|
||||
try {
|
||||
// Ensure to handle only mentioned object in the trigger's scope
|
||||
if ($oTrigger->IsMentionedObjectInScope($oMentionedObject) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
@@ -4005,6 +4061,58 @@ abstract class DBObject implements iDisplay
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to set a date computed from another date with extra logic
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sAttCode attribute code of a date or date-time which will be set
|
||||
* @param string $sModifier string specifying how to modify the date time
|
||||
* @param string $sAttCodeSource attribute code of a date or date-time used as source
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetComputedDate($sAttCode, $sModifier = '', $sAttCodeSource = '')
|
||||
{
|
||||
$oDate = new DateTime(); // Use now if no Source provided
|
||||
if ($sAttCodeSource != "") {
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCodeSource);
|
||||
$oSourceValue = $this->Get($sAttCodeSource);
|
||||
if (!$oAttDef->IsNull($oSourceValue)) {
|
||||
// Use the existing value as the Source date
|
||||
$oDate = new DateTime($oSourceValue);
|
||||
}
|
||||
}
|
||||
$oDate->modify($sModifier);
|
||||
$this->Set($sAttCode, $oDate->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to set a date computed from another date with extra logic
|
||||
* Call SetComputedDate() only of the internal representation of the attribute is null.
|
||||
*
|
||||
* @api
|
||||
* @see SetComputedDate()
|
||||
*
|
||||
* @param string $sAttCode attribute code which will be set
|
||||
* @param string $sModifier string specifying how to modify the date time
|
||||
* @param string $sAttCodeSource attribute code used as source
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetComputedDateIfNull($sAttCode, $sModifier = '', $sAttCodeSource = '')
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$oCurrentValue = $this->Get($sAttCode);
|
||||
if ($oAttDef->IsNull($oCurrentValue)) {
|
||||
$this->SetComputedDate($sAttCode, $sModifier, $sAttCodeSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to set a value only if it is currently undefined
|
||||
*
|
||||
@@ -4023,40 +4131,35 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$oCurrentValue = $this->Get($sAttCode);
|
||||
if ($oAttDef->IsNull($oCurrentValue))
|
||||
{
|
||||
if ($oAttDef->IsNull($oCurrentValue)) {
|
||||
$this->SetCurrentDate($sAttCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to set the current logged in user for the given attribute
|
||||
* Suitable for use as a lifecycle action
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper to set the current logged in user for the given attribute
|
||||
* Suitable for use as a lifecycle action
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*/
|
||||
public function SetCurrentUser($sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef instanceof AttributeString)
|
||||
{
|
||||
if ($oAttDef instanceof AttributeString) {
|
||||
// Note: the user friendly name is the contact friendly name if a contact is attached to the logged in user
|
||||
$this->Set($sAttCode, UserRights::GetUserFriendlyName());
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
} else {
|
||||
if ($oAttDef->IsExternalKey()) {
|
||||
/** @var \AttributeExternalKey $oAttDef */
|
||||
if ($oAttDef->GetTargetClass() != 'User')
|
||||
{
|
||||
if ($oAttDef->GetTargetClass() != 'User') {
|
||||
throw new Exception("SetCurrentUser: the attribute $sAttCode must be an external key to 'User', found '".$oAttDef->GetTargetClass()."'");
|
||||
}
|
||||
}
|
||||
@@ -4575,8 +4678,9 @@ abstract class DBObject implements iDisplay
|
||||
public function GetRelatedObjectsUp($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
|
||||
{
|
||||
$oGraph = new RelationGraph();
|
||||
$oGraph->AddSourceObject($this);
|
||||
$oGraph->AddSinkObject($this);
|
||||
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
|
||||
|
||||
return $oGraph;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,10 +127,8 @@ class DisplayableNode extends GraphNode
|
||||
if ($this->GetProperty('source')) {
|
||||
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(204, 51, 51)));
|
||||
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, 16 * 1.25 * $fScale, 0, 360, 'D');
|
||||
}
|
||||
else if ($this->GetProperty('sink'))
|
||||
{
|
||||
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(51, 51, 204)));
|
||||
} else if ($this->GetProperty('sink')) {
|
||||
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(51, 51, 204)));
|
||||
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, 16 * 1.25 * $fScale, 0, 360, 'D');
|
||||
}
|
||||
|
||||
@@ -409,7 +407,7 @@ class DisplayableNode extends GraphNode
|
||||
{
|
||||
$oNewNode = new DisplayableGroupNode($oGraph, $sNewId);
|
||||
$oNewNode->SetProperty('label', 'x'.$aGroupProps['count']);
|
||||
$oNewNode->SetProperty('icon_url', $aGroupProps['icon_url']);
|
||||
$oNewNode->SetProperty('icon_url', MetaModel::GetClassIcon($sClass, false));
|
||||
$oNewNode->SetProperty('class', $sClass);
|
||||
$oNewNode->SetProperty('is_reached', ($sStatus == 'reached'));
|
||||
$oNewNode->SetProperty('count', $aGroupProps['count']);
|
||||
@@ -589,7 +587,7 @@ class DisplayableRedundancyNode extends DisplayableNode
|
||||
{
|
||||
$oNewNode = new DisplayableGroupNode($oGraph, '-'.$this->GetId().'::'.$sClass.'/'.$sStatus);
|
||||
$oNewNode->SetProperty('label', 'x'.count($aGroupProps['nodes']));
|
||||
$oNewNode->SetProperty('icon_url', $aGroupProps['icon_url']);
|
||||
$oNewNode->SetProperty('icon_url', MetaModel::GetClassIcon($sClass, false));
|
||||
$oNewNode->SetProperty('is_reached', ($sStatus == 'is_reached'));
|
||||
$oNewNode->SetProperty('class', $sClass);
|
||||
$oNewNode->SetProperty('count', count($aGroupProps['nodes']));
|
||||
@@ -767,12 +765,9 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
{
|
||||
$bReached = $this->GetProperty('is_reached');
|
||||
$oPdf->SetFillColor(255, 255, 255);
|
||||
if ($bReached)
|
||||
{
|
||||
if ($bReached) {
|
||||
$aBorderColor = array(100, 100, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$aBorderColor = array(200, 200, 200);
|
||||
}
|
||||
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aBorderColor));
|
||||
@@ -780,14 +775,11 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
$sIconUrl = $this->GetProperty('icon_url');
|
||||
$sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
|
||||
$oPdf->SetAlpha(1);
|
||||
$oPdf->Circle($this->x*$fScale, $this->y*$fScale, $this->GetWidth() / 2 * $fScale, 0, 360, 'DF');
|
||||
|
||||
if ($bReached)
|
||||
{
|
||||
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, $this->GetWidth() / 2 * $fScale, 0, 360, 'DF');
|
||||
|
||||
if ($bReached) {
|
||||
$oPdf->SetAlpha(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$oPdf->SetAlpha(0.4);
|
||||
}
|
||||
$oPdf->AddImage($sIconPath, ($this->x - 17) * $fScale, ($this->y - 17) * $fScale, 16 * $fScale, 16 * $fScale);
|
||||
@@ -801,9 +793,8 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
|
||||
public function GetTooltip($aContextDefs)
|
||||
{
|
||||
$sHtml = '';
|
||||
$iGroupIdx = $this->GetProperty('group_index');
|
||||
$sHtml .= '<a href="#" onclick="$(\'.itop-simple-graph\').simple_graph(\'show_group\', \'relation_group_'.$iGroupIdx.'\');">'.Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx))."</a>";
|
||||
$sHtml = '<a href="#" id="a_'.$iGroupIdx.'" onclick="$(this).closest(\'.tippy-box\').parent().hide();$(\'.itop-simple-graph\').simple_graph(\'show_group\', \'relation_group_'.$iGroupIdx.'\');return false;">'.Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx))."</a>";
|
||||
$sHtml .= '<hr/>';
|
||||
$sHtml .= '<table><tbody><tr>';
|
||||
$sHtml .= '<td style="vertical-align:top;padding-right: 0.5em;"><img class="ibo-class-icon ibo-is-small" src="'.$this->GetProperty('icon_url').'"></td><td style="vertical-align:top">'.MetaModel::GetName($this->GetObjectClass()).'<br/>';
|
||||
@@ -855,66 +846,68 @@ class DisplayableGraph extends SimpleGraph
|
||||
@unlink($sTempFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build a DisplayableGraph from a RelationGraph
|
||||
*
|
||||
* @param RelationGraph $oGraph
|
||||
* @param number $iGroupingThreshold
|
||||
* @param string $bDirectionDown
|
||||
*
|
||||
* @return DisplayableGraph
|
||||
*/
|
||||
public static function FromRelationGraph(RelationGraph $oGraph, $iGroupingThreshold = 20, $bDirectionDown = true)
|
||||
public static function FromRelationGraph(RelationGraph $oGraph, $iGroupingThreshold = 20, $bDirectionDown = true, $bForPdf = false)
|
||||
{
|
||||
$oNewGraph = new DisplayableGraph();
|
||||
$oNewGraph->bDirectionDown = $bDirectionDown;
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
|
||||
|
||||
$oNodesIter = new RelationTypeIterator($oGraph, 'Node');
|
||||
foreach($oNodesIter as $oNode)
|
||||
{
|
||||
foreach ($oNodesIter as $oNode) {
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
switch(get_class($oNode))
|
||||
{
|
||||
case 'RelationObjectNode':
|
||||
$oNewNode = new DisplayableNode($oNewGraph, $oNode->GetId(), 0, 0);
|
||||
|
||||
$oObj = $oNode->GetProperty('object');
|
||||
$sClass = get_class($oObj);
|
||||
if ($oNode->GetProperty('source'))
|
||||
{
|
||||
if (!array_key_exists($sClass, $oNewGraph->aSourceObjects))
|
||||
{
|
||||
$oNewGraph->aSourceObjects[$sClass] = array();
|
||||
switch (get_class($oNode)) {
|
||||
case 'RelationObjectNode':
|
||||
$oNewNode = new DisplayableNode($oNewGraph, $oNode->GetId(), 0, 0);
|
||||
|
||||
$oObj = $oNode->GetProperty('object');
|
||||
$sClass = get_class($oObj);
|
||||
if ($oNode->GetProperty('source')) {
|
||||
if (!array_key_exists($sClass, $oNewGraph->aSourceObjects)) {
|
||||
$oNewGraph->aSourceObjects[$sClass] = array();
|
||||
}
|
||||
$oNewGraph->aSourceObjects[$sClass][] = $oObj->GetKey();
|
||||
$oNewNode->SetProperty('source', true);
|
||||
}
|
||||
$oNewGraph->aSourceObjects[$sClass][] = $oObj->GetKey();
|
||||
$oNewNode->SetProperty('source', true);
|
||||
}
|
||||
if ($oNode->GetProperty('sink'))
|
||||
{
|
||||
if (!array_key_exists($sClass, $oNewGraph->aSinkObjects))
|
||||
{
|
||||
$oNewGraph->aSinkObjects[$sClass] = array();
|
||||
if ($oNode->GetProperty('sink')) {
|
||||
if (!array_key_exists($sClass, $oNewGraph->aSinkObjects)) {
|
||||
$oNewGraph->aSinkObjects[$sClass] = array();
|
||||
}
|
||||
$oNewGraph->aSinkObjects[$sClass][] = $oObj->GetKey();
|
||||
$oNewNode->SetProperty('sink', true);
|
||||
}
|
||||
$oNewGraph->aSinkObjects[$sClass][] = $oObj->GetKey();
|
||||
$oNewNode->SetProperty('sink', true);
|
||||
}
|
||||
$oNewNode->SetProperty('object', $oObj);
|
||||
$oNewNode->SetProperty('icon_url', $oObj->GetIcon(false));
|
||||
$oNewNode->SetProperty('label', $oObj->GetRawName());
|
||||
$oNewNode->SetProperty('is_reached', $bDirectionDown ? $oNode->GetProperty('is_reached') : true); // When going "up" is_reached does not matter
|
||||
$oNewNode->SetProperty('is_reached_allowed', $oNode->GetProperty('is_reached_allowed'));
|
||||
$oNewNode->SetProperty('context_root_causes', $oNode->GetProperty('context_root_causes'));
|
||||
break;
|
||||
|
||||
$oNewNode->SetProperty('object', $oObj);
|
||||
$sIconUrl = $oObj->GetIcon(false);
|
||||
//when icons are displayed in a PDF file, puts the image data in place of the url
|
||||
if ($bForPdf && strpos($sIconUrl, 'display_document') > 0) {
|
||||
$sImageAttCode = MetaModel::GetImageAttributeCode($sClass);
|
||||
$sIconUrl = '@'.$oObj->Get($sImageAttCode)->GetData();
|
||||
}
|
||||
$oNewNode->SetProperty('icon_url', $sIconUrl);
|
||||
$oNewNode->SetProperty('label', $oObj->GetRawName());
|
||||
$oNewNode->SetProperty('is_reached', $bDirectionDown ? $oNode->GetProperty('is_reached') : true); // When going "up" is_reached does not matter
|
||||
$oNewNode->SetProperty('is_reached_allowed', $oNode->GetProperty('is_reached_allowed'));
|
||||
$oNewNode->SetProperty('context_root_causes', $oNode->GetProperty('context_root_causes'));
|
||||
break;
|
||||
|
||||
default:
|
||||
$oNewNode = new DisplayableRedundancyNode($oNewGraph, $oNode->GetId(), 0, 0);
|
||||
$iNbReached = (is_null($oNode->GetProperty('is_reached_count'))) ? 0 : $oNode->GetProperty('is_reached_count');
|
||||
$oNewNode->SetProperty('label', $iNbReached."/".($oNode->GetProperty('min_up') + $oNode->GetProperty('threshold')));
|
||||
$oNewNode->SetProperty('min_up', $oNode->GetProperty('min_up'));
|
||||
$oNewNode->SetProperty('threshold', $oNode->GetProperty('threshold'));
|
||||
$oNewNode->SetProperty('is_reached_count', $iNbReached);
|
||||
$oNewNode->SetProperty('is_reached', true);
|
||||
$oNewNode = new DisplayableRedundancyNode($oNewGraph, $oNode->GetId(), 0, 0);
|
||||
$iNbReached = (is_null($oNode->GetProperty('is_reached_count'))) ? 0 : $oNode->GetProperty('is_reached_count');
|
||||
$oNewNode->SetProperty('label', $iNbReached."/".($oNode->GetProperty('min_up') + $oNode->GetProperty('threshold')));
|
||||
$oNewNode->SetProperty('min_up', $oNode->GetProperty('min_up'));
|
||||
$oNewNode->SetProperty('threshold', $oNode->GetProperty('threshold'));
|
||||
$oNewNode->SetProperty('is_reached_count', $iNbReached);
|
||||
$oNewNode->SetProperty('is_reached', true);
|
||||
}
|
||||
}
|
||||
$oEdgesIter = new RelationTypeIterator($oGraph, 'Edge');
|
||||
|
||||
@@ -97,64 +97,163 @@ class HTMLNullSanitizer extends HTMLSanitizer
|
||||
{
|
||||
return $sHTML;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A standard-compliant HTMLSanitizer based on the HTMLPurifier library by Edward Z. Yang
|
||||
* Complete but quite slow
|
||||
* http://htmlpurifier.org
|
||||
* Common implementation for sanitizer using DOM parsing
|
||||
*/
|
||||
/*
|
||||
class HTMLPurifierSanitizer extends HTMLSanitizer
|
||||
abstract class DOMSanitizer extends HTMLSanitizer
|
||||
{
|
||||
protected static $oPurifier = null;
|
||||
/** @var DOMDocument */
|
||||
protected $oDoc;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (self::$oPurifier == null)
|
||||
{
|
||||
$sLibPath = APPROOT.'lib/htmlpurifier/HTMLPurifier.auto.php';
|
||||
if (!file_exists($sLibPath))
|
||||
{
|
||||
throw new Exception("Missing library '$sLibPath', cannot use HTMLPurifierSanitizer.");
|
||||
}
|
||||
require_once($sLibPath);
|
||||
abstract public function GetTagsWhiteList();
|
||||
|
||||
$oPurifierConfig = HTMLPurifier_Config::createDefault();
|
||||
$oPurifierConfig->set('Core.Encoding', 'UTF-8'); // defaults to 'UTF-8'
|
||||
$oPurifierConfig->set('HTML.Doctype', 'XHTML 1.0 Strict'); // defaults to 'XHTML 1.0 Transitional'
|
||||
$oPurifierConfig->set('URI.AllowedSchemes', array (
|
||||
'http' => true,
|
||||
'https' => true,
|
||||
'data' => true, // This one is not present by default
|
||||
));
|
||||
$sPurifierCache = APPROOT.'data/HTMLPurifier';
|
||||
if (!is_dir($sPurifierCache))
|
||||
{
|
||||
mkdir($sPurifierCache);
|
||||
}
|
||||
if (!is_dir($sPurifierCache))
|
||||
{
|
||||
throw new Exception("Could not create the cache directory '$sPurifierCache'");
|
||||
}
|
||||
$oPurifierConfig->set('Cache.SerializerPath', $sPurifierCache); // no trailing slash
|
||||
self::$oPurifier = new HTMLPurifier($oPurifierConfig);
|
||||
}
|
||||
}
|
||||
abstract public function GetTagsBlackList();
|
||||
|
||||
abstract public function GetAttrsWhiteList();
|
||||
|
||||
abstract public function GetAttrsBlackList();
|
||||
|
||||
abstract public function GetStylesWhiteList();
|
||||
|
||||
public function DoSanitize($sHTML)
|
||||
{
|
||||
$sCleanHtml = self::$oPurifier->purify($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->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;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
||||
@@ -239,6 +338,31 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'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()
|
||||
{
|
||||
parent::__construct();
|
||||
@@ -264,139 +388,152 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
// Replace badly encoded non breaking space
|
||||
$sHTML = preg_replace('~\xc2\xa0~', ' ', $sHTML);
|
||||
|
||||
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
}
|
||||
|
||||
$this->CleanNode($this->oDoc);
|
||||
|
||||
public function PrintDoc()
|
||||
{
|
||||
$oXPath = new DOMXPath($this->oDoc);
|
||||
$sXPath = "//body";
|
||||
$oNodesList = $oXPath->query($sXPath);
|
||||
|
||||
if ($oNodesList->length == 0)
|
||||
{
|
||||
if ($oNodesList->length == 0) {
|
||||
// No body, save the whole document
|
||||
$sCleanHtml = $this->oDoc->saveHTML();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Export only the content of the body tag
|
||||
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
|
||||
// remove the body tag itself
|
||||
$sCleanHtml = str_replace( array('<body>', '</body>'), '', $sCleanHtml);
|
||||
$sCleanHtml = str_replace(array('<body>', '</body>'), '', $sCleanHtml);
|
||||
}
|
||||
|
||||
return $sCleanHtml;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
// Gather the attributes to remove
|
||||
if ($oElement->hasAttributes())
|
||||
{
|
||||
foreach($oElement->attributes as $oAttr)
|
||||
{
|
||||
$sAttr = strtolower($oAttr->name);
|
||||
if (!in_array($sAttr, self::$aTagsWhiteList[strtolower($oElement->tagName)]))
|
||||
{
|
||||
// Forbidden (or unknown) attribute
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value))
|
||||
{
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else if ($sAttr == 'style')
|
||||
{
|
||||
// Special processing for style tags
|
||||
$sCleanStyle = $this->CleanStyle($oAttr->value);
|
||||
if ($sCleanStyle == '')
|
||||
{
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oElement->setAttribute($oAttr->name, $sCleanStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aAttrToRemove as $sName)
|
||||
{
|
||||
$oElement->removeAttribute($sName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oElement->hasChildNodes())
|
||||
{
|
||||
$aChildElementsToRemove = array();
|
||||
// Gather the child noes to remove
|
||||
foreach($oElement->childNodes as $oNode)
|
||||
{
|
||||
if (($oNode instanceof DOMElement) && (!array_key_exists(strtolower($oNode->tagName), self::$aTagsWhiteList)))
|
||||
{
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
}
|
||||
else if ($oNode instanceof DOMComment)
|
||||
{
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recurse
|
||||
$this->CleanNode($oNode);
|
||||
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img'))
|
||||
{
|
||||
InlineImage::ProcessImageTag($oNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aChildElementsToRemove as $oDomElement)
|
||||
{
|
||||
$oElement->removeChild($oDomElement);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function CleanStyle($sStyle)
|
||||
/**
|
||||
* @return string[]
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
|
||||
*/
|
||||
public function GetTagsBlackList()
|
||||
{
|
||||
$aAllowedStyles = array();
|
||||
$aItems = explode(';', $sStyle);
|
||||
{
|
||||
foreach($aItems as $sItem)
|
||||
{
|
||||
$aElements = explode(':', trim($sItem));
|
||||
if (in_array(trim(strtolower($aElements[0])), static::$aStylesWhiteList))
|
||||
{
|
||||
$aAllowedStyles[] = trim($sItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(';', $aAllowedStyles);
|
||||
return [
|
||||
'script',
|
||||
];
|
||||
}
|
||||
|
||||
protected function IsValidAttributeContent($sAttributeName, $sValue)
|
||||
public function GetAttrsWhiteList()
|
||||
{
|
||||
if (array_key_exists($sAttributeName, self::$aAttrsWhiteList))
|
||||
{
|
||||
return preg_match(self::$aAttrsWhiteList[$sAttributeName], $sValue);
|
||||
}
|
||||
return true;
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Events#document_event_attributes
|
||||
*/
|
||||
public function GetAttrsBlackList()
|
||||
{
|
||||
return [
|
||||
'onbegin',
|
||||
'onbegin',
|
||||
'onrepeat',
|
||||
'onabort',
|
||||
'onerror',
|
||||
'onerror',
|
||||
'onscroll',
|
||||
'onunload',
|
||||
'oncopy',
|
||||
'oncut',
|
||||
'onpaste',
|
||||
'oncancel',
|
||||
'oncanplay',
|
||||
'oncanplaythrough',
|
||||
'onchange',
|
||||
'onclick',
|
||||
'onclose',
|
||||
'oncuechange',
|
||||
'ondblclick',
|
||||
'ondrag',
|
||||
'ondragend',
|
||||
'ondragenter',
|
||||
'ondragleave',
|
||||
'ondragover',
|
||||
'ondragstart',
|
||||
'ondrop',
|
||||
'ondurationchange',
|
||||
'onemptied',
|
||||
'onended',
|
||||
'onerror',
|
||||
'onfocus',
|
||||
'oninput',
|
||||
'oninvalid',
|
||||
'onkeydown',
|
||||
'onkeypress',
|
||||
'onkeyup',
|
||||
'onload',
|
||||
'onloadeddata',
|
||||
'onloadedmetadata',
|
||||
'onloadstart',
|
||||
'onmousedown',
|
||||
'onmouseenter',
|
||||
'onmouseleave',
|
||||
'onmousemove',
|
||||
'onmouseout',
|
||||
'onmouseover',
|
||||
'onmouseup',
|
||||
'onmousewheel',
|
||||
'onpause',
|
||||
'onplay',
|
||||
'onplaying',
|
||||
'onprogress',
|
||||
'onratechange',
|
||||
'onreset',
|
||||
'onresize',
|
||||
'onscroll',
|
||||
'onseeked',
|
||||
'onseeking',
|
||||
'onselect',
|
||||
'onshow',
|
||||
'onstalled',
|
||||
'onsubmit',
|
||||
'onsuspend',
|
||||
'ontimeupdate',
|
||||
'ontoggle',
|
||||
'onvolumechange',
|
||||
'onwaiting',
|
||||
'onactivate',
|
||||
'onfocusin',
|
||||
'onfocusout',
|
||||
];
|
||||
}
|
||||
|
||||
public function GetStylesWhiteList()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function LoadDoc($sHTML)
|
||||
{
|
||||
@$this->oDoc->loadXml($sHTML, LIBXML_NOBLANKS);
|
||||
}
|
||||
|
||||
public function PrintDoc()
|
||||
{
|
||||
return $this->oDoc->saveXML();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -548,8 +548,6 @@ JS
|
||||
// Hook the file upload of all CKEditor instances
|
||||
$('.htmlEditor').each(function() {
|
||||
var oEditor = $(this).ckeditorGet();
|
||||
oEditor.config.extraPlugins = 'font,uploadimage';
|
||||
oEditor.config.uploadUrl = '$sAbsoluteUrlAppRoot'+'pages/ajax.render.php';
|
||||
oEditor.config.filebrowserBrowseUrl = '$sAbsoluteUrlAppRoot'+'pages/ajax.render.php?operation=cke_browse&temp_id=$sTempId&obj_class=$sObjClass&obj_key=$iObjKey';
|
||||
oEditor.on( 'fileUploadResponse', function( evt ) {
|
||||
var fileLoader = evt.data.fileLoader;
|
||||
|
||||
@@ -502,20 +502,22 @@ class FileLog
|
||||
|
||||
protected function Write($sText, $sLevel = '', $sChannel = '', $aContext = array())
|
||||
{
|
||||
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7).' | ');
|
||||
$sTextPrefix .= str_pad(UserRights::GetUserId(), 5)." | ";
|
||||
$sTextSuffix = empty($sChannel) ? '' : " | $sChannel";
|
||||
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
|
||||
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
|
||||
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7));
|
||||
$sTextPrefix .= ' | ';
|
||||
$sTextPrefix .= str_pad(LogAPI::GetUserInfo(), 5)." | ";
|
||||
|
||||
if (empty($sLogFilePath))
|
||||
{
|
||||
$sTextSuffix = ' | '.(empty($sChannel) ? '' : $sChannel);
|
||||
$sTextSuffix .= ' |||';
|
||||
|
||||
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
|
||||
|
||||
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
|
||||
if (empty($sLogFilePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hLogFile = @fopen($sLogFilePath, 'a');
|
||||
if ($hLogFile !== false)
|
||||
{
|
||||
if ($hLogFile !== false) {
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
if (empty($aContext)) {
|
||||
@@ -540,12 +542,13 @@ class FileLog
|
||||
*/
|
||||
class LogChannels
|
||||
{
|
||||
public const CLI = 'CLI';
|
||||
public const CONSOLE = 'console';
|
||||
public const DEADLOCK = 'DeadLock';
|
||||
public const CLI = 'CLI';
|
||||
public const CONSOLE = 'console';
|
||||
public const DEADLOCK = 'DeadLock';
|
||||
public const INLINE_IMAGE = 'InlineImage';
|
||||
public const PORTAL = 'portal';
|
||||
public const CMDB_SOURCE = 'cmdbsource';
|
||||
public const PORTAL = 'portal';
|
||||
public const CMDB_SOURCE = 'cmdbsource';
|
||||
public const CORE = 'core';
|
||||
}
|
||||
|
||||
|
||||
@@ -559,28 +562,42 @@ abstract class LogAPI
|
||||
public const LEVEL_OK = 'Ok';
|
||||
public const LEVEL_DEBUG = 'Debug';
|
||||
public const LEVEL_TRACE = 'Trace';
|
||||
|
||||
/**
|
||||
* @var string default log level
|
||||
* @see GetMinLogLevel
|
||||
* @see GetMinLogLevel
|
||||
* @used-by GetLevelDefault
|
||||
* @var string default log level.
|
||||
* @since 2.7.1 N°2977
|
||||
*/
|
||||
public const LEVEL_DEFAULT = self::LEVEL_OK;
|
||||
|
||||
/**
|
||||
* @see GetMinLogLevel
|
||||
* @used-by GetLevelDefault
|
||||
* @var string|bool default log level when writing to DB: false by default in order to disable EventIssue creation, and so on, do not change the behavior.
|
||||
* @since 3.0.0 N°4261
|
||||
*/
|
||||
public const LEVEL_DEFAULT_DB = false;
|
||||
|
||||
protected static $aLevelsPriority = array(
|
||||
self::LEVEL_ERROR => 400,
|
||||
self::LEVEL_ERROR => 400,
|
||||
self::LEVEL_WARNING => 300,
|
||||
self::LEVEL_INFO => 200,
|
||||
self::LEVEL_OK => 200,
|
||||
self::LEVEL_DEBUG => 100,
|
||||
self::LEVEL_TRACE => 50,
|
||||
self::LEVEL_INFO => 200,
|
||||
self::LEVEL_OK => 200,
|
||||
self::LEVEL_DEBUG => 100,
|
||||
self::LEVEL_TRACE => 50,
|
||||
);
|
||||
|
||||
public const ENUM_CONFIG_PARAM_FILE = 'log_level_min';
|
||||
public const ENUM_CONFIG_PARAM_DB = 'log_level_min.write_in_db';
|
||||
|
||||
/**
|
||||
* @var \Config attribute allowing to mock config in the tests
|
||||
*/
|
||||
protected static $m_oMockMetaModelConfig = null;
|
||||
|
||||
protected static $oLastEventIssue = null;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
// m_oFileLog is not defined as a class attribute so that each impl will have its own
|
||||
@@ -631,10 +648,6 @@ abstract class LogAPI
|
||||
*/
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
|
||||
{
|
||||
if (!static::$m_oFileLog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset(self::$aLevelsPriority[$sLevel])) {
|
||||
IssueLog::Error("invalid log level '{$sLevel}'");
|
||||
|
||||
@@ -645,22 +658,46 @@ abstract class LogAPI
|
||||
$sChannel = static::CHANNEL_DEFAULT;
|
||||
}
|
||||
|
||||
if (!static::IsLogLevelEnabled($sLevel, $sChannel)) {
|
||||
return;
|
||||
static::WriteLog($sLevel, $sMessage, $sChannel, $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ConfigException
|
||||
*/
|
||||
protected static function WriteLog(string $sLevel, string $sMessage, ?string $sChannel = null, ?array $aContext = array()): void
|
||||
{
|
||||
if (
|
||||
(null !== static::$m_oFileLog)
|
||||
&& static::IsLogLevelEnabled($sLevel, $sChannel, static::ENUM_CONFIG_PARAM_FILE)
|
||||
) {
|
||||
static::$m_oFileLog->$sLevel($sMessage, $sChannel, $aContext);
|
||||
}
|
||||
|
||||
static::$m_oFileLog->$sLevel($sMessage, $sChannel, $aContext);
|
||||
if (static::IsLogLevelEnabled($sLevel, $sChannel, static::ENUM_CONFIG_PARAM_DB)) {
|
||||
self::WriteToDb($sMessage, $sChannel, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetUserInfo(): ?string
|
||||
{
|
||||
$oConnectedUser = UserRights::GetUserObject();
|
||||
if (is_null($oConnectedUser)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $oConnectedUser->GetKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ConfigException if log wrongly configured
|
||||
* @uses GetMinLogLevel
|
||||
*/
|
||||
final public static function IsLogLevelEnabled(string $sLevel, string $sChannel, string $sCode = 'log_level_min'): bool
|
||||
final public static function IsLogLevelEnabled(string $sLevel, string $sChannel, string $sConfigKey = self::ENUM_CONFIG_PARAM_FILE): bool
|
||||
{
|
||||
$sMinLogLevel = self::GetMinLogLevel($sChannel, $sCode);
|
||||
$sMinLogLevel = self::GetMinLogLevel($sChannel, $sConfigKey);
|
||||
|
||||
if ($sMinLogLevel === false || $sMinLogLevel === 'false') {
|
||||
// the is_bool call is to remove a IDE O:) warning as $sMinLogLevel is typed as string
|
||||
if ((is_bool($sMinLogLevel) && ($sMinLogLevel === false)) || $sMinLogLevel === 'false') {
|
||||
return false;
|
||||
}
|
||||
if (!is_string($sMinLogLevel)) {
|
||||
@@ -678,7 +715,7 @@ abstract class LogAPI
|
||||
|
||||
/**
|
||||
* @param string $sChannel
|
||||
* @param string $sCode
|
||||
* @param string $sConfigKey
|
||||
*
|
||||
* @return string one of the LEVEL_* const value : the one configured it if exists, otherwise default log level for this channel
|
||||
* Config can be set :
|
||||
@@ -694,21 +731,44 @@ abstract class LogAPI
|
||||
*
|
||||
* @uses \LogAPI::GetConfig()
|
||||
* @uses `log_level_min` config parameter
|
||||
* @uses `log_level_min.write_to_db` config parameter
|
||||
* @uses \LogAPI::GetLevelDefault
|
||||
*
|
||||
* @link https://www.itophub.io/wiki/page?id=3_0_0%3Aadmin%3Alog iTop log reference
|
||||
*/
|
||||
protected static function GetMinLogLevel($sChannel, $sCode = 'log_level_min')
|
||||
protected static function GetMinLogLevel($sChannel, $sConfigKey = self::ENUM_CONFIG_PARAM_FILE)
|
||||
{
|
||||
$sLogLevelMin = static::GetLogConfig($sConfigKey);
|
||||
|
||||
$sConfiguredLevelForChannel = static::GetMinLogLevelFromChannel($sLogLevelMin, $sChannel, $sConfigKey);
|
||||
if (!is_null($sConfiguredLevelForChannel)) {
|
||||
return $sConfiguredLevelForChannel;
|
||||
}
|
||||
|
||||
return static::GetMinLogLevelFromDefault($sLogLevelMin, $sChannel, $sConfigKey);
|
||||
}
|
||||
|
||||
final protected static function GetLogConfig($sConfigKey)
|
||||
{
|
||||
$oConfig = static::GetConfig();
|
||||
if (!$oConfig instanceof Config) {
|
||||
return static::GetLevelDefault();
|
||||
return static::GetLevelDefault($sConfigKey);
|
||||
}
|
||||
|
||||
$sLogLevelMin = $oConfig->Get($sCode);
|
||||
return $oConfig->Get($sConfigKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $sLogLevelMin log config parameter value
|
||||
* @param string $sChannel
|
||||
* @param string $sConfigKey config option key
|
||||
*
|
||||
* @return string|null null if not defined
|
||||
*/
|
||||
protected static function GetMinLogLevelFromChannel($sLogLevelMin, $sChannel, $sConfigKey)
|
||||
{
|
||||
if (empty($sLogLevelMin)) {
|
||||
return static::GetLevelDefault();
|
||||
return static::GetLevelDefault($sConfigKey);
|
||||
}
|
||||
|
||||
if (!is_array($sLogLevelMin)) {
|
||||
@@ -719,19 +779,81 @@ abstract class LogAPI
|
||||
return $sLogLevelMin[$sChannel];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static function GetMinLogLevelFromDefault($sLogLevelMin, $sChannel, $sConfigKey)
|
||||
{
|
||||
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT])) {
|
||||
return $sLogLevelMin[static::CHANNEL_DEFAULT];
|
||||
return $sLogLevelMin[static::CHANNEL_DEFAULT];
|
||||
}
|
||||
|
||||
// Even though the *self*::CHANNEL_DEFAULT is set to '' in the current class (LogAPI), the test below is necessary as the CHANNEL_DEFAULT constant can be (and is!) overloaded in derivated classes, don't remove this test to factorize it with the previous one.
|
||||
// Even though the *self*::CHANNEL_DEFAULT is set to '' in the current class (LogAPI), the test below is necessary as the CHANNEL_DEFAULT constant can be (and is!) overloaded in children classes, don't remove this test to factorize it with the previous one.
|
||||
if (isset($sLogLevelMin[''])) {
|
||||
return $sLogLevelMin[''];
|
||||
return $sLogLevelMin[''];
|
||||
}
|
||||
|
||||
return static::GetLevelDefault();
|
||||
return static::GetLevelDefault($sConfigKey);
|
||||
}
|
||||
|
||||
protected static function WriteToDb(string $sMessage, string $sChannel, array $aContext): void
|
||||
{
|
||||
if (false === MetaModel::IsLogEnabledIssue()) {
|
||||
return;
|
||||
}
|
||||
if (false === MetaModel::IsValidClass('EventIssue')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Protect against reentrance
|
||||
static $bWriteToDbReentrance;
|
||||
if ($bWriteToDbReentrance === true) {
|
||||
return;
|
||||
}
|
||||
$bWriteToDbReentrance = true;
|
||||
|
||||
try {
|
||||
self::$oLastEventIssue = static::GetEventIssue($sMessage, $sChannel, $aContext);
|
||||
self::$oLastEventIssue->DBInsertNoReload();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// calling low level methods : if we would call Error() for example we would try to write to DB again...
|
||||
static::$m_oFileLog->Error('Failed to log issue into the DB', LogChannels::CORE, [
|
||||
'exception message' => $e->getMessage(),
|
||||
'exception stack' => $e->getTraceAsString(),
|
||||
]);
|
||||
}
|
||||
finally {
|
||||
$bWriteToDbReentrance = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \OQLException
|
||||
*/
|
||||
protected static function GetEventIssue(string $sMessage, string $sChannel, array $aContext): EventIssue
|
||||
{
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
|
||||
$sCurrentCallStack = var_export($aStack, true);
|
||||
|
||||
$oEventIssue = new EventIssue();
|
||||
$oEventIssue->Set('issue', $sMessage);
|
||||
$oEventIssue->Set('message', $sMessage);
|
||||
$oEventIssue->Set('date', $sDate);
|
||||
$oEventIssue->Set('userinfo', static::GetUserInfo());
|
||||
$oEventIssue->Set('callstack', $sCurrentCallStack);
|
||||
$oEventIssue->Set('data', $aContext);
|
||||
|
||||
return $oEventIssue;
|
||||
}
|
||||
|
||||
/**
|
||||
* **Warning** : during \MFCompiler::Compile the config will be partial, so when logging in this method you won't get the proper log config !
|
||||
* See N°4345
|
||||
*
|
||||
* @uses m_oMockMetaModelConfig if defined
|
||||
* @uses \MetaModel::GetConfig()
|
||||
*/
|
||||
@@ -741,16 +863,29 @@ abstract class LogAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* A method to override if default log level needs to be computed. Otherwise simply override the {@see LEVEL_DEFAULT} constant
|
||||
* A method to override if default log level needs to be computed. Otherwise, simply override the corresponding constants
|
||||
*
|
||||
* @used-by GetMinLogLevel
|
||||
* @uses \LogAPI::LEVEL_DEFAULT
|
||||
*
|
||||
* @since 3.0.0 N°3731
|
||||
* @param string $sConfigKey config key used for log
|
||||
*
|
||||
* @return string|bool if false, then disable log for any level
|
||||
*
|
||||
* @uses \LogAPI::LEVEL_DEFAULT
|
||||
* @uses \LogAPI::LEVEL_DEFAULT_DB
|
||||
*
|
||||
* @since 3.0.0 N°3731 Method creation
|
||||
* @since 3.0.0 N°4261 add specific default level for DB write
|
||||
*/
|
||||
protected static function GetLevelDefault(): string
|
||||
protected static function GetLevelDefault(string $sConfigKey)
|
||||
{
|
||||
return static::LEVEL_DEFAULT;
|
||||
switch ($sConfigKey) {
|
||||
case static::ENUM_CONFIG_PARAM_DB:
|
||||
return static::LEVEL_DEFAULT_DB;
|
||||
case static::ENUM_CONFIG_PARAM_FILE:
|
||||
default:
|
||||
return static::LEVEL_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -765,6 +900,16 @@ class SetupLog extends LogAPI
|
||||
const LEVEL_DEFAULT = self::LEVEL_INFO;
|
||||
|
||||
protected static $m_oFileLog = null;
|
||||
|
||||
/**
|
||||
* In the setup there is no user logged...
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function GetUserInfo(): ?string
|
||||
{
|
||||
return 'SETUP';
|
||||
}
|
||||
}
|
||||
|
||||
class IssueLog extends LogAPI
|
||||
@@ -803,6 +948,7 @@ class DeadLockLog extends LogAPI
|
||||
parent::Enable($sTargetFile);
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnreachableStatementInspection we want to keep the break statements to keep clarity and avoid errors */
|
||||
private static function GetChannelFromMysqlErrorNo($iMysqlErrorNo)
|
||||
{
|
||||
switch ($iMysqlErrorNo)
|
||||
@@ -962,13 +1108,25 @@ class DeprecatedCallsLog extends LogAPI
|
||||
return true;
|
||||
}
|
||||
|
||||
protected static function GetLevelDefault(): string
|
||||
/**
|
||||
* Override so that :
|
||||
* - if we are in dev mode ({@see \utils::IsDevelopmentEnvironment()}), the level for file will be DEBUG
|
||||
* - else call parent method
|
||||
*
|
||||
* In other words, when in dev mode all deprecated calls will be logged to file
|
||||
*
|
||||
*/
|
||||
protected static function GetLevelDefault(string $sConfigKey)
|
||||
{
|
||||
if ($sConfigKey === self::ENUM_CONFIG_PARAM_DB) {
|
||||
return parent::GetLevelDefault($sConfigKey);
|
||||
}
|
||||
|
||||
if (utils::IsDevelopmentEnvironment()) {
|
||||
return static::LEVEL_DEBUG;
|
||||
}
|
||||
|
||||
return static::LEVEL_DEFAULT;
|
||||
return parent::GetLevelDefault($sConfigKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1130,14 +1288,12 @@ class LogFileRotationProcess implements iScheduledProcess
|
||||
*
|
||||
* Please use {@see ExceptionLog::LogException()} to log exceptions
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @since 3.0.0 N°4261 class creation to ease logging when an exception occurs
|
||||
*/
|
||||
class ExceptionLog extends LogAPI
|
||||
{
|
||||
const CHANNEL_DEFAULT = 'Exception';
|
||||
const CONTEXT_EXCEPTION = '__exception';
|
||||
|
||||
private static $oLastEventIssue = null;
|
||||
public const CHANNEL_DEFAULT = 'Exception';
|
||||
public const CONTEXT_EXCEPTION = '__exception';
|
||||
|
||||
protected static $m_oFileLog = null;
|
||||
|
||||
@@ -1145,90 +1301,141 @@ class ExceptionLog extends LogAPI
|
||||
* This method should be used to write logs.
|
||||
*
|
||||
* As it encapsulate the operations performed using the Exception, you should prefer it to the standard API inherited from LogApi `ExceptionLog::Error($oException->getMessage(), get_class($oException), ['__exception' => $oException]);`
|
||||
* The parameter order is not standard, but in our use case, the resulting API is way more convenient this way.
|
||||
* The parameter order is not standard, but in our use case, the resulting API is way more convenient this way !
|
||||
*/
|
||||
public static function LogException(Exception $oException, $aContext = array(), $sLevel = self::LEVEL_WARNING)
|
||||
public static function LogException(Throwable $oException, $aContext = array(), $sLevel = self::LEVEL_ERROR): void
|
||||
{
|
||||
if (empty($aContext[self::CONTEXT_EXCEPTION])) {
|
||||
$aContext[self::CONTEXT_EXCEPTION] = $oException;
|
||||
}
|
||||
|
||||
if (empty($aContext['exception class'])) {
|
||||
$aContext['exception class'] = get_class($oException);
|
||||
}
|
||||
|
||||
if (empty($aContext['file'])) {
|
||||
$aContext['file'] = $oException->getFile();
|
||||
}
|
||||
|
||||
if (empty($aContext['line'])) {
|
||||
$aContext['line'] =$oException->getLine();
|
||||
}
|
||||
|
||||
self::Log($sLevel, $oException->getMessage(), get_class($oException), $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @throws \ConfigException if log wrongly configured
|
||||
*/
|
||||
public static function Log($sLevel, $sMessage, $sClass = null, $aContext = array())
|
||||
{
|
||||
if (!static::$m_oFileLog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isset(self::$aLevelsPriority[$sLevel])) {
|
||||
IssueLog::Error("invalid log level '{$sLevel}'");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$sExceptionClass = get_class($oException);
|
||||
|
||||
$sChannel = self::FindClassChannel($sClass);
|
||||
if (static::IsLogLevelEnabled($sLevel, $sChannel)) {
|
||||
static::$m_oFileLog->$sLevel($sMessage, $sChannel, array_diff_key($aContext, [self::CONTEXT_EXCEPTION => null])); //The exception should not be included in the error.log because of its verbosity.
|
||||
}
|
||||
|
||||
$sDbChannel = self::FindClassChannel($sClass, 'log_level_min.write_in_db');
|
||||
if (static::IsLogLevelEnabled($sLevel, $sDbChannel, 'log_level_min.write_in_db')) {
|
||||
self::WriteToDb($aContext);
|
||||
}
|
||||
$aDefaultValues = [
|
||||
self::CONTEXT_EXCEPTION => $oException,
|
||||
'exception class' => $sExceptionClass,
|
||||
'file' => $oException->getFile(),
|
||||
'line' => $oException->getLine(),
|
||||
];
|
||||
$aContext = array_merge($aDefaultValues, $aContext);
|
||||
|
||||
parent::Log($sLevel, $oException->getMessage(), $sExceptionClass, $aContext);
|
||||
}
|
||||
|
||||
protected static function FindClassChannel($sClass, $sCode = 'log_level_min')
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
|
||||
{
|
||||
$oConfig = static::GetConfig();
|
||||
if (!$oConfig instanceof Config) {
|
||||
return static::GetLevelDefault();
|
||||
throw new ApplicationException('Do not call this directly, prefer using ExceptionLog::LogException() instead');
|
||||
}
|
||||
|
||||
/** @noinspection PhpParameterNameChangedDuringInheritanceInspection */
|
||||
protected static function WriteLog(string $sLevel, string $sMessage, ?string $sExceptionClass = null, ?array $aContext = array()): void
|
||||
{
|
||||
if (
|
||||
(null !== static::$m_oFileLog)
|
||||
&& static::IsLogLevelEnabled($sLevel, $sExceptionClass, static::ENUM_CONFIG_PARAM_FILE)
|
||||
) {
|
||||
$sExceptionClassConfiguredForFile = static::ExceptionClassFromHierarchy($sExceptionClass, static::ENUM_CONFIG_PARAM_FILE);
|
||||
if (null === $sExceptionClassConfiguredForFile) {
|
||||
$sExceptionClassConfiguredForFile = $sExceptionClass;
|
||||
}
|
||||
|
||||
// clearing the Exception object as it is too verbose to write to a file !
|
||||
$aContextForFile = array_diff_key($aContext, [self::CONTEXT_EXCEPTION => null]);
|
||||
|
||||
static::$m_oFileLog->$sLevel($sMessage, $sExceptionClassConfiguredForFile, $aContextForFile);
|
||||
}
|
||||
|
||||
$sLogLevelMin = $oConfig->Get($sCode);
|
||||
if (static::IsLogLevelEnabled($sLevel, $sExceptionClass, static::ENUM_CONFIG_PARAM_DB)) {
|
||||
$sExceptionClassConfiguredForDb = static::ExceptionClassFromHierarchy($sExceptionClass, static::ENUM_CONFIG_PARAM_DB);
|
||||
if (null === $sExceptionClassConfiguredForDb) {
|
||||
$sExceptionClassConfiguredForDb = $sExceptionClass;
|
||||
}
|
||||
self::WriteToDb($sMessage, $sExceptionClassConfiguredForDb, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($sLogLevelMin)) {
|
||||
return $sClass;
|
||||
/**
|
||||
* Will seek for the configuration based on the exception class, using {@see \ExceptionLog::ExceptionClassFromHierarchy()}
|
||||
*
|
||||
* @param string $sExceptionClass
|
||||
* @param string $sConfigKey
|
||||
*
|
||||
* @return string
|
||||
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
|
||||
*/
|
||||
protected static function GetMinLogLevel($sExceptionClass, $sConfigKey = self::ENUM_CONFIG_PARAM_FILE)
|
||||
{
|
||||
$sLogLevelMin = static::GetLogConfig($sConfigKey);
|
||||
$sExceptionClassInConfig = static::ExceptionClassFromHierarchy($sExceptionClass, $sConfigKey);
|
||||
|
||||
if (null !== $sExceptionClassInConfig) {
|
||||
return $sConfigKey[$sExceptionClassInConfig];
|
||||
}
|
||||
|
||||
if (!is_array($sLogLevelMin)) {
|
||||
return $sClass;
|
||||
return static::GetMinLogLevelFromDefault($sLogLevelMin, $sExceptionClass, $sConfigKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searching config first for the current exception class
|
||||
* If not found we are seeking for config for all the parent classes
|
||||
*
|
||||
* That means if we are logging a UnknownClassOqlException, we will seek log config all the way the class hierarchy :
|
||||
* 1. UnknownClassOqlException
|
||||
* 2. OqlNormalizeException
|
||||
* 3. OQLException
|
||||
* 4. CoreException
|
||||
* 5. Exception
|
||||
*
|
||||
* @param string $sExceptionClass
|
||||
* @param string $sConfigKey
|
||||
*
|
||||
* @return string|null the current or parent class name defined in the config, otherwise null if no class of the hierarchy found in the config
|
||||
*/
|
||||
protected static function ExceptionClassFromHierarchy($sExceptionClass, $sConfigKey = self::ENUM_CONFIG_PARAM_FILE)
|
||||
{
|
||||
$sLogLevelMin = static::GetLogConfig($sConfigKey);
|
||||
|
||||
if (false === is_array($sLogLevelMin)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$sParentClass = $sClass;
|
||||
while (
|
||||
(!isset($sLogLevelMin[$sParentClass]))
|
||||
&&
|
||||
($sParentClass !== false)
|
||||
)
|
||||
{
|
||||
$sParentClass = get_parent_class($sParentClass);
|
||||
$sExceptionClassInHierarchy = $sExceptionClass;
|
||||
while ($sExceptionClassInHierarchy !== false) {
|
||||
$sConfiguredLevelForExceptionClass = static::GetMinLogLevelFromChannel($sLogLevelMin, $sExceptionClassInHierarchy, $sConfigKey);
|
||||
if (!is_null($sConfiguredLevelForExceptionClass)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$sExceptionClassInHierarchy = get_parent_class($sExceptionClassInHierarchy);
|
||||
}
|
||||
|
||||
if (isset($sLogLevelMin[$sParentClass])) {
|
||||
return $sParentClass;
|
||||
if ($sExceptionClassInHierarchy === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $sClass;
|
||||
return $sExceptionClassInHierarchy;
|
||||
}
|
||||
|
||||
protected static function GetEventIssue(string $sMessage, string $sChannel, array $aContext): EventIssue
|
||||
{
|
||||
$oEventIssue = parent::GetEventIssue($sMessage, $sChannel, $aContext);
|
||||
|
||||
$oContextException = $aContext[self::CONTEXT_EXCEPTION];
|
||||
unset($aContext[self::CONTEXT_EXCEPTION]);
|
||||
|
||||
$sIssue = ($oContextException instanceof CoreException) ? $oContextException->GetIssue() : 'PHP Exception';
|
||||
$sErrorStackTrace = ($oContextException instanceof CoreException) ? $oContextException->getFullStackTraceAsString() : $oContextException->getTraceAsString();
|
||||
$aContextData = ($oContextException instanceof CoreException) ? $oContextException->getContextData() : [];
|
||||
|
||||
$oEventIssue->Set('issue', $sIssue);
|
||||
$oEventIssue->Set('message', $oContextException->getMessage());
|
||||
$oEventIssue->Set('callstack', $sErrorStackTrace);
|
||||
$oEventIssue->Set('data', array_merge($aContextData, $aContext));
|
||||
|
||||
return $oEventIssue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1236,43 +1443,12 @@ class ExceptionLog extends LogAPI
|
||||
*/
|
||||
public static function Enable($sTargetFile = null)
|
||||
{
|
||||
if (empty($sTargetFile))
|
||||
{
|
||||
if (empty($sTargetFile)) {
|
||||
$sTargetFile = APPROOT.'log/error.log';
|
||||
}
|
||||
parent::Enable($sTargetFile);
|
||||
}
|
||||
|
||||
private static function WriteToDb(array $aContext): void
|
||||
{
|
||||
$oContextException = $aContext[self::CONTEXT_EXCEPTION];
|
||||
unset($aContext[self::CONTEXT_EXCEPTION]);
|
||||
|
||||
if (MetaModel::IsLogEnabledIssue()) {
|
||||
if (MetaModel::IsValidClass('EventIssue')) {
|
||||
try {
|
||||
self::$oLastEventIssue = new EventIssue();
|
||||
|
||||
$sIssue = ($oContextException instanceof CoreException) ? $oContextException->GetIssue() : 'PHP Exception';
|
||||
$sErrorStackTrace = ($oContextException instanceof CoreException) ? $oContextException->getFullStackTraceAsString() : $oContextException->getTraceAsString();
|
||||
$aContextData = ($oContextException instanceof CoreException) ? $oContextException->getContextData() : [];
|
||||
|
||||
|
||||
self::$oLastEventIssue->Set('message', $oContextException->getMessage());
|
||||
self::$oLastEventIssue->Set('userinfo', '');
|
||||
self::$oLastEventIssue->Set('issue', $sIssue);
|
||||
self::$oLastEventIssue->Set('impact', '');
|
||||
self::$oLastEventIssue->Set('callstack', $sErrorStackTrace);
|
||||
self::$oLastEventIssue->Set('data', array_merge($aContextData, $aContext));
|
||||
self::$oLastEventIssue->DBInsertNoReload();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
IssueLog::Error("Failed to log issue into the DB");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Used by the tests
|
||||
*/
|
||||
|
||||
@@ -3083,8 +3083,7 @@ abstract class MetaModel
|
||||
// Set attribute code
|
||||
self::$m_aClassParams[$sPHPClass]['state_attcode'] = self::$m_aClassParams[$sParent]['state_attcode'];
|
||||
|
||||
// Set states
|
||||
self::$m_aStates[$sPHPClass] = self::$m_aStates[$sParent];
|
||||
// Note: Don't set self::$m_aStates[$sPHPClass], it has already been done by self::Init_DefineState()
|
||||
}
|
||||
// - Image attribute
|
||||
$bParentHasImageAttribute = (isset(self::$m_aClassParams[$sParent]['image_attcode']) && !empty(self::$m_aClassParams[$sParent]['image_attcode']));
|
||||
@@ -4132,19 +4131,26 @@ abstract class MetaModel
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param int $iOption one of ENUM_CHILD_CLASSES_EXCLUDETOP, ENUM_CHILD_CLASSES_ALL
|
||||
* @param bool $bRootFirst Only when $iOption NOT set to ENUM_CHILD_CLASSES_EXCLUDETOP. If true, the $sClass will be the first element of the returned array, otherwise it will be the last (legacy behavior)
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0 Added $bRootFirst param.
|
||||
*/
|
||||
public static function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP)
|
||||
public static function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP, $bRootFirst = false)
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
|
||||
$aRes = self::$m_aChildClasses[$sClass];
|
||||
if ($iOption != ENUM_CHILD_CLASSES_EXCLUDETOP)
|
||||
{
|
||||
// Add it to the list
|
||||
$aRes[] = $sClass;
|
||||
if ($bRootFirst) {
|
||||
// Root class on top
|
||||
array_unshift($aRes, $sClass);
|
||||
} else {
|
||||
// Root class at the end, legacy behavior
|
||||
$aRes[] = $sClass;
|
||||
}
|
||||
}
|
||||
|
||||
return $aRes;
|
||||
@@ -4531,15 +4537,15 @@ abstract class MetaModel
|
||||
/**
|
||||
* Check (and updates if needed) the hierarchical keys
|
||||
*
|
||||
* @param boolean $bDiagnosticsOnly If true only a diagnostic pass will be run, returning true or false
|
||||
* @param boolean $bVerbose Displays some information about what is done/what needs to be done
|
||||
* @param boolean $bForceComputation If true, the _left and _right parameters will be recomputed even if some
|
||||
* @param bool $bDiagnosticsOnly If true only a diagnostic pass will be run, returning true or false
|
||||
* @param bool $bVerbose Displays some information about what is done/what needs to be done
|
||||
* @param bool $bForceComputation If true, the _left and _right parameters will be recomputed even if some
|
||||
* values already exist in the DB
|
||||
*
|
||||
* @return bool
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function CheckHKeys($bDiagnosticsOnly = false, $bVerbose = false, $bForceComputation = false)
|
||||
public static function CheckHKeys(bool $bDiagnosticsOnly = false, bool $bVerbose = false, bool $bForceComputation = false)
|
||||
{
|
||||
$bChangeNeeded = false;
|
||||
foreach(self::GetClasses() as $sClass)
|
||||
@@ -5964,7 +5970,8 @@ abstract class MetaModel
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated 2.7.0 N°2369 will be removed in 2.8
|
||||
* @deprecated 2.7.0 N°2369 Method will not be removed any time soon as we still need to drop view if the instance is migrating from an iTop 2.x to an iTop 3.0 or newer, even if they skip iTop 3.0.
|
||||
* @since 3.0.0 Does not recreate SQL views, only drops them. Method has not been renamed to avoid regressions
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
@@ -7457,9 +7464,11 @@ abstract class MetaModel
|
||||
* @param string $sInput
|
||||
* @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);
|
||||
|
||||
@@ -7469,7 +7478,7 @@ abstract class MetaModel
|
||||
|
||||
$aSearches = array();
|
||||
$aReplacements = array();
|
||||
foreach($aParams as $sSearch => $replace)
|
||||
foreach ($aParams as $sSearch => $replace)
|
||||
{
|
||||
// Some environment parameters are objects, we just need scalars
|
||||
if (is_object($replace))
|
||||
|
||||
@@ -506,6 +506,7 @@ class ormCaseLog {
|
||||
$oBlockRenderer = new BlockRenderer($oBlock);
|
||||
$sHtml = $oBlockRenderer->RenderHtml();
|
||||
$sScript = $oBlockRenderer->RenderJsInlineRecursively($oBlock,iUIBlock::ENUM_JS_TYPE_ON_READY);
|
||||
$aJsFiles = $oBlockRenderer->GetJsFiles();
|
||||
if ($sScript!=''){
|
||||
if ($oP == null) {
|
||||
$sScript = '<script>'.$sScript.'</script>';
|
||||
@@ -514,6 +515,18 @@ class ormCaseLog {
|
||||
$oP->add_ready_script($sScript);
|
||||
}
|
||||
}
|
||||
// Ugly hack as we use a block and strip its content above, we'll also need JS files it depends on
|
||||
if(count($aJsFiles) > 0){
|
||||
foreach ($aJsFiles as $sFileAbsUrl) {
|
||||
if ($oP === null) {
|
||||
$sScript = '<script src="'.$sFileAbsUrl.'"></></script>';
|
||||
$sHtml .= $sScript;
|
||||
} else {
|
||||
$oP->add_linked_script($sFileAbsUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ final class ormTagSet extends ormSet
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tags indexed by code
|
||||
* @return array index: code, value: corresponding {@see \TagSetFieldData}
|
||||
*/
|
||||
public function GetTags()
|
||||
{
|
||||
|
||||
@@ -354,7 +354,7 @@ EOF
|
||||
}
|
||||
else if ($oAttDef instanceof AttributeTagSet)
|
||||
{
|
||||
$sField = $oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, '');
|
||||
$sField = utils::HtmlEntities($oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, ''));
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else
|
||||
|
||||
@@ -41,6 +41,7 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
"db_table" => "priv_trigger",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-conflict.svg'),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
@@ -238,7 +239,8 @@ abstract class TriggerOnObject extends Trigger
|
||||
* @param $iObjectId
|
||||
* @param array $aChanges
|
||||
*
|
||||
* @return bool
|
||||
* @return bool True if the object of ID $iObjectId is within the scope of the OQL defined by the "filter" attribute
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
@@ -252,6 +254,7 @@ abstract class TriggerOnObject extends Trigger
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sFilter);
|
||||
$oSearch->AddCondition('id', $iObjectId, '=');
|
||||
$oSearch->AllowAllData();
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$bRet = ($oSet->Count() > 0);
|
||||
}
|
||||
@@ -582,13 +585,56 @@ class TriggerOnObjectMention extends TriggerOnObject
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("mentioned_filter", array("allowed_values" => null, "sql" => "mentioned_filter", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'mentioned_filter', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject $oObject
|
||||
*
|
||||
* @return bool True if $oObject is within the scope of the OQL defined by the "mentioned_filter" attribute OR if no mentioned_filter defined. Otherwise, returns false.
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function IsMentionedObjectInScope(DBObject $oObject)
|
||||
{
|
||||
$sFilter = trim($this->Get('mentioned_filter'));
|
||||
if (strlen($sFilter) > 0)
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sFilter);
|
||||
$sSearchClass = $oSearch->GetClass();
|
||||
|
||||
// If filter not on current object class (or descendants), consider it as not in scope
|
||||
if (is_a($oObject, $sSearchClass, true) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oSearch->AddCondition('id', $oObject->GetKey(), '=');
|
||||
if (MetaModel::IsAbstract($oSearch->GetClass())) {
|
||||
$oSearch->AddCondition('finalclass', get_class($oObject), '=');
|
||||
}
|
||||
|
||||
$aParams = $oObject->ToArgs('this');
|
||||
$oSet = new DBObjectSet($oSearch, [], $aParams);
|
||||
$bRet = $oSet->CountExceeds(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
|
||||
return $bRet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -638,7 +638,7 @@ abstract class UserInternal extends User
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core,grant_by_profile",
|
||||
"category" => "core,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "login",
|
||||
"state_attcode" => "",
|
||||
@@ -1437,6 +1437,21 @@ class UserRights
|
||||
return self::$m_oRealUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int|string ID of the connected user : if impersonate then use {@see m_oRealUser}, else {@see m_oUser}. If no user set then return ''
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
|
||||
*/
|
||||
public static function GetConnectedUserId() {
|
||||
if (false === is_null(static::$m_oRealUser)) {
|
||||
return static::$m_oRealUser->GetKey();
|
||||
}
|
||||
if (false === is_null(static::$m_oUser)) {
|
||||
return static::$m_oUser->GetKey();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -1492,7 +1507,7 @@ class UserRights
|
||||
try
|
||||
{
|
||||
// Check Bug 1436 for details
|
||||
if (MetaModel::HasCategory($sClass, 'bizmodel') || MetaModel::HasCategory($sClass, 'silo'))
|
||||
if (MetaModel::HasCategory($sClass, 'bizmodel') || MetaModel::HasCategory($sClass, 'silo') || MetaModel::HasCategory($sClass, 'filter'))
|
||||
{
|
||||
return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass, $aSettings);
|
||||
}
|
||||
@@ -1844,15 +1859,15 @@ class UserRights
|
||||
{
|
||||
self::$m_aCacheUsers = array('internal' => array(), 'external' => array());
|
||||
}
|
||||
|
||||
if (!array_key_exists($sLogin, self::$m_aCacheUsers[$sAuthentication]))
|
||||
|
||||
if (!isset(self::$m_aCacheUsers[$sAuthentication][$sLogin]))
|
||||
{
|
||||
switch($sAuthentication)
|
||||
{
|
||||
case 'external':
|
||||
$sBaseClass = 'UserExternal';
|
||||
break;
|
||||
|
||||
|
||||
case 'internal':
|
||||
$sBaseClass = 'UserInternal';
|
||||
break;
|
||||
@@ -1862,6 +1877,7 @@ class UserRights
|
||||
assert(false); // should never happen
|
||||
}
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT $sBaseClass WHERE login = :login");
|
||||
$oSearch->AllowAllData();
|
||||
if (!$bAllowDisabledUsers)
|
||||
{
|
||||
$oSearch->AddCondition('status', 'enabled');
|
||||
|
||||
@@ -22,4 +22,8 @@ fieldset {
|
||||
|
||||
legend {
|
||||
@extend .ibo-fieldset-legend;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@extend .ibo-input-text;
|
||||
}
|
||||
@@ -32,5 +32,5 @@
|
||||
}
|
||||
|
||||
#form_part_interactive_fields_xlsx, #form_part_interactive_fields_csv, #form_part_interactive_fields_pdf {
|
||||
margin-top: $ibo-panel--spacing-top;
|
||||
margin-top: $ibo-spacing-600;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ $ibo-scrollbar--scrollbar-thumb-background-color: $ibo-color-grey-300 !default;
|
||||
$ibo-scrollbar--scrollbar-thumb-border: none !default;
|
||||
$ibo-scrollbar--scrollbar-thumb-border-radius: $ibo-border-radius-500 !default;
|
||||
|
||||
$ibo-hyperlink-color: $ibo-color-primary-500 !default;
|
||||
$ibo-hyperlink-color--on-hover: $ibo-color-primary-600 !default;
|
||||
$ibo-hyperlink-color--on-active: $ibo-color-primary-700 !default;
|
||||
$ibo-hyperlink-color: $ibo-color-primary-700 !default;
|
||||
$ibo-hyperlink-color--on-hover: $ibo-color-primary-800 !default;
|
||||
$ibo-hyperlink-color--on-active: $ibo-color-primary-900 !default;
|
||||
|
||||
$ibo-svg-illustration--fill: $ibo-color-primary-500 !default;
|
||||
|
||||
|
||||
@@ -3,17 +3,22 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "dashlet-within-dashboard";
|
||||
@import "alert/all";
|
||||
@import "button/all";
|
||||
@import "collapsible-section/all";
|
||||
@import "datatable/all";
|
||||
@import "display-block/all";
|
||||
@import "field/all";
|
||||
@import "fieldset/all";
|
||||
@import "input/all";
|
||||
@import "panel/all";
|
||||
@import "pill/all";
|
||||
@import "dashlet/all";
|
||||
@import "add-to-dashboard";
|
||||
@import "caselog-entry-form-within-activity-panel";
|
||||
@import "panel-with-datatable";
|
||||
@import "panel-with-tab-container";
|
||||
@import "panel-within-main-content";
|
||||
@import "panel-within-modal";
|
||||
@import "tab-container-within-panel";
|
||||
@import "object-details-with-tab-container";
|
||||
@import "medallion-with-blocklist";
|
||||
@import "input-within-datatable";
|
||||
@import "field-badge-within-datatable";
|
||||
@import "jquery-blockui-within-dialog";
|
||||
@import "jquery-blockui-within-datatable";
|
||||
@import "collapsible-section-within-caselog-list";
|
||||
@import "jquery-blockui-within-datatable";
|
||||
@@ -3,8 +3,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--margin: 0 !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--padding: 0 !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--margin: $ibo-spacing-0 !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--padding: $ibo-spacing-0 !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--text-color: unset !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--background-color: unset !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge-dot--size: 10px !default;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-alert--spacing-top--with-same-block: $ibo-spacing-200 !default;
|
||||
$ibo-alert--spacing-top--with-other-blocks: $ibo-spacing-500 !default;
|
||||
|
||||
/* Spacing between alert blocks */
|
||||
.ibo-alert + .ibo-alert {
|
||||
margin-top: $ibo-alert--spacing-top--with-same-block;
|
||||
}
|
||||
/* Spacing between an alert block and something else */
|
||||
.ibo-alert + .ibo-block:not(.ibo-alert) {
|
||||
margin-top: $ibo-alert--spacing-top--with-other-blocks;
|
||||
}
|
||||
6
css/backoffice/blocks-integrations/alert/_all.scss
Normal file
6
css/backoffice/blocks-integrations/alert/_all.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "alert-with-blocks";
|
||||
7
css/backoffice/blocks-integrations/button/_all.scss
Normal file
7
css/backoffice/blocks-integrations/button/_all.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "button-with-button";
|
||||
@import "button-with-button-group";
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-button--spacing-left--with-button-group: $ibo-button--spacing-left--with-same-block !default;
|
||||
|
||||
/* Reset siblings spacing */
|
||||
.ibo-button-group + .ibo-button-group,
|
||||
.ibo-button + .ibo-button-group,
|
||||
.ibo-button-group + .ibo-button{
|
||||
margin-left: $ibo-button--spacing-left--with-button-group;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-button--spacing-left--with-same-block: $ibo-spacing-200 !default;
|
||||
|
||||
.ibo-button + .ibo-button {
|
||||
margin-left: $ibo-button--spacing-left--with-same-block;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "collapsible-section-with-blocks";
|
||||
@import "collapsible-section-within-caselog-list";
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-collapsible-section--spacing-top--with-same-block: $ibo-spacing-400 !default;
|
||||
$ibo-collapsible-section--spacing-top--with-other-blocks: $ibo-spacing-500 !default;
|
||||
|
||||
/* Spacing between collapsible-section blocks */
|
||||
.ibo-collapsible-section + .ibo-collapsible-section {
|
||||
margin-top: $ibo-collapsible-section--spacing-top--with-same-block;
|
||||
}
|
||||
|
||||
/* Spacing between an alert block and something else */
|
||||
.ibo-collapsible-section + .ibo-block:not(.ibo-collapsible-section) {
|
||||
margin-top: $ibo-collapsible-section--spacing-top--with-other-blocks;
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-caselog-entry-in-collapsible-section--body--background-color: transparentize($ibo-color-grey-100,0.5) !default;
|
||||
$ibo-caselog-entry-in-collapsible-section--body--padding: 8px !default;
|
||||
$ibo-caselog-entry-in-collapsible-section--body--padding: $ibo-spacing-300 !default;
|
||||
$ibo-caselog-entry-in-collapsible-section--body--color: $ibo-color-grey-900 !default;
|
||||
|
||||
/* - caselog display in ormcaselog */
|
||||
6
css/backoffice/blocks-integrations/dashlet/_all.scss
Normal file
6
css/backoffice/blocks-integrations/dashlet/_all.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "dashlet-within-dashboard";
|
||||
@@ -3,8 +3,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-dashlet-within-dashboard--dashlet-header-static--margin-top--is-first-dashlet: 0 !default;
|
||||
$ibo-dashlet-within-dashboard--dashlet-header-static--margin-top--is-not-first-dashlet: 12px !default;
|
||||
$ibo-dashlet-within-dashboard--dashlet-header-static--margin-top--is-first-dashlet: $ibo-spacing-0 !default;
|
||||
$ibo-dashlet-within-dashboard--dashlet-header-static--margin-top--is-not-first-dashlet: $ibo-spacing-400 !default;
|
||||
|
||||
.ibo-dashboard--grid-row{
|
||||
// Margin on top to have a better visual separation like with fieldsets
|
||||
7
css/backoffice/blocks-integrations/datatable/_all.scss
Normal file
7
css/backoffice/blocks-integrations/datatable/_all.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "datatable-with-blocks";
|
||||
@import "datatable-within-panel";
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-datatable--spacing-top--with-other-blocks: $ibo-spacing-200 !default;
|
||||
|
||||
.ibo-datatable + .ibo-block{
|
||||
margin-top: $ibo-datatable--spacing-top--with-other-blocks;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "display-block-with-blocks";
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-display-block--spacing-top--with-same-block: $ibo-spacing-600 !default;
|
||||
$ibo-display-block--spacing-top--with-other-block: $ibo-spacing-500 !default;
|
||||
|
||||
|
||||
.display_block + .display_block {
|
||||
margin-top: $ibo-display-block--spacing-top--with-same-block;
|
||||
}
|
||||
|
||||
.display_block + .ibo-block:not(.display_block) {
|
||||
margin-top: $ibo-display-block--spacing-top--with-other-block;
|
||||
}
|
||||
6
css/backoffice/blocks-integrations/field/_all.scss
Normal file
6
css/backoffice/blocks-integrations/field/_all.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "field-with-field";
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-field--spacing-top--with-same-block: $ibo-spacing-500 !default;
|
||||
|
||||
.ibo-field + .ibo-field {
|
||||
margin-top: $ibo-field--spacing-top--with-same-block;
|
||||
}
|
||||
|
||||
.form_field + .form_field {
|
||||
margin-top: $ibo-field--spacing-top--with-same-block;
|
||||
}
|
||||
7
css/backoffice/blocks-integrations/fieldset/_all.scss
Normal file
7
css/backoffice/blocks-integrations/fieldset/_all.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "fieldset-with-fieldset";
|
||||
@import "fieldset-with-multicolumn";
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-fieldset--spacing-left--with-fieldset: $ibo-spacing-800 !default;
|
||||
|
||||
.ibo-fieldset + .ibo-fieldset:not(.ibo-column) {
|
||||
margin-top: $ibo-fieldset--spacing-left--with-fieldset;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-fieldset--spacing-left--with-multicolumn: $ibo-spacing-800 !default;
|
||||
|
||||
|
||||
.ibo-multi-column + .ibo-fieldset {
|
||||
margin-top: $ibo-fieldset--spacing-left--with-multicolumn;
|
||||
}
|
||||
7
css/backoffice/blocks-integrations/input/_all.scss
Normal file
7
css/backoffice/blocks-integrations/input/_all.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "input-with-label";
|
||||
@import "input-within-datatable";
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-input--spacing-left--with-label: $ibo-spacing-300 !default;
|
||||
|
||||
|
||||
/* Input reset */
|
||||
/* - Standard spacing between label and input */
|
||||
select + label, label + select, label > select,
|
||||
input + label, label + input, label > input {
|
||||
margin-left: $ibo-input--spacing-left--with-label;
|
||||
}
|
||||
@@ -4,15 +4,21 @@
|
||||
*/
|
||||
|
||||
$ibo-input-within-datatable--attribute-set-item--padding-x: $ibo-input-set--item--padding-x !default;
|
||||
$ibo-input-within-datatable--attribute-set-item--padding-y: $ibo-input-set--item--padding-y !default;
|
||||
$ibo-input-within-datatable--attribute-set-item--box-shadow: $ibo-elevation-100 !default;
|
||||
$ibo-input-within-datatable--attribute-set-item--siblings-spacing: 0.5rem !default;
|
||||
|
||||
.ibo-datatable {
|
||||
.attribute-set {
|
||||
.attribute-set-item {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0 $ibo-input-within-datatable--attribute-set-item--padding-x;
|
||||
padding: $ibo-input-within-datatable--attribute-set-item--padding-y $ibo-input-within-datatable--attribute-set-item--padding-x;
|
||||
box-shadow: $ibo-input-within-datatable--attribute-set-item--box-shadow;
|
||||
|
||||
+ .attribute-set-item {
|
||||
margin-left: $ibo-input-within-datatable--attribute-set-item--siblings-spacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
css/backoffice/blocks-integrations/panel/_all.scss
Normal file
8
css/backoffice/blocks-integrations/panel/_all.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "panel-with-blocks";
|
||||
@import "panel-within-main-content";
|
||||
@import "panel-within-modal";
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-panel--spacing-top--with-same-block: $ibo-spacing-600 !default;
|
||||
$ibo-panel--spacing-top--with-other-block: $ibo-spacing-500 !default;
|
||||
|
||||
|
||||
.ibo-panel + .ibo-panel {
|
||||
margin-top: $ibo-panel--spacing-top--with-same-block;
|
||||
}
|
||||
|
||||
.ibo-panel + .ibo-block:not(.ibo-panel) {
|
||||
margin-top: $ibo-panel--spacing-top--with-other-block;
|
||||
}
|
||||
6
css/backoffice/blocks-integrations/pill/_all.scss
Normal file
6
css/backoffice/blocks-integrations/pill/_all.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "pill-with-pill";
|
||||
14
css/backoffice/blocks-integrations/pill/_pill-with-pill.scss
Normal file
14
css/backoffice/blocks-integrations/pill/_pill-with-pill.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-pill--spacing-left--with-same-block: $ibo-spacing-500 !default;
|
||||
|
||||
|
||||
/* For pills, margin is set on the right to keep them aligned horizontally when they wrap */
|
||||
/* The drawback is that we can't set the margin only when next to another .ibo-pill as there is no such CSS selector */
|
||||
/* Also :last-child is used instead of :last-of-type as pill can be either an <a> or a <span> */
|
||||
.ibo-pill:not(:last-child) {
|
||||
margin-right: $ibo-pill--spacing-left--with-same-block;
|
||||
}
|
||||
@@ -4,8 +4,6 @@
|
||||
*/
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-alert--spacing-top--with-same-block: 6px !default;
|
||||
$ibo-alert--spacing-top--with-other-blocks: 16px !default;
|
||||
$ibo-alert--padding-y: 18px !default;
|
||||
$ibo-alert--padding-x: 20px !default;
|
||||
$ibo-alert--min-height: 30px !default;
|
||||
@@ -14,10 +12,10 @@ $ibo-alert--border-radius: $ibo-border-radius-300 !default;
|
||||
$ibo-alert--title--highlight--width: 4px !default;
|
||||
$ibo-alert--title--highlight--height: 100% !default;
|
||||
|
||||
$ibo-alert--body--margin-top: 4px !default;
|
||||
$ibo-alert--body--margin-top: $ibo-spacing-200 !default;
|
||||
|
||||
$ibo-alert-minimized--padding-y: 5px !default;
|
||||
$ibo-alert-minimized--title--padding-bottom: 0px !default;
|
||||
$ibo-alert-minimized--title--padding-bottom: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-alert--action-button--top: 5px !default;
|
||||
$ibo-alert--maximize-minimize-button--right: 30px !default;
|
||||
@@ -49,7 +47,10 @@ $ibo-alert-colors: (
|
||||
.ibo-alert.ibo-is-#{$sColor} {
|
||||
background-color: $bg-color;
|
||||
color: $text-color;
|
||||
|
||||
a {
|
||||
color: $text-color;
|
||||
@extend %ibo-font-ral-bol-150;
|
||||
}
|
||||
&::before {
|
||||
background-color: $highlight-color;
|
||||
}
|
||||
@@ -113,15 +114,6 @@ $ibo-alert-colors: (
|
||||
margin-top: $ibo-alert--body--margin-top;
|
||||
}
|
||||
|
||||
/* Spacing between alert blocks */
|
||||
.ibo-alert + .ibo-alert {
|
||||
margin-top: $ibo-alert--spacing-top--with-same-block;
|
||||
}
|
||||
/* Spacing between an alert block and something else */
|
||||
.ibo-alert + :not(.ibo-alert) {
|
||||
margin-top: $ibo-alert--spacing-top--with-other-blocks;
|
||||
}
|
||||
|
||||
.ibo-alert--action-button{
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -3,29 +3,29 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-breadcrumbs--margin-right: 32px !default;
|
||||
$ibo-breadcrumbs--margin-right: $ibo-spacing-700 !default;
|
||||
|
||||
$ibo-breadcrumbs--item--text-color: $ibo-color-grey-800 !default;
|
||||
|
||||
$ibo-breadcrumbs--item-icon--margin-x: 8px !default;
|
||||
$ibo-breadcrumbs--item-icon--margin-x: $ibo-spacing-300 !default;
|
||||
$ibo-breadcrumbs--item-icon--max-width: 16px !default;
|
||||
$ibo-breadcrumbs--item-icon--text-color: $ibo-color-grey-600 !default;
|
||||
|
||||
$ibo-breadcrumbs--item-label--max-width: 100px !default;
|
||||
|
||||
$ibo-breadcrumbs--item-separator--margin-x: 12px !default;
|
||||
$ibo-breadcrumbs--item-separator--margin-x: $ibo-spacing-400 !default;
|
||||
$ibo-breadcrumbs--item-separator--text-color: $ibo-color-grey-500 !default;
|
||||
|
||||
$ibo-breadcrumbs--previous-items-list-toggler--text-color: $ibo-color-grey-700 !default;
|
||||
$ibo-breadcrumbs--previous-items-list-toggler--margin-right: 2 * $ibo-breadcrumbs--item-separator--margin-x !default;
|
||||
|
||||
$ibo-breadcrumbs--previous-items-list--top: 37px !default;
|
||||
$ibo-breadcrumbs--previous-items-list--padding-y: 8px !default;
|
||||
$ibo-breadcrumbs--previous-items-list--padding-y: $ibo-spacing-300 !default;
|
||||
$ibo-breadcrumbs--previous-items-list--background-color: $ibo-color-white-100 !default;
|
||||
|
||||
$ibo-breadcrumbs--previous-item--text-color: $ibo-breadcrumbs--item--text-color !default;
|
||||
$ibo-breadcrumbs--previous-item--padding-x: 12px !default;
|
||||
$ibo-breadcrumbs--previous-item--padding-y: 12px !default;
|
||||
$ibo-breadcrumbs--previous-item--padding-x: $ibo-spacing-400 !default;
|
||||
$ibo-breadcrumbs--previous-item--padding-y: $ibo-spacing-400 !default;
|
||||
$ibo-breadcrumbs--previous-item-label--max-width: 200px !default;
|
||||
|
||||
.ibo-breadcrumbs{
|
||||
|
||||
@@ -63,11 +63,4 @@ $ibo-button-group--elements-separator--border-left: 1px solid transparent !defau
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset siblings spacing */
|
||||
.ibo-button-group + .ibo-button-group,
|
||||
.ibo-button + .ibo-button-group,
|
||||
.ibo-button-group + .ibo-button{
|
||||
margin-left: $ibo-button--sibling-spacing;
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-button--sibling-spacing: 5px !default;
|
||||
$ibo-button--padding-y: 6px !default;
|
||||
$ibo-button--padding-x: 9px !default;
|
||||
$ibo-button--border: 0 !default;
|
||||
@@ -11,10 +10,10 @@ $ibo-button--border-radius: $ibo-border-radius-400 !default;
|
||||
$ibo-button--box-shadow-bottom: 0px 2px 0px !default;
|
||||
$ibo-button--box-shadow-top: inset 0px 2px 0px !default;
|
||||
|
||||
$ibo-button--label--margin-left: 4px !default;
|
||||
$ibo-button--label--margin-left: $ibo-spacing-200 !default;
|
||||
|
||||
$ibo-button--vertical-align--margin-bottom: 4px !default;
|
||||
$ibo-button--vertical-align--margin-top: 4px !default;
|
||||
$ibo-button--vertical-align--margin-bottom: $ibo-spacing-200 !default;
|
||||
$ibo-button--vertical-align--margin-top: $ibo-spacing-200 !default;
|
||||
|
||||
/**
|
||||
* - Text color
|
||||
@@ -54,22 +53,22 @@ $ibo-button-colors: (
|
||||
/* Primary action does not have the colors from the primary brand color, at least not for now */
|
||||
'primary': (
|
||||
'': (
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-cyan-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-800,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-button--box-shadow-top $ibo-color-cyan-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -106,22 +105,22 @@ $ibo-button-colors: (
|
||||
),
|
||||
'danger': (
|
||||
'': (
|
||||
$ibo-color-danger-600,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-danger-800,
|
||||
$ibo-color-danger-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-danger-900,
|
||||
$ibo-color-danger-800,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-danger-700,
|
||||
$ibo-color-danger-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-danger-800,
|
||||
$ibo-color-danger-700,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-danger-900,
|
||||
$ibo-color-danger-800,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-danger-700,
|
||||
$ibo-color-danger-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-danger-800 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-700,
|
||||
$ibo-color-danger-700,
|
||||
$ibo-button--box-shadow-top $ibo-color-danger-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-800,
|
||||
$ibo-color-danger-800,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -132,22 +131,22 @@ $ibo-button-colors: (
|
||||
),
|
||||
'success': (
|
||||
'': (
|
||||
$ibo-color-success-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-success-900,
|
||||
$ibo-color-success-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-success-950,
|
||||
$ibo-color-success-900,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-success-800,
|
||||
$ibo-color-success-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-success-900,
|
||||
$ibo-color-success-800,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-success-950,
|
||||
$ibo-color-success-900,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-success-800,
|
||||
$ibo-color-success-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-success-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-800,
|
||||
$ibo-color-success-800,
|
||||
$ibo-button--box-shadow-top $ibo-color-success-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-900,
|
||||
$ibo-color-success-900,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -159,22 +158,22 @@ $ibo-button-colors: (
|
||||
/* Colors */
|
||||
'red': (
|
||||
'': (
|
||||
$ibo-color-red-600,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-red-800,
|
||||
$ibo-color-red-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-red-900,
|
||||
$ibo-color-red-800,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-red-700,
|
||||
$ibo-color-red-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-red-800,
|
||||
$ibo-color-red-700,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-red-900,
|
||||
$ibo-color-red-800,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-red-700,
|
||||
$ibo-color-red-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-red-800 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-700,
|
||||
$ibo-color-red-700,
|
||||
$ibo-button--box-shadow-top $ibo-color-red-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-800,
|
||||
$ibo-color-red-800,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -185,22 +184,22 @@ $ibo-button-colors: (
|
||||
),
|
||||
'green': (
|
||||
'': (
|
||||
$ibo-color-green-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-green-900,
|
||||
$ibo-color-green-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-green-950,
|
||||
$ibo-color-green-900,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-green-800,
|
||||
$ibo-color-green-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-green-900,
|
||||
$ibo-color-green-800,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-green-950,
|
||||
$ibo-color-green-900,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-green-800,
|
||||
$ibo-color-green-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-green-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-800,
|
||||
$ibo-color-green-800,
|
||||
$ibo-button--box-shadow-top $ibo-color-green-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-900,
|
||||
$ibo-color-green-900,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -211,22 +210,22 @@ $ibo-button-colors: (
|
||||
),
|
||||
'cyan': (
|
||||
'': (
|
||||
$ibo-color-cyan-500,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-cyan-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-700,
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-button--box-shadow-top $ibo-color-cyan-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -268,9 +267,9 @@ $ibo-button-colors: (
|
||||
'primary': (
|
||||
'': (
|
||||
transparent,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-button--box-shadow-bottom transparent,
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-color-cyan-800,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-white-100,
|
||||
@@ -475,10 +474,7 @@ $ibo-button-colors: (
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap; /* To force sub elements to be on 1 line */
|
||||
@extend %ibo-font-ral-sembol-100;
|
||||
|
||||
& + .ibo-button {
|
||||
margin-left: $ibo-button--sibling-spacing;
|
||||
}
|
||||
|
||||
|
||||
&.ibo-action-button {
|
||||
float: right;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*/
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-collapsible-section--margin-top: 3rem !default;
|
||||
$ibo-collapsible-section--title--color: $ibo-panel--title--color !default;
|
||||
$ibo-collapsible-section--body--background-color: $ibo-panel--body--background-color !default;
|
||||
$ibo-collapsible-section--highlight--height: 8px !default;
|
||||
@@ -13,18 +12,15 @@ $ibo-collapsible-section--maximize-minimize-button--color: $ibo-panel--collapsib
|
||||
$ibo-collapsible-section--maximize-minimize-button--right: $ibo-panel--collapsible-toggler--margin-right !default;
|
||||
|
||||
|
||||
$ibo-collapsible-section--body--padding-bottom: 16px !default;
|
||||
$ibo-collapsible-section--body--padding-bottom: $ibo-spacing-500 !default;
|
||||
$ibo-collapsible-section--body--padding-top: $ibo-collapsible-section--body--padding-bottom + $ibo-collapsible-section--highlight--height !default;
|
||||
$ibo-collapsible-section--body--padding-x: 16px !default;
|
||||
$ibo-collapsible-section--body--padding-x: $ibo-spacing-500 !default;
|
||||
$ibo-collapsible-section--body--border-radius: $ibo-panel--body--border-radius !default;
|
||||
$ibo-collapsible-section--body--border-size: 1px !default;
|
||||
$ibo-collapsible-section--body--border-color: $ibo-panel--base-border-color !default;
|
||||
|
||||
|
||||
/* Rules */
|
||||
.ibo-collapsible-section {
|
||||
margin: $ibo-collapsible-section--margin-top auto;
|
||||
}
|
||||
|
||||
.ibo-collapsible-section--header {
|
||||
display: flex;
|
||||
@@ -64,6 +60,7 @@ $ibo-collapsible-section--body--border-color: $ibo-panel--base-border-color !def
|
||||
}
|
||||
|
||||
.ibo-collapsible-section--action-button {
|
||||
align-self: center;
|
||||
&.ibo-collapsible-section--maximize-button, &.ibo-collapsible-section--minimize-button {
|
||||
color: $ibo-collapsible-section--maximize-minimize-button--color;
|
||||
margin-right: $ibo-collapsible-section--maximize-minimize-button--right;
|
||||
|
||||
@@ -4,15 +4,18 @@
|
||||
*/
|
||||
|
||||
$ibo-datatable--toolbar--padding-x: 6px !default;
|
||||
$ibo-datatable--toolbar--padding-y: 0 !default;
|
||||
$ibo-datatable--toolbar--text-color: $ibo-color-grey-700 !default;
|
||||
$ibo-datatable--toolbar--padding-y: $ibo-spacing-0 !default;
|
||||
$ibo-datatable--toolbar--text-color: $ibo-color-grey-800 !default;
|
||||
$ibo-datatable--toolbar--elements-spacing: 1rem !default;
|
||||
$ibo-datatable--toolbar--table-spacing: 18px !default;
|
||||
|
||||
$ibo-datatable-header--text-color: $ibo-base-variable--text-color !default;
|
||||
|
||||
$ibo-datatable-panel--table-spacing: 48px !default;
|
||||
$ibo-datatable-panel--body--padding: $ibo-panel--body--padding-top 0px $ibo-panel--body--padding-bottom !default;
|
||||
$ibo-datatable-panel--table-spacing: $ibo-spacing-800 !default;
|
||||
$ibo-datatable-panel--body--padding: $ibo-panel--body--padding-top $ibo-spacing-0 $ibo-panel--body--padding-bottom !default;
|
||||
|
||||
$ibo-datatable--row--background-color--is-hover: $ibo-color-primary-200 !default;
|
||||
$ibo-datatable--row--background-color--is-selected: $ibo-color-primary-300 !default;
|
||||
|
||||
$ibo-datatable--selection-validation-buttons-toolbar--margin-top: 10px !default;
|
||||
$ibo-list-column--max-height: 150px !default;
|
||||
@@ -91,6 +94,7 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
|
||||
&.ibo-is-ascending::after{
|
||||
content: '\f0de';
|
||||
}
|
||||
|
||||
&.ibo-is-none::after{
|
||||
content: '\f0dc';
|
||||
}
|
||||
@@ -100,4 +104,20 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
|
||||
>.selected{
|
||||
background-color: $ibo-fieldsorter--selected--background-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ibo-datatable {
|
||||
tbody > tr {
|
||||
transition: background-color 300ms linear;
|
||||
|
||||
&:hover, &.selected:hover {
|
||||
cursor: pointer;
|
||||
background-color: $ibo-datatable--row--background-color--is-hover;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
background-color: $ibo-datatable--row--background-color--is-selected;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-field-badge--margin: 0 !default;
|
||||
$ibo-field-badge--padding: 4px 10px !default;
|
||||
$ibo-field-badge--margin: $ibo-spacing-0 !default;
|
||||
$ibo-field-badge--padding: $ibo-spacing-200 10px !default;
|
||||
$ibo-field-badge--border-radius: $ibo-border-radius-300 !default;
|
||||
|
||||
$ibo-field-badge--label--spacing-x: 0.5rem !default;
|
||||
|
||||
@@ -4,18 +4,17 @@
|
||||
*/
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-field--sibling-spacing: 16px !default;
|
||||
$ibo-field--value--color: $ibo-color-grey-700 !default;
|
||||
$ibo-field--value--color: $ibo-color-grey-800 !default;
|
||||
|
||||
$ibo-field--label--description--content: "?" !default;
|
||||
$ibo-field--label--description--padding-left: 4px !default;
|
||||
$ibo-field--label--description--padding-left: $ibo-spacing-200 !default;
|
||||
$ibo-field--label--description--color: $ibo-color-grey-600 !default;
|
||||
|
||||
$ibo-field--background-color--is-fullscreen: $ibo-color-white-100 !default;
|
||||
|
||||
$ibo-field--label--width--is-fullscreen: 100% !default;
|
||||
$ibo-field--label--padding-x--is-fullscreen: 8px !default;
|
||||
$ibo-field--label--padding-y--is-fullscreen: 4px !default;
|
||||
$ibo-field--label--padding-x--is-fullscreen: $ibo-spacing-300 !default;
|
||||
$ibo-field--label--padding-y--is-fullscreen: $ibo-spacing-200 !default;
|
||||
$ibo-field--label--background-color--is-fullscreen: $ibo-color-grey-100 !default;
|
||||
$ibo-field--label--border-bottom--is-fullscreen: 1px solid $ibo-color-grey-400 !default;
|
||||
|
||||
@@ -23,20 +22,24 @@ $ibo-field--fullscreen-toggler--size: 20px !default;
|
||||
$ibo-field--fullscreen-toggler--border-radius: $ibo-border-radius-500 !default;
|
||||
$ibo-field--fullscreen-toggler--background-color--on-hover: $ibo-color-white-200 !default;
|
||||
|
||||
$ibo-field--comments--size: 5em !default;
|
||||
$ibo-field--comments--font-size: $ibo-font-size-150 !default;
|
||||
|
||||
$ibo-field--value--scrollbar-track-background-color: $ibo-color-white-200 !default;
|
||||
$ibo-field--value--margin-top--for-large: $ibo-spacing-100 !default;
|
||||
$ibo-field--value--padding-x--is-fullscreen: $ibo-field--label--padding-x--is-fullscreen !default;
|
||||
$ibo-field--value--padding-top--is-fullscreen: $ibo-field--label--padding-y--is-fullscreen + 32px !default;
|
||||
$ibo-field--value--padding-top--is-fullscreen: $ibo-field--label--padding-y--is-fullscreen + $ibo-spacing-700 !default;
|
||||
$ibo-field--value--padding-bottom--is-fullscreen: $ibo-field--label--padding-y--is-fullscreen !default;
|
||||
|
||||
$ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
||||
|
||||
$ibo-field--enable-bulk--padding-y: 2px !default;
|
||||
$ibo-field--enable-bulk--padding-y: $ibo-spacing-100 !default;
|
||||
$ibo-field--enable-bulk--padding-x: 5px !default;
|
||||
$ibo-field--enable-bulk--margin-left: 5px !default;
|
||||
$ibo-field--enable-bulk--height: calc(100% - #{$ibo-field--enable-bulk--padding-x}) !default;
|
||||
$ibo-field--enable-bulk--border-radius: $ibo-border-radius-500 !default;
|
||||
|
||||
$ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
|
||||
$ibo-field--enable-bulk--checkbox--margin-left: $ibo-spacing-300 !default;
|
||||
|
||||
/* SCSS rules */
|
||||
.ibo-field {
|
||||
@@ -44,25 +47,26 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
|
||||
|
||||
/* Avoid value to overflow from its container with very long strings (typically URLs) */
|
||||
/* Note: Some types of attribute must be excluding as it can alter their rendering */
|
||||
&:not([data-attribute-type="AttributeBlob"]):not([data-attribute-type="AttributeFile"]):not([data-attribute-type="AttributeImage"]):not([data-attribute-type="AttributeCustomFields"]):not(.ibo-input-file-select--container) {
|
||||
/* We need the rule to apply for the class and all its descendants, hence the "&, & *" */
|
||||
&:not(
|
||||
[data-attribute-type="AttributeBlob"],
|
||||
[data-attribute-type="AttributeFile"],
|
||||
[data-attribute-type="AttributeImage"],
|
||||
[data-attribute-type="AttributeCustomFields"],
|
||||
[data-attribute-type="AttributeTagSet"],
|
||||
[data-attribute-type="AttributeEnumSet"],
|
||||
[data-attribute-type="AttributeClassAttCodeSet"],
|
||||
[data-attribute-type="AttributeQueryAttCodeSet"],
|
||||
.ibo-input-file-select--container
|
||||
) {
|
||||
/* We need the rule to apply for the class and all its descendants, hence the "& *" */
|
||||
.ibo-field--value {
|
||||
&, & * {
|
||||
word-break: break-word;
|
||||
white-space: inherit; /* Here we don't put break-spaces as it would put ".ibo-field-small .ibo-field-value" on a new line due to the spaces/indentation of the HTML templates. For now we rather have this rule than diminish the templates readability/maintenability */
|
||||
& * {
|
||||
word-break: break-word;
|
||||
white-space: inherit; /* Here we don't put break-spaces as it would put ".ibo-field-small .ibo-field-value" on a new line due to the spaces/indentation of the HTML templates. For now we rather have this rule than diminish the templates readability/maintenability */
|
||||
white-space: inherit;
|
||||
}
|
||||
}
|
||||
&.ibo-field-large {
|
||||
.ibo-field--value {
|
||||
&, & * {
|
||||
white-space: break-spaces; /* For large fields we don't have the issue stated above */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& ~ .ibo-field {
|
||||
margin-top: $ibo-field--sibling-spacing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +92,10 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.ibo-field--value {
|
||||
margin-top: $ibo-field--value--margin-top--for-large; /* Mostly used to have a clear separation from elements in .ibo-field--comments */
|
||||
}
|
||||
|
||||
/* N°4318 - Visible scrollbar background for large fields overflowing to ease "limits" visualization by the user */
|
||||
.ibo-field--value > * {
|
||||
--ibo-scrollbar--scrollbar-track-background-color: $ibo-field--value--scrollbar-track-background-color;
|
||||
@@ -109,6 +117,11 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
|
||||
|
||||
.ibo-field--value {
|
||||
padding: $ibo-field--value--padding-top--is-fullscreen $ibo-field--value--padding-x--is-fullscreen $ibo-field--value--padding-bottom--is-fullscreen $ibo-field--value--padding-x--is-fullscreen;
|
||||
|
||||
> * {
|
||||
height: initial !important; /* !important is necessary to overload the inline style put by the lib */
|
||||
width: initial !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,6 +157,7 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
|
||||
min-width: 100px;
|
||||
max-width: 145px;
|
||||
width: 30%;
|
||||
word-break: break-word; /* We want labels to wrap if it is very long if it has no spaces */
|
||||
@extend %ibo-font-weight-600;
|
||||
|
||||
> .ibo-has-description {
|
||||
@@ -162,29 +176,42 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
|
||||
width: 20em;
|
||||
}
|
||||
.ibo-field--value {
|
||||
display: table;
|
||||
width: 100%;
|
||||
color: $ibo-field--value--color;
|
||||
|
||||
/* Hack to force a table to fit its container without overflow (see N°2127) */
|
||||
/* Note that along with with, we now display a "expand" icon on large fields so we can have a better view of its content */
|
||||
.HTML {
|
||||
@extend .ibo-vendors-ckeditor--display-content;
|
||||
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-field--label > .ibo-field--comments {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
/* For log fields in the bulk operation screens */
|
||||
.ibo-fieldset-legend > .ibo-field--comments {
|
||||
padding-bottom: $ibo-field--value--margin-top--for-large;
|
||||
font-size: $ibo-field--comments--font-size;
|
||||
}
|
||||
|
||||
.ibo-field--comments {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
width: $ibo-field--comments--size; /* Ensure that inputs have all the same width (this is due to the of display table / table-cell) */
|
||||
|
||||
> input[type="checkbox"] {
|
||||
margin-left: 5px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
> .multi_values, > .mono_value, > .ibo-field--comments--synchro{
|
||||
> .multi_values, > .mono_value, > .ibo-field--comments--synchro {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
@@ -223,6 +250,3 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
|
||||
margin-left: $ibo-field--enable-bulk--checkbox--margin-left;
|
||||
}
|
||||
|
||||
.form_field ~ .form_field {
|
||||
margin-top: $ibo-field--sibling-spacing;
|
||||
}
|
||||
@@ -3,11 +3,11 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-fieldset--sibling-spacing: 48px !default;
|
||||
$ibo-fieldset--sibling-spacing: $ibo-spacing-800 !default;
|
||||
|
||||
$ibo-fieldset--legend--width: 100% !default;
|
||||
$ibo-fieldset--legend--margin-bottom: 16px !default;
|
||||
$ibo-fieldset--legend--padding-bottom: 4px !default;
|
||||
$ibo-fieldset--legend--margin-bottom: $ibo-spacing-500 !default;
|
||||
$ibo-fieldset--legend--padding-bottom: $ibo-spacing-200 !default;
|
||||
$ibo-fieldset--legend--border-bottom-size: 2px !default;
|
||||
$ibo-fieldset--legend--border-bottom-color: $ibo-color-grey-500 !default;
|
||||
$ibo-fieldset--legend--border-bottom-style: solid !default;
|
||||
@@ -18,6 +18,14 @@ $ibo-fieldset--legend--border-bottom-style: solid !default;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-column > .ibo-fieldset {
|
||||
// display: contents;
|
||||
}
|
||||
|
||||
.ibo-multi-column ~ .ibo-fieldset {
|
||||
margin-top: $ibo-fieldset--sibling-spacing;
|
||||
}
|
||||
|
||||
.ibo-fieldset-legend {
|
||||
width: $ibo-fieldset--legend--width;
|
||||
margin-bottom: $ibo-fieldset--legend--margin-bottom;
|
||||
|
||||
@@ -6,12 +6,16 @@
|
||||
/* SCSS variables */
|
||||
$ibo-global-search--head--background-color: $ibo-color-white-100 !default;
|
||||
|
||||
$ibo-global-search--icon-padding-x: 16px !default;
|
||||
$ibo-global-search--icon-padding-y: 0 !default;
|
||||
$ibo-global-search--icon-padding-x: $ibo-spacing-500 !default;
|
||||
$ibo-global-search--icon-padding-y: $ibo-spacing-0 !default;
|
||||
$ibo-global-search--icon--color: $ibo-color-primary-600 !default;
|
||||
$ibo-global-search--icon--color--on-hover: $ibo-color-primary-700 !default;
|
||||
$ibo-global-search--icon--color--on-active: $ibo-color-primary-800 !default;
|
||||
|
||||
$ibo-global-search--input--padding: 0 !default;
|
||||
$ibo-global-search--input--padding-x--is-opened: 8px !default;
|
||||
$ibo-global-search--input--padding-y--is-opened: 8px !default;
|
||||
|
||||
$ibo-global-search--input--padding: $ibo-spacing-0 !default;
|
||||
$ibo-global-search--input--padding-x--is-opened: $ibo-spacing-300 !default;
|
||||
$ibo-global-search--input--padding-y--is-opened: $ibo-spacing-300 !default;
|
||||
$ibo-global-search--input--width: 0 !default;
|
||||
$ibo-global-search--input--width--is-opened: 245px !default;
|
||||
$ibo-global-search--input--text-color: $ibo-color-grey-800 !default;
|
||||
@@ -19,30 +23,30 @@ $ibo-global-search--input--placeholder-color: $ibo-color-grey-600 !default;
|
||||
|
||||
$ibo-global-search--drawer--max-height: 300px !default;
|
||||
$ibo-global-search--drawer--padding-x: $ibo-global-search--icon-padding-x !default;
|
||||
$ibo-global-search--drawer--padding-y: 16px !default;
|
||||
$ibo-global-search--drawer--top: -1 * ($ibo-global-search--drawer--max-height) !default;
|
||||
$ibo-global-search--drawer--padding-y: $ibo-spacing-500 !default;
|
||||
$ibo-global-search--drawer--top: -1 * ($ibo-global-search--drawer--max-height) - 10 !default; /* 10px of margin to avoid to be slightly visible when closed and has a lot of history */
|
||||
$ibo-global-search--drawer--top--is-opened: 100% !default;
|
||||
$ibo-global-search--drawer--background-color: $ibo-color-white-100 !default;
|
||||
|
||||
$ibo-global-search--compartment-title--margin-bottom: 8px !default;
|
||||
$ibo-global-search--compartment-title--padding-left: 32px !default;
|
||||
$ibo-global-search--compartment-title--text-color: $ibo-color-grey-600 !default;
|
||||
$ibo-global-search--compartment-title--line-spacing: 8px !default;
|
||||
$ibo-global-search--compartment-title--margin-bottom: $ibo-spacing-300 !default;
|
||||
$ibo-global-search--compartment-title--padding-left: $ibo-spacing-700 !default;
|
||||
$ibo-global-search--compartment-title--text-color: $ibo-color-grey-800 !default;
|
||||
$ibo-global-search--compartment-title--line-spacing: $ibo-spacing-300 !default;
|
||||
|
||||
$ibo-global-search--compartment-content--text-color: $ibo-color-grey-900 !default;
|
||||
|
||||
$ibo-global-search--compartment-element--margin-bottom: 8px !default;
|
||||
$ibo-global-search--compartment-element--margin-bottom: $ibo-spacing-300 !default;
|
||||
|
||||
$ibo-global-search--compartment-element-image--margin-right: 8px !default;
|
||||
$ibo-global-search--compartment-element-image--margin-right: $ibo-spacing-300 !default;
|
||||
$ibo-global-search--compartment-element-image--width: 20px !default;
|
||||
|
||||
$ibo-global-search--compartment--placeholder-image--margin-top: 24px !default;
|
||||
$ibo-global-search--compartment--placeholder-image--margin-bottom: 16px !default;
|
||||
$ibo-global-search--compartment--placeholder-image--margin-top: $ibo-spacing-600 !default;
|
||||
$ibo-global-search--compartment--placeholder-image--margin-bottom: $ibo-spacing-500 !default;
|
||||
$ibo-global-search--compartment--placeholder-image--margin-y: auto !default;
|
||||
$ibo-global-search--compartment--placeholder-image--width: 66% !default;
|
||||
|
||||
$ibo-global-search--compartment--placeholder-hint--padding-x: 8px !default;
|
||||
$ibo-global-search--compartment--placeholder-hint--padding-y: 0 !default;
|
||||
$ibo-global-search--compartment--placeholder-hint--padding-x: $ibo-spacing-300 !default;
|
||||
$ibo-global-search--compartment--placeholder-hint--padding-y: $ibo-spacing-0 !default;
|
||||
$ibo-global-search--compartment--placeholder-hint--text-color: $ibo-color-grey-700 !default;
|
||||
|
||||
/* Animations*/
|
||||
@@ -81,9 +85,17 @@ $ibo-global-search--compartment--placeholder-hint--text-color: $ibo-color-grey-7
|
||||
background-color: $ibo-global-search--head--background-color;
|
||||
}
|
||||
.ibo-global-search--icon{
|
||||
color: $ibo-global-search--icon--color;
|
||||
align-self: center;
|
||||
padding: $ibo-global-search--icon-padding-y $ibo-global-search--icon-padding-x;
|
||||
@extend %ibo-font-ral-nor-400;
|
||||
|
||||
&:hover{
|
||||
color: $ibo-global-search--icon--color--on-hover;
|
||||
}
|
||||
&:active{
|
||||
color: $ibo-global-search--icon--color--on-active;
|
||||
}
|
||||
}
|
||||
.ibo-global-search--input{
|
||||
padding: $ibo-global-search--input--padding;
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
*/
|
||||
|
||||
$ibo-medallion-icon--padding-y: 13px !default;
|
||||
$ibo-medallion-icon--padding-x: 0 !default;
|
||||
$ibo-medallion-icon--padding-x: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-medallion-icon--image--diameter: 48px !default;
|
||||
$ibo-medallion-icon--image--padding: 2px !default;
|
||||
$ibo-medallion-icon--image--padding: $ibo-spacing-100 !default;
|
||||
$ibo-medallion-icon--image--border-radius: $ibo-border-radius-full !default;
|
||||
$ibo-medallion-icon--image--background-color: $ibo-color-blue-200 !default;
|
||||
|
||||
$ibo-medallion-icon--description--padding-left: 8px !default;
|
||||
$ibo-medallion-icon--description--padding-left: $ibo-spacing-300 !default;
|
||||
|
||||
.ibo-medallion-icon{
|
||||
display: flex;
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
$ibo-navigation-menu--notifications-menu--min-width: 250px;
|
||||
|
||||
$ibo-navigation-menu--notifications--item--image--margin-x: 6px !default;
|
||||
$ibo-navigation-menu--notifications--item--image--margin-y: 0 !default;
|
||||
$ibo-navigation-menu--notifications--item--image--margin-y: $ibo-spacing-0 !default;
|
||||
$ibo-navigation-menu--notifications--item--image--max-width: 20px !default;
|
||||
$ibo-navigation-menu--notifications--item--image--max-height: 20px !default;
|
||||
$ibo-navigation-menu--notifications--item--image--border-radius: $ibo-border-radius-full !default;
|
||||
|
||||
$ibo-navigation-menu--notifications--item--bottom-text--margin-left: auto !default;
|
||||
|
||||
$ibo-navigation-menu--notifications--item--content--padding-y: 0 !default;
|
||||
$ibo-navigation-menu--notifications--item--content--padding-y: $ibo-spacing-0 !default;
|
||||
$ibo-navigation-menu--notifications--item--content--padding-x: 14px !default;
|
||||
$ibo-navigation-menu--notifications--item--content--img--max-height: 100px !default;
|
||||
$ibo-navigation-menu--notifications--item--content--img--padding: 5px !default;
|
||||
@@ -23,11 +23,11 @@ $ibo-navigation-menu--notifications--item--new-message-indicator--width: 10px !d
|
||||
$ibo-navigation-menu--notifications--item--new-message-indicator--height: 10px !default;
|
||||
$ibo-navigation-menu--notifications--item--new-message-indicator--background-color: $ibo-color-blue-500 !default;
|
||||
$ibo-navigation-menu--notifications--item--new-message-indicator--border-radius: $ibo-border-radius-full !default;
|
||||
$ibo-navigation-menu--notifications--item--new-message-indicator--margin-top: 4px !default;
|
||||
$ibo-navigation-menu--notifications--item--new-message-indicator--margin-top: $ibo-spacing-200 !default;
|
||||
|
||||
$ibo-navigation-menu--notifications-show-all-multiple--ibo-popover-menu--indicator--margin-right: 15px !default;
|
||||
|
||||
$ibo-navigation-menu--notifications-dismiss-all--icon--margin: 0 10px 0 0 !default;
|
||||
$ibo-navigation-menu--notifications-dismiss-all--icon--margin: $ibo-spacing-0 10px $ibo-spacing-0 $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-popover-menu--item--no-message--image--svg--width : 100% !default;
|
||||
$ibo-popover-menu--item--no-message--image--svg--height : inherit !default;
|
||||
|
||||
@@ -39,32 +39,29 @@ $ibo-panel-colors: (
|
||||
) !default;
|
||||
|
||||
/* - Specific variables for the block */
|
||||
$ibo-panel--spacing-top: 24px !default;
|
||||
|
||||
$ibo-panel--highlight--width: 100% !default;
|
||||
$ibo-panel--highlight--height: 8px !default;
|
||||
$ibo-panel--highlight--background-color: 8px !default;
|
||||
$ibo-panel--highlight--border-radius: $ibo-border-radius-400 $ibo-border-radius-400 0 0 !default;
|
||||
$ibo-panel--highlight--padding-bottom: $ibo-panel--highlight--height !default;
|
||||
|
||||
$ibo-panel--body--z-index: 1 !default;
|
||||
$ibo-panel--body--background-color: $ibo-color-white-100 !default;
|
||||
$ibo-panel--body--padding-bottom: 24px !default;
|
||||
$ibo-panel--body--padding-bottom: $ibo-spacing-600 !default;
|
||||
$ibo-panel--body--padding-top: $ibo-panel--body--padding-bottom + $ibo-panel--highlight--height !default;
|
||||
$ibo-panel--body--padding-x: 16px !default;
|
||||
$ibo-panel--body--padding-x: $ibo-spacing-500 !default;
|
||||
$ibo-panel--body--border-radius: $ibo-border-radius-500 !default;
|
||||
$ibo-panel--body--border: $ibo-panel--base-border !default;
|
||||
|
||||
$ibo-panel--header--z-index: $ibo-panel--body--z-index + 1 !default; /* Should always be above the body */
|
||||
$ibo-panel--header--margin-bottom: 4px !default;
|
||||
$ibo-panel--header--margin-bottom: $ibo-spacing-200 !default;
|
||||
$ibo-panel--header--background-color--is-sticking: $ibo-color-grey-100 !default;
|
||||
$ibo-panel--header--border--is-sticking: $ibo-panel--base-border !default;
|
||||
$ibo-panel--header--padding-y--is-sticking: 4px !default;
|
||||
$ibo-panel--header--padding-y--is-sticking: $ibo-spacing-200 !default;
|
||||
|
||||
$ibo-panel--icon--size: 48px !default;
|
||||
$ibo-panel--icon--spacing: 16px !default;
|
||||
$ibo-panel--icon--spacing: $ibo-spacing-500 !default;
|
||||
$ibo-panel--icon--size-as-medallion: 72px !default;
|
||||
$ibo-panel--icon--spacing--as-medallion: 16px !default;
|
||||
$ibo-panel--icon--spacing--as-medallion: $ibo-spacing-500 !default;
|
||||
$ibo-panel--icon--bottom--as-medallion: -24px !default;
|
||||
$ibo-panel--icon--background-color--as-medallion: $ibo-color-grey-100 !default;
|
||||
$ibo-panel--icon--border--as-medallion: 2px solid $ibo-color-blue-grey-300 !default;
|
||||
@@ -84,14 +81,12 @@ $ibo-panel--title--color: $ibo-color-grey-900 !default;
|
||||
$ibo-panel--subtitle--font-size--is-sticking: $ibo-font-size-100 !default;
|
||||
$ibo-panel--subtitle--color: $ibo-color-grey-800 !default;
|
||||
|
||||
$ibo-panel--collapsible-toggler--margin-right: 8px !default;
|
||||
$ibo-panel--collapsible-toggler--margin-right: $ibo-spacing-300 !default;
|
||||
$ibo-panel--collapsible-toggler--font-size: $ibo-font-size-250 !default;
|
||||
$ibo-panel--collapsible-toggler--color: $ibo-color-grey-700 !default;
|
||||
|
||||
/* Rules */
|
||||
.ibo-panel + .ibo-panel {
|
||||
margin-top: $ibo-panel--spacing-top;
|
||||
}
|
||||
|
||||
|
||||
.ibo-panel {
|
||||
--ibo-main-color: map-get($ibo-panel-colors, 'neutral'); /* --ibo-main-color is to allow overload from custom dynamic value from the DM. The overload will be done through an additional CSS class of a particular DM class or DM attribute */
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-pill--margin: 4px 8px !default;
|
||||
$ibo-pill--margin-y: $ibo-spacing-200 !default;
|
||||
$ibo-pill--padding: 6px 10px !default;
|
||||
$ibo-pill--max-width: 240px !default;
|
||||
$ibo-pill--border-radius: $ibo-border-radius-300 !default;
|
||||
@@ -84,7 +84,8 @@ $ibo-pill-states-colors: (
|
||||
|
||||
@extend %ibo-fully-centered-content;
|
||||
max-width: $ibo-pill--max-width;
|
||||
margin: $ibo-pill--margin;
|
||||
margin-top: $ibo-pill--margin-y;
|
||||
margin-bottom: $ibo-pill--margin-y;
|
||||
padding: $ibo-pill--padding;
|
||||
border-radius: $ibo-pill--border-radius;
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-prop--apply--padding-left: 12px !default;
|
||||
$ibo-prop--cancel--padding-left: 7px !default;
|
||||
$ibo-prop--apply--padding-left: $ibo-spacing-400 !default;
|
||||
$ibo-prop--cancel--padding-left: $ibo-spacing-300 !default;
|
||||
|
||||
$ibo-prop--apply-cancel--span--height: 28px !default;
|
||||
$ibo-prop--apply-cancel--span--width: 32px !default;
|
||||
@@ -42,6 +42,9 @@ $ibo-prop--apply--error--color: $ibo-color-grey-800 !default;
|
||||
width: $ibo-prop--apply-cancel--span--width;
|
||||
text-align: center;
|
||||
> div{
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
/* SCSS variables */
|
||||
$ibo-quick-create--head--background-color: $ibo-color-white-100 !default;
|
||||
|
||||
$ibo-quick-create--icon-padding-x: 16px !default;
|
||||
$ibo-quick-create--icon-padding-y: 0 !default;
|
||||
$ibo-quick-create--icon-padding-x: $ibo-spacing-500 !default;
|
||||
$ibo-quick-create--icon-padding-y: $ibo-spacing-0 !default;
|
||||
$ibo-quick-create--icon--color: $ibo-color-primary-600 !default;
|
||||
$ibo-quick-create--icon--color--on-hover: $ibo-color-primary-700 !default;
|
||||
$ibo-quick-create--icon--color--on-active: $ibo-color-primary-800 !default;
|
||||
|
||||
$ibo-quick-create--input--padding: 0 default;
|
||||
$ibo-quick-create--input--padding-x--is-opened: 8px !default;
|
||||
$ibo-quick-create--input--padding-y--is-opened: 8px !default;
|
||||
$ibo-quick-create--input--padding-x--is-opened: $ibo-spacing-300 !default;
|
||||
$ibo-quick-create--input--padding-y--is-opened: $ibo-spacing-300 !default;
|
||||
$ibo-quick-create--input--width: 0 !default;
|
||||
$ibo-quick-create--input--width--is-opened: 245px !default;
|
||||
$ibo-quick-create--input--text-color: $ibo-color-grey-800 !default;
|
||||
@@ -23,37 +26,37 @@ $ibo-quick-create--input-options--border-radius: 0 !default;
|
||||
|
||||
$ibo-quick-create--drawer--max-height: 300px !default;
|
||||
$ibo-quick-create--drawer--padding-x: $ibo-quick-create--icon-padding-x !default;
|
||||
$ibo-quick-create--drawer--padding-y: 16px !default;
|
||||
$ibo-quick-create--drawer--top: -1 * ($ibo-quick-create--drawer--max-height) !default;
|
||||
$ibo-quick-create--drawer--padding-y: $ibo-spacing-500 !default;
|
||||
$ibo-quick-create--drawer--top: -1 * ($ibo-quick-create--drawer--max-height) - 10 !default; /* 10px of margin to avoid to be slightly visible when closed and has a lot of history */
|
||||
$ibo-quick-create--drawer--top--is-opened: 100% !default;
|
||||
$ibo-quick-create--drawer--background-color: $ibo-color-white-100 !default;
|
||||
|
||||
$ibo-quick-create--compartment-title--margin-top: 8px !default;
|
||||
$ibo-quick-create--compartment-title--margin-top: $ibo-spacing-300 !default;
|
||||
$ibo-quick-create--compartment-title--margin-bottom: $ibo-quick-create--compartment-title--margin-top !default;
|
||||
$ibo-quick-create--compartment-title--padding-left: 32px !default;
|
||||
$ibo-quick-create--compartment-title--text-color: $ibo-color-grey-600 !default;
|
||||
$ibo-quick-create--compartment-title--line-spacing: 8px !default;
|
||||
$ibo-quick-create--compartment-title--padding-left: $ibo-spacing-700 !default;
|
||||
$ibo-quick-create--compartment-title--text-color: $ibo-color-grey-800 !default;
|
||||
$ibo-quick-create--compartment-title--line-spacing: $ibo-spacing-300 !default;
|
||||
|
||||
$ibo-quick-create--compartment-content--text-color: $ibo-color-grey-900 !default;
|
||||
|
||||
$ibo-quick-create--compartment-element--padding-x: 8px !default;
|
||||
$ibo-quick-create--compartment-element--padding-y: 4px !default;
|
||||
$ibo-quick-create--compartment-element--padding-x: $ibo-spacing-300 !default;
|
||||
$ibo-quick-create--compartment-element--padding-y: $ibo-spacing-200 !default;
|
||||
$ibo-quick-create--compartment-element--margin-x: -1 * $ibo-quick-create--compartment-element--padding-x !default;
|
||||
$ibo-quick-create--compartment-element--background-color--is-active: $ibo-color-grey-200 !default;
|
||||
$ibo-quick-create--compartment-element--border-radius--is-active: $ibo-border-radius-300 !default;
|
||||
|
||||
$ibo-quick-create--compartment-element-image--margin-right: 8px !default;
|
||||
$ibo-quick-create--compartment-element-image--margin-right: $ibo-spacing-300 !default;
|
||||
$ibo-quick-create--compartment-element-image--width: 20px !default;
|
||||
|
||||
$ibo-quick-create--compartment-results--container--width: 100% !important !default;
|
||||
|
||||
$ibo-quick-create--compartment--placeholder-image--margin-top: 24px !default;
|
||||
$ibo-quick-create--compartment--placeholder-image--margin-bottom: 16px !default;
|
||||
$ibo-quick-create--compartment--placeholder-image--margin-top: $ibo-spacing-600 !default;
|
||||
$ibo-quick-create--compartment--placeholder-image--margin-bottom: $ibo-spacing-500 !default;
|
||||
$ibo-quick-create--compartment--placeholder-image--margin-x: auto !default;
|
||||
$ibo-quick-create--compartment--placeholder-image--width: 66% !default;
|
||||
|
||||
$ibo-quick-create--compartment--placeholder-hint--padding-x: 8px !default;
|
||||
$ibo-quick-create--compartment--placeholder-hint--padding-y: 0 !default;
|
||||
$ibo-quick-create--compartment--placeholder-hint--padding-x: $ibo-spacing-300 !default;
|
||||
$ibo-quick-create--compartment--placeholder-hint--padding-y: $ibo-spacing-0 !default;
|
||||
$ibo-quick-create--compartment--placeholder-hint--text-color: $ibo-color-grey-700 !default;
|
||||
|
||||
/* Animations*/
|
||||
@@ -91,9 +94,17 @@ $ibo-quick-create--compartment--placeholder-hint--text-color: $ibo-color-grey-70
|
||||
background-color: $ibo-quick-create--head--background-color;
|
||||
}
|
||||
.ibo-quick-create--icon{
|
||||
color: $ibo-quick-create--icon--color;
|
||||
align-self: center;
|
||||
padding: $ibo-quick-create--icon-padding-y $ibo-quick-create--icon-padding-x;
|
||||
@extend %ibo-font-ral-nor-400;
|
||||
|
||||
&:hover{
|
||||
color: $ibo-quick-create--icon--color--on-hover;
|
||||
}
|
||||
&:active{
|
||||
color: $ibo-quick-create--icon--color--on-active;
|
||||
}
|
||||
}
|
||||
.ibo-quick-create--input{
|
||||
width: $ibo-quick-create--input--width;
|
||||
|
||||
@@ -9,6 +9,7 @@ $ibo-search-form-panel--body--padding-top: 18px !default;
|
||||
$ibo-search-form-panel--body--padding-bottom: 10px !default;
|
||||
$ibo-search-form-panel--body--padding-x: 14px !default;
|
||||
|
||||
$ibo-search-form-panel--criteria--margin-bottom: 5px !default;
|
||||
$ibo-search-form-panel--criteria--color: $ibo-color-grey-900 !default;
|
||||
$ibo-search-form-panel--criteria--background-color: $ibo-color-white-200 !default;
|
||||
$ibo-search-form-panel--criteria--border-color: $ibo-color-grey-300 !default;
|
||||
@@ -19,10 +20,15 @@ $ibo-search-form-panel--more-criteria--background-color: $ibo-color-white-100 !d
|
||||
$ibo-search-form-panel--more-criteria--icon--color: $ibo-color-primary-600 !default;
|
||||
$ibo-search-form-panel--more-criteria--border-color: $ibo-search-form-panel--criteria--border-color !default;
|
||||
|
||||
$ibo-search-form-panel--items--hover--color: $ibo-color-grey-200 !default;
|
||||
|
||||
$ibo-search-form-panel--multiple-choice--hover--color: $ibo-color-grey-200 !default;
|
||||
|
||||
$ibo-search-form-panel--misc-button--background-color: $ibo-search-form-panel--more-criteria--background-color !default;
|
||||
$ibo-search-form-panel--misc-button--icon--color: $ibo-search-form-panel--more-criteria--icon--color !default;
|
||||
|
||||
$ibo-search-results-area--z-index: $ibo-search-form-panel--z-index - 2 !default; /* Minus 2 because the criteria expands between the search form panel and the results area */
|
||||
$ibo-search-results-area--margin-bottom: 300px !default; /* Arbitrary value, must be greater than the position (y) of the first result line in non sticking state, should be discussed to find a clever way to adjust it in both search page and search modals (linkedsets) */
|
||||
|
||||
$ibo-search-results-area--datatable-toolbar--background-color--is-sticking: $ibo-panel--body--background-color !default;
|
||||
$ibo-search-results-area--datatable-toolbar--border--is-sticking: $ibo-panel--body--border !default;
|
||||
@@ -146,13 +152,13 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
|
||||
.sf_message {
|
||||
display: none;
|
||||
margin: 8px 8px 0px 8px;
|
||||
margin: $ibo-spacing-300 $ibo-spacing-300 $ibo-spacing-0 $ibo-spacing-300;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.sf_criterion_area {
|
||||
/*display: none;*/
|
||||
padding: 8px 8px 3px 8px; /* padding-bottom must equals to padding-top - .search_form_criteria:margin-bottom */
|
||||
padding: $ibo-spacing-300 $ibo-spacing-300 3px $ibo-spacing-300; /* padding-bottom must equals to padding-top - .search_form_criteria:margin-bottom */
|
||||
|
||||
.sf_criterion_row {
|
||||
|
||||
@@ -197,11 +203,12 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px; /* Bottom spacing (6px) for next criteria line is actually splitted between top and bottom margin in order to center the criteria within the container. The cumulated margins fo the lines will do the trick. */
|
||||
vertical-align: top;
|
||||
|
||||
&.opened {
|
||||
margin-bottom: 0px; /* To compensate the .sfc/.sfm_header:padding-bottom: 13px */
|
||||
margin-bottom: $ibo-spacing-0; /* To compensate the .sfc/.sfm_header:padding-bottom: 13px */
|
||||
|
||||
.sfc_header,
|
||||
.sfm_header {
|
||||
@@ -211,7 +218,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
}
|
||||
|
||||
> * {
|
||||
padding: 7px 8px;
|
||||
padding: 7px $ibo-spacing-300;
|
||||
vertical-align: top;
|
||||
border: solid 1px $ibo-search-form-panel--more-criteria--border-color;
|
||||
border-radius: $ibo-border-radius-300;
|
||||
@@ -362,7 +369,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
|
||||
.sfc_op_radio {
|
||||
width: 12px;
|
||||
margin: 0px;
|
||||
margin: $ibo-spacing-0;
|
||||
margin-right: 7px;
|
||||
}
|
||||
|
||||
@@ -380,8 +387,8 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
|
||||
.sfc_opc_multichoices {
|
||||
label > input {
|
||||
vertical-align: middle;
|
||||
margin-left: 0px;
|
||||
vertical-align: text-top;
|
||||
margin-left: $ibo-spacing-0;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
@@ -392,7 +399,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
.sfc_opc_mc_items_wrapper {
|
||||
max-height: 415px; /* Must be less than .sfc_form_group:max-height - .sfc_opc_mc_toggler:height - .sfc_opc_mc_filter:height */
|
||||
overflow-y: auto;
|
||||
margin: 0px -8px; /* Compensate .sfc_opc_multichoices side padding so the hover style can take the full with */
|
||||
margin: $ibo-spacing-0 -8px; /* Compensate .sfc_opc_multichoices side padding so the hover style can take the full with */
|
||||
|
||||
.sfc_opc_mc_items {
|
||||
.sfc_opc_mc_items_list {
|
||||
@@ -419,10 +426,10 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
}
|
||||
|
||||
.sfc_opc_mc_item {
|
||||
padding: 4px 8px; /* Putting back the padding remove by .sfc_opc_mc_items */
|
||||
padding: $ibo-spacing-200 $ibo-spacing-300; /* Putting back the padding remove by .sfc_opc_mc_items */
|
||||
|
||||
&:hover {
|
||||
background-color: $ibo-color-grey-200;
|
||||
background-color: $ibo-search-form-panel--multiple-choice--hover--color;
|
||||
}
|
||||
|
||||
label {
|
||||
@@ -503,7 +510,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
margin-bottom: 3px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 0px;
|
||||
margin-bottom: $ibo-spacing-0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,8 +684,8 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
min-width: 200px; /* To avoid element going to thin on filter, not very slick */
|
||||
|
||||
.sfm_lists {
|
||||
margin: 0px -8px;
|
||||
padding: 0px 8px;
|
||||
margin: $ibo-spacing-0 -8px;
|
||||
padding: $ibo-spacing-0 8px;
|
||||
max-height: 400px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
@@ -686,7 +693,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
.sfl_items {
|
||||
> li {
|
||||
&:hover {
|
||||
background-color: $ibo-color-grey-200;
|
||||
background-color: $ibo-search-form-panel--items--hover--color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -702,7 +709,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
font-size: 11px;
|
||||
|
||||
&:last-of-type {
|
||||
margin-right: 0px;
|
||||
margin-right: $ibo-spacing-0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -735,17 +742,17 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
}
|
||||
|
||||
.sfl_items {
|
||||
margin: 5px -8px 0px -8px;
|
||||
padding: 0px;
|
||||
margin: 5px -8px $ibo-spacing-0 -8px;
|
||||
padding: $ibo-spacing-0;
|
||||
text-align: left;
|
||||
|
||||
> li {
|
||||
padding: 4px 8px;
|
||||
padding: $ibo-spacing-200 $ibo-spacing-300;
|
||||
list-style: none;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background-color: $ibo-color-white-100;
|
||||
background-color: $ibo-search-form-panel--items--hover--color;
|
||||
}
|
||||
|
||||
&.sfl_i_placeholder {
|
||||
@@ -762,8 +769,8 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
}
|
||||
|
||||
> input[type="checkbox"] {
|
||||
margin-left: 0px;
|
||||
margin-right: 8px;
|
||||
margin-left: $ibo-spacing-0;
|
||||
margin-right: $ibo-spacing-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -869,9 +876,6 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
#ibo-main-content .search_form_handler .sf_criterion_area{
|
||||
padding: 0;
|
||||
}
|
||||
.ibo-search-form{
|
||||
padding-top: $ibo-panel--spacing-top ;
|
||||
}
|
||||
|
||||
.sfm_tg_title{
|
||||
display: none;
|
||||
@@ -881,6 +885,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
}
|
||||
.sf_results_area {
|
||||
z-index: $ibo-search-results-area--z-index;
|
||||
margin-bottom: $ibo-search-results-area--margin-bottom; /* This is mostly to avoid glitches when the result list's bottom limit is just around the window bottom limit and the user scrolls down (boucing effect, see N°4427) */
|
||||
}
|
||||
|
||||
/***********************/
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
$ibo-title--text-color: $ibo-color-grey-900 !default;
|
||||
$ibo-title--padding-y: 12px !default;
|
||||
$ibo-title--padding-x: 0 !default;
|
||||
$ibo-title--padding-y: $ibo-spacing-400 !default;
|
||||
$ibo-title--padding-x: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-title--icon--size: 90px !default;
|
||||
|
||||
@@ -55,7 +55,7 @@ $ibo-title--icon-background--size--must-zoomout: 66.67% !default;
|
||||
}
|
||||
|
||||
.ibo-title--text {
|
||||
@extend %ibo-font-ral-sembol-300;
|
||||
@extend %ibo-font-ral-med-300;
|
||||
}
|
||||
|
||||
.ibo-title--subtitle {
|
||||
@@ -83,7 +83,7 @@ $ibo-title--icon-background--size--must-zoomout: 66.67% !default;
|
||||
padding-bottom:1em;
|
||||
}
|
||||
.ibo-title-separator{
|
||||
border-radius: 5px 5px 0px 0px;
|
||||
border-radius: 5px 5px $ibo-spacing-0 $ibo-spacing-0;
|
||||
border-color:$ibo-color-blue-600;
|
||||
color:$ibo-color-blue-600;
|
||||
background-color:$ibo-color-blue-600;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*/
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-dashlet-badge--padding-x: 16px !default;
|
||||
$ibo-dashlet-badge--padding-y: 16px !default;
|
||||
$ibo-dashlet-badge--padding-x: $ibo-spacing-500 !default;
|
||||
$ibo-dashlet-badge--padding-y: $ibo-spacing-500 !default;
|
||||
$ibo-dashlet-badge--min-width: 200px !default;
|
||||
$ibo-dashlet-badge--max-width: 350px !default;
|
||||
$ibo-dashlet-badge--background-color: $ibo-color-white-100 !default;
|
||||
@@ -13,12 +13,12 @@ $ibo-dashlet-badge--border: 1px solid $ibo-color-grey-400 !default;
|
||||
$ibo-dashlet-badge--border-radius: $ibo-border-radius-500 !default;
|
||||
|
||||
$ibo-dashlet-badge--action-list--text-color: inherit !default;
|
||||
$ibo-dashlet-badge--action-list-count--margin-right: 8px !default;
|
||||
$ibo-dashlet-badge--action-list-count--margin-right: $ibo-spacing-300 !default;
|
||||
|
||||
$ibo-dashlet-badge--icon-container--margin-right: 16px !default;
|
||||
$ibo-dashlet-badge--icon-container--margin-right: $ibo-spacing-500 !default;
|
||||
$ibo-dashlet-badge--icon--size: 48px !default;
|
||||
|
||||
$ibo-dashlet-badge--action-icon--margin-right: 6px !default;
|
||||
$ibo-dashlet-badge--action-icon--margin-right: $ibo-spacing-300 !default;
|
||||
|
||||
/* CSS variables (can be changed directly from the browser) */
|
||||
:root {
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
*/
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-dashlet-header-static--padding-top: 16px !default;
|
||||
$ibo-dashlet-header-static--padding-bottom: 0 !default;
|
||||
$ibo-dashlet-header-static--padding-x: 16px !default;
|
||||
$ibo-dashlet-header-static--padding-top: $ibo-spacing-500 !default;
|
||||
$ibo-dashlet-header-static--padding-bottom: $ibo-spacing-0 !default;
|
||||
$ibo-dashlet-header-static--padding-x: $ibo-spacing-500 !default;
|
||||
|
||||
$ibo-dashlet-header-static--body--margin-left: 48px !default;
|
||||
$ibo-dashlet-header-static--body--margin-left: $ibo-spacing-800 !default;
|
||||
$ibo-dashlet-header-static--body--text-color: $ibo-color-grey-900 !default;
|
||||
|
||||
$ibo-dashlet-header-static--title--margin-right: 8px !default;
|
||||
$ibo-dashlet-header-static--title--margin-right: $ibo-spacing-300 !default;
|
||||
|
||||
$ibo-dashlet-header-static--text--text-color: inherit !default;
|
||||
|
||||
$ibo-dashlet-header-static--icon-container--margin-right: 16px !default;
|
||||
$ibo-dashlet-header-static--icon-container--margin-right: $ibo-spacing-500 !default;
|
||||
$ibo-dashlet-header-static--icon--size: 48px !default;
|
||||
|
||||
$ibo-dashlet-header-static--body--separator--top: 50% !default;
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
/* SCSS variables */
|
||||
$ibo-dashlet--width: 100% !default;
|
||||
$ibo-dashlet--width--is-inline: auto !default;
|
||||
$ibo-dashlet--elements-spacing-x: 24px !default;
|
||||
$ibo-dashlet--elements-spacing-y: 24px !default;
|
||||
$ibo-dashlet--elements-spacing-x: $ibo-spacing-600 !default;
|
||||
$ibo-dashlet--elements-spacing-y: $ibo-spacing-600 !default;
|
||||
|
||||
/* Rules */
|
||||
.ibo-dashlet {
|
||||
|
||||
@@ -9,8 +9,10 @@
|
||||
@import "input-datetime";
|
||||
@import "input-duration";
|
||||
@import "input-image";
|
||||
@import "input-richtext";
|
||||
@import "input-select";
|
||||
@import "input-select-icon";
|
||||
@import "input-string";
|
||||
@import "input-one-way-password";
|
||||
@import "input-set";
|
||||
@import "input-text";
|
||||
|
||||
@@ -7,7 +7,7 @@ $ibo-input-date--width: 100% !default;
|
||||
|
||||
$ibo-input-date--button--margin--left: -20px !default;
|
||||
$ibo-input-date--button--margin--top: 5px !default;
|
||||
$ibo-input-date--button--padding: 0 !default;
|
||||
$ibo-input-date--button--padding: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-input-date--button--background-color: transparent !default;
|
||||
$ibo-input-date--button--color: $ibo-color-grey-800 !default;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user