mirror of
https://github.com/Combodo/iTop.git
synced 2026-03-21 17:04:11 +01:00
Compare commits
322 Commits
3.0.0-beta
...
3.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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'));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -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,
|
||||
)));
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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::HtmlEntities($aValue['label']), 'obsolescence_flag' => $aValue['obsolescence_flag']];
|
||||
if ($aValue['additional_field'] != '') {
|
||||
$aElt['additional_field'] = $aValue['additional_field'];
|
||||
$aElt['additional_field'] = utils::HtmlEntities($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;
|
||||
}
|
||||
@@ -8159,24 +8160,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>';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,80 @@ 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' => '',
|
||||
'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,
|
||||
],
|
||||
'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 +1335,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 +1471,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');
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -86,4 +86,11 @@ a{
|
||||
&:active{
|
||||
color: var(--ibo-hyperlink-color--on-active);
|
||||
}
|
||||
}
|
||||
|
||||
/* Input reset */
|
||||
/* - Standard spacing between label and input */
|
||||
select + label, label + select, label > select,
|
||||
input + label, label + input, label > input {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
@@ -6,8 +6,8 @@
|
||||
@import "dashlet-within-dashboard";
|
||||
@import "add-to-dashboard";
|
||||
@import "caselog-entry-form-within-activity-panel";
|
||||
@import "panel-with-datatable";
|
||||
@import "panel-with-tab-container";
|
||||
@import "datatable-within-panel";
|
||||
@import "tab-container-within-panel";
|
||||
@import "panel-within-main-content";
|
||||
@import "panel-within-modal";
|
||||
@import "object-details-with-tab-container";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,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;
|
||||
}
|
||||
|
||||
@@ -54,22 +54,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 +106,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 +132,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 +159,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 +185,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 +211,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 +268,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,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
$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--text-color: $ibo-color-grey-800 !default;
|
||||
$ibo-datatable--toolbar--elements-spacing: 1rem !default;
|
||||
$ibo-datatable--toolbar--table-spacing: 18px !default;
|
||||
|
||||
@@ -14,6 +14,9 @@ $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--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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/* 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;
|
||||
@@ -23,7 +23,11 @@ $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: 2px !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-bottom--is-fullscreen: $ibo-field--label--padding-y--is-fullscreen !default;
|
||||
@@ -44,19 +48,24 @@ $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 */
|
||||
}
|
||||
}
|
||||
&.ibo-field-large {
|
||||
.ibo-field--value {
|
||||
&, & * {
|
||||
white-space: break-spaces; /* For large fields we don't have the issue stated above */
|
||||
}
|
||||
white-space: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,6 +97,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 +122,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 +162,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 +181,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 +255,6 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
|
||||
margin-left: $ibo-field--enable-bulk--checkbox--margin-left;
|
||||
}
|
||||
|
||||
.form_field ~ .form_field {
|
||||
.form_field ~ .form_field {
|
||||
margin-top: $ibo-field--sibling-spacing;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -8,6 +8,10 @@ $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--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;
|
||||
@@ -20,13 +24,13 @@ $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--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--text-color: $ibo-color-grey-800 !default;
|
||||
$ibo-global-search--compartment-title--line-spacing: 8px !default;
|
||||
|
||||
$ibo-global-search--compartment-content--text-color: $ibo-color-grey-900 !default;
|
||||
@@ -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;
|
||||
|
||||
@@ -8,6 +8,9 @@ $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--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;
|
||||
@@ -24,14 +27,14 @@ $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--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-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--text-color: $ibo-color-grey-800 !default;
|
||||
$ibo-quick-create--compartment-title--line-spacing: 8px !default;
|
||||
|
||||
$ibo-quick-create--compartment-content--text-color: $ibo-color-grey-900 !default;
|
||||
@@ -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;
|
||||
@@ -197,7 +203,8 @@ $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 {
|
||||
@@ -380,7 +387,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
|
||||
.sfc_opc_multichoices {
|
||||
label > input {
|
||||
vertical-align: middle;
|
||||
vertical-align: text-top;
|
||||
margin-left: 0px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
@@ -422,7 +429,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
padding: 4px 8px; /* 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 {
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -745,7 +752,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background-color: $ibo-color-white-100;
|
||||
background-color: $ibo-search-form-panel--items--hover--color;
|
||||
}
|
||||
|
||||
&.sfl_i_placeholder {
|
||||
@@ -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) */
|
||||
}
|
||||
|
||||
/***********************/
|
||||
|
||||
@@ -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";
|
||||
|
||||
12
css/backoffice/components/input/_input-one-way-password.scss
Normal file
12
css/backoffice/components/input/_input-one-way-password.scss
Normal file
@@ -0,0 +1,12 @@
|
||||
/*!
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-input-one-way-password--elements-spacing-y: 6px !default;
|
||||
|
||||
.ibo-input-one-way-password-wrapper {
|
||||
> *:not(first-child) {
|
||||
margin-top: $ibo-input-one-way-password--elements-spacing-y;
|
||||
}
|
||||
}
|
||||
5
css/backoffice/components/input/_input-richtext.scss
Normal file
5
css/backoffice/components/input/_input-richtext.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
.ibo-input-richtext-placeholder{
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
visibility: hidden;
|
||||
}
|
||||
@@ -14,6 +14,9 @@ $ibo-input-select-selectize--value--min-midth: 150px;
|
||||
$ibo-input-select-selectize--value--height: 100% !default;
|
||||
$ibo-input-select-selectize--value--line-height: $ibo-input--height !default;
|
||||
$ibo-input-select-selectize--dropdown--max-height: 50vh !default;
|
||||
$ibo-input-select-selectize--dropdown--max-width: 50em !default;
|
||||
$ibo-input-select-selectize--item--active--text-color: $ibo-color-grey-800 !default;
|
||||
$ibo-input-select-selectize--item--active--background-color: $ibo-color-blue-100 !default;
|
||||
|
||||
$ibo-input-select-wrapper--width: 100% !default;
|
||||
|
||||
@@ -84,6 +87,7 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
|
||||
}
|
||||
.ibo-input-select-autocomplete{
|
||||
min-width: $ibo-input-select-autocomplete--value--min-midth !important;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.ibo-input-selectize {
|
||||
min-width: $ibo-input-select-selectize--value--min-midth !important;
|
||||
@@ -104,7 +108,7 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
|
||||
}
|
||||
.ibo-input-select-wrapper--with-buttons .selectize-control
|
||||
{
|
||||
display: inline-block;
|
||||
display: grid;
|
||||
width: $ibo-input-select-wrapper--width;
|
||||
}
|
||||
.ibo-input-select-wrapper::after{
|
||||
@@ -178,16 +182,18 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
|
||||
|
||||
/*2 types of selectize to manage (select + autocomplete)*/
|
||||
.selectize-dropdown.ui-autocomplete,
|
||||
.selectize-dropdown.set-dropdown,
|
||||
.selectize-dropdown.plugin-custom_itop {
|
||||
z-index: 2000; /* Note: This is not great as it does not take into account other elements z-index, but as selectize puts its dropdown under the <body> tag, we cannot have a z-index relative to input container. */
|
||||
max-height: $ibo-input-select-selectize--dropdown--max-height;
|
||||
max-width: $ibo-input-select-selectize--dropdown--max-width ;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.selectize-dropdown.ui-menu .ui-state-active {
|
||||
margin: unset;
|
||||
background-color: #f5fafd;
|
||||
color: #495c68;
|
||||
background-color: $ibo-input-select-selectize--item--active--background-color;
|
||||
color: $ibo-input-select-selectize--item--active--text-color;
|
||||
}
|
||||
|
||||
.ibo-input-select--autocomplete-item {
|
||||
@@ -213,5 +219,11 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
|
||||
}
|
||||
|
||||
.ibo-input-select--autocomplete-item-txt {
|
||||
white-space: nowrap;
|
||||
@extend %ibo-text-truncated-with-ellipsis;
|
||||
}
|
||||
|
||||
/* Use "select" in order to avoid stylizing selectize wrapper that copy select classes*/
|
||||
select.ibo-input-select-placeholder{
|
||||
@extend .ibo-input;
|
||||
@extend .ibo-input-select;
|
||||
}
|
||||
@@ -3,20 +3,26 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-input-text--width: 100% !default;
|
||||
$ibo-input-text--min-height: 12rem !default;
|
||||
$ibo-input-text--padding-x: 12px !default;
|
||||
$ibo-input-text--padding-y: 10px !default;
|
||||
|
||||
.ibo-input-text {
|
||||
width: $ibo-input-text--width;
|
||||
min-height: $ibo-input-text--min-height;
|
||||
padding: $ibo-input-text--padding-y $ibo-input-text--padding-x;
|
||||
|
||||
&.ibo-is-code {
|
||||
@extend %ibo-font-code-150;
|
||||
/* Note: There is a mic-mac in the inputs SCSS partials / PHP classes / TWIG templates. All base style should be defined in a common ancestor (AbstractInput) which should have its own BLOCK_CODE and SCSS partial; which is not the case right now.
|
||||
* We should correct this later as now is too close to the RC to make such changes.
|
||||
*/
|
||||
background-color: $ibo-input--background-color;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-input-text--export {
|
||||
width: 100%;
|
||||
min-height: 15em;
|
||||
}
|
||||
|
||||
textarea.ibo-queryoql, .ibo-queryoql:not(:nth-child(n)), .ibo-queryoql > :not(.ibo-field--label) {
|
||||
@extend %ibo-font-code-150;
|
||||
}
|
||||
@@ -49,11 +49,22 @@ $ibo-input--margin-x: 5px !default;
|
||||
color: $ibo-input--placeholder--color;
|
||||
}
|
||||
}
|
||||
.ibo-input-wrapper.is-error {
|
||||
.ibo-input{
|
||||
background-color: $ibo-input-wrapper--is-error--background-color;
|
||||
textarea.ibo-input{
|
||||
height:unset;
|
||||
}
|
||||
.ibo-input-wrapper.is-error, .ibo-input-field-wrapper.is-error {
|
||||
.ibo-input, .ibo-input-vanilla, .cke, textarea {
|
||||
border: 1px solid $ibo-input-wrapper--is-error--border-color;
|
||||
background-color: $ibo-input-wrapper--is-error--background-color;
|
||||
}
|
||||
.ibo-input-vanilla input{
|
||||
border: 0;
|
||||
background-color: #11ffee00;
|
||||
}
|
||||
}
|
||||
input.ibo-input-vanilla{
|
||||
width: unset;
|
||||
display:initial;
|
||||
}
|
||||
.ibo-input-wrapper--with-buttons{
|
||||
position: relative;
|
||||
|
||||
@@ -11,9 +11,9 @@ $ibo-popover-menu--item--text-color: $ibo-color-grey-900 !default;
|
||||
$ibo-popover-menu--item--hyperlink-color: $ibo-popover-menu--item--text-color !default;
|
||||
$ibo-popover-menu--item--hover-background-color: $ibo-color-grey-200 !default;
|
||||
|
||||
$ibo-popover-menu--separator--padding: 0 !default;
|
||||
$ibo-popover-menu--separator--margin: 0 !default;
|
||||
$ibo-popover-menu--separator--background-color: $ibo-color-grey-200 !default;
|
||||
$ibo-popover-menu--item-separator--padding: 0 !default;
|
||||
$ibo-popover-menu--item-separator--margin: 0 !default;
|
||||
$ibo-popover-menu--item-separator--background-color: $ibo-color-grey-200 !default;
|
||||
|
||||
$ibo-popover-menu--item--icon--color: $ibo-color-grey-700 !default;
|
||||
$ibo-popover-menu--item--icon--font-size: $ibo-font-size-200 !default;
|
||||
@@ -31,10 +31,10 @@ $ibo-popover-menu--item--icon--padding-right: 5px !default;
|
||||
background-color: $ibo-popover-menu--item--hover-background-color;
|
||||
color: inherit;
|
||||
}
|
||||
&.ibo-popover-menu--separator{
|
||||
padding: $ibo-popover-menu--separator--padding;
|
||||
margin: $ibo-popover-menu--separator--margin;
|
||||
background-color: $ibo-popover-menu--separator--background-color
|
||||
&.ibo-popover-menu--item-separator{
|
||||
padding: $ibo-popover-menu--item-separator--padding;
|
||||
margin: $ibo-popover-menu--item-separator--margin;
|
||||
background-color: $ibo-popover-menu--item-separator--background-color
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ $ibo-navigation-menu--drawer--border-right: 1px solid $ibo-color-grey-300 !defau
|
||||
$ibo-navigation-menu--menu-filter-input--padding-x: 10px !default;
|
||||
$ibo-navigation-menu--menu-filter-input--padding-y: 8px !default;
|
||||
$ibo-navigation-menu--menu-filter-input--width: 100% !default;
|
||||
$ibo-navigation-menu--menu-filter-input--placeholder-color: $ibo-color-grey-500 !default;
|
||||
$ibo-navigation-menu--menu-filter-input--placeholder-color: $ibo-color-grey-700 !default;
|
||||
$ibo-navigation-menu--menu-filter-input--text-color: $ibo-color-grey-900 !default;
|
||||
$ibo-navigation-menu--menu-filter-input--background-color: $ibo-color-white-100 !default;
|
||||
$ibo-navigation-menu--menu-filter-input--border: 1px solid $ibo-color-grey-300 !default;
|
||||
|
||||
@@ -81,7 +81,7 @@ $ibo-activity-panel--load-all-entries--is-hover--margin-left: ($ibo-activity-pan
|
||||
/* Current or not user specificities */
|
||||
&.ibo-is-current-user{
|
||||
flex-direction: row-reverse;
|
||||
min-width: max-content;
|
||||
min-width: min-content;
|
||||
|
||||
.ibo-activity-entry--medallion{
|
||||
margin-right: initial;
|
||||
|
||||
@@ -17,6 +17,9 @@ $ibo-caselog-entry--main-information--decoration--width: 3px !default;
|
||||
.ibo-activity-entry--main-information-icon{
|
||||
display: none;
|
||||
}
|
||||
.ibo-activity-entry--main-information-content{
|
||||
@extend .ibo-vendors-ckeditor--display-content;
|
||||
}
|
||||
|
||||
/* Highlight color */
|
||||
.ibo-activity-entry--main-information::before{
|
||||
|
||||
@@ -24,7 +24,7 @@ a.ibo-notification-entry--short-description {
|
||||
}
|
||||
/* - Long desc. opened */
|
||||
.ibo-notification-entry{
|
||||
&.ibo-is-opened{
|
||||
&:not(.ibo-is-closed){
|
||||
.ibo-notification-entry--long-description-toggler-icon{
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ $ibo-object-details--tag--separator--diameter: 5px !default;
|
||||
$ibo-object-details--tag--separator--margin-right: 12px !default;
|
||||
$ibo-object-details--tag--separator--border-radius: $ibo-border-radius-full !default;
|
||||
|
||||
$ibo-object-details--header-right--padding-right--is-sticking: 8px !default;
|
||||
|
||||
/* Parent block overloads (!= than blocks integrations) */
|
||||
.ibo-object-details {
|
||||
&.ibo-has-icon.ibo-has-medallion-icon {
|
||||
@@ -51,6 +53,13 @@ $ibo-object-details--tag--separator--border-radius: $ibo-border-radius-full !def
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: Direct child selector is mandatory, otherwise a panel within a panel will have be impacted (eg. n:n relations tabs)
|
||||
> .ibo-panel--header {
|
||||
.ibo-panel--subtitle {
|
||||
@extend %ibo-text-truncated-with-ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-object-details--status {
|
||||
@@ -138,6 +147,9 @@ $ibo-object-details--tag--separator--border-radius: $ibo-border-radius-full !def
|
||||
.ibo-panel--header-left {
|
||||
padding-left: $ibo-object-details--icon--size--is-sticking;
|
||||
}
|
||||
.ibo-panel--header-right {
|
||||
padding-right: $ibo-object-details--header-right--padding-right--is-sticking;
|
||||
}
|
||||
|
||||
.ibo-panel--titles {
|
||||
padding-left: $ibo-object-details--icon--spacing--as-medallion--is-sticking;
|
||||
|
||||
@@ -9,7 +9,7 @@ $ibo-tab-container--tabs-list--padding-x: 24px !default;
|
||||
$ibo-tab-container--tabs-list--background-color: $ibo-color-grey-100 !default;
|
||||
|
||||
$ibo-tab-container--tab-header--max-width: 110px !default;
|
||||
$ibo-tab-container--tab-header--text-color: $ibo-color-grey-700 !default;
|
||||
$ibo-tab-container--tab-header--text-color: $ibo-color-grey-800 !default;
|
||||
$ibo-tab-container--tab-header--text-color--is-active: $ibo-color-blue-800 !default;
|
||||
$ibo-tab-container--tab-header--text-color--on-hover: $ibo-color-blue-800 !default;
|
||||
$ibo-tab-container--tab-header--background-color--on-hover: $ibo-color-grey-200 !default;
|
||||
@@ -49,6 +49,8 @@ $ibo-tab--temporary-remote-content--button--hover--opacity: 0.5 !default;
|
||||
$ibo-tab--temporary-remote-content--button--hover--background-color: $ibo-color-grey-900 !default;
|
||||
$ibo-tab--temporary-remote-content--button--hover--color: $ibo-color-grey-200 !default;
|
||||
|
||||
$ibo-tab-container--tab-container--min-height: auto !default;
|
||||
$ibo-tab-container--tab-container--last--min-height: 60vh !default;
|
||||
|
||||
/* Rules */
|
||||
.ibo-tab-container--tabs-list {
|
||||
@@ -140,8 +142,16 @@ $ibo-tab--temporary-remote-content--button--hover--color: $ibo-color-grey-200 !d
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.ibo-is-scrollable .ibo-tab-container--tab-container--label {
|
||||
display: block;
|
||||
.ibo-tab-container--tab-container-list.ibo-is-scrollable {
|
||||
.ibo-tab-container--tab-container--label {
|
||||
display: block;
|
||||
}
|
||||
.ibo-tab-container--tab-container {
|
||||
min-height: $ibo-tab-container--tab-container--min-height;
|
||||
}
|
||||
.ibo-tab-container--tab-container:last-child {
|
||||
min-height: $ibo-tab-container--tab-container--last--min-height;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-tab-container--tab-container--label {
|
||||
|
||||
@@ -70,8 +70,9 @@ body{
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
#ibo-main-content{
|
||||
padding-top: $ibo-main-content--padding-top;
|
||||
padding-bottom: $ibo-main-content--padding-bottom;
|
||||
|
||||
#ibo-main-content {
|
||||
padding-top: $ibo-main-content--padding-top;
|
||||
padding-bottom: $ibo-main-content--padding-bottom;
|
||||
overflow: auto; /* For scroll to happen in the main content instead of the center content */
|
||||
}
|
||||
@@ -33,14 +33,7 @@ $ibo-datamodel-viewer--schema--tooltip--span--margin: 3px !default;
|
||||
$ibo-datamodel-viewer--schema--tooltip-top--border-color: $ibo-color-grey-700 !default;
|
||||
$ibo-datamodel-viewer--schema--tooltip-top--padding: 3px !default;
|
||||
|
||||
|
||||
#ibo-datamodel-viewer{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.ibo-datamodel-viewer--details{
|
||||
flex-grow: 1;
|
||||
.ibo-panel--subtitle{
|
||||
@extend %ibo-font-ral-nor-150;
|
||||
}
|
||||
|
||||
@@ -69,19 +69,28 @@ $ibo-keyboard-shortcut--input--is-focus--border-color: $ibo-color-primary-600 !d
|
||||
width: auto;
|
||||
text-transform: capitalize;
|
||||
text-align: center;
|
||||
|
||||
|
||||
color: $ibo-keyboard-shortcut--input--color;
|
||||
background-color: $ibo-keyboard-shortcut--input--background-color;
|
||||
border: 1px solid $ibo-keyboard-shortcut--input--border-color;
|
||||
border-bottom: 2px solid $ibo-keyboard-shortcut--input--border-color;
|
||||
border-radius: $ibo-keyboard-shortcut--input--border-radius;
|
||||
|
||||
|
||||
padding: $ibo-keyboard-shortcut--input--padding-y $ibo-keyboard-shortcut--input--padding-x;
|
||||
margin-bottom: $ibo-keyboard-shortcut--input--margin-bottom;
|
||||
|
||||
&.ibo-is-focus{
|
||||
|
||||
&.ibo-is-focus {
|
||||
text-transform: none;
|
||||
color: $ibo-keyboard-shortcut--input--is-focus--color;
|
||||
border-color: $ibo-keyboard-shortcut--input--is-focus--border-color;
|
||||
}
|
||||
}
|
||||
|
||||
#ibo-favorite-organizations .ibo-toolbar {
|
||||
float: right;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#ibo-favorite-organizations .ibo-datatable--toolbar {
|
||||
padding-top: $ibo-button--padding-y/2;
|
||||
}
|
||||
@@ -6,6 +6,8 @@
|
||||
/**
|
||||
* Adjust the lightness of $sColor to the absolute $fTargetLightness value.
|
||||
* It is different than lighten() / darken() that shift the current lightness by X%
|
||||
*
|
||||
* @return Modified color value in HSLA format
|
||||
*/
|
||||
@function ibo-adjust-lightness($sColor, $fTargetLightness) {
|
||||
$iHue: hue($sColor);
|
||||
@@ -16,3 +18,17 @@
|
||||
@return hsla($iHue, $fSaturation, $fTargetLightness, $fAlpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the alpha chanel (opacity) of $sColor to the absolute $fTargetAlpha value.
|
||||
* It is different than opacify() / transparentize() that shift the current alpha value by X%
|
||||
*
|
||||
* @return Modified color value in HSLA format
|
||||
*/
|
||||
@function ibo-adjust-alpha($sColor, $fTargetAlpha) {
|
||||
$iHue: hue($sColor);
|
||||
$fSaturation: saturation($sColor);
|
||||
$fLightness: lightness($sColor);
|
||||
$fAlpha: alpha($sColor);
|
||||
|
||||
@return hsla($iHue, $fSaturation, $fLightness, $fTargetAlpha);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ $ibo-color-grey-600: hsla(215, 16.6%, 63.3%, 1) !default;
|
||||
$ibo-color-grey-700: hsla(214, 11.3%, 48.6%, 1) !default;
|
||||
$ibo-color-grey-800: hsla(215, 16.9%, 30.2%, 1) !default;
|
||||
$ibo-color-grey-900: hsla(215, 22.4%, 16.7%, 1) !default;
|
||||
$ibo-color-grey-950: hsla(215, 26.4%, 10.7%, 1) !default;
|
||||
|
||||
$ibo-color-blue-grey-50: hsla(210, 36%, 96%, 1) !default;
|
||||
$ibo-color-blue-grey-100: hsla(198, 15.7%, 83.7%, 1) !default;
|
||||
@@ -29,6 +30,7 @@ $ibo-color-blue-grey-600: hsla(199, 18.4%, 40.4%, 1) !default;
|
||||
$ibo-color-blue-grey-700: hsla(199, 18.3%, 33.1%, 1) !default;
|
||||
$ibo-color-blue-grey-800: hsla(200, 17.9%, 26.3%, 1) !default;
|
||||
$ibo-color-blue-grey-900: hsla(200, 19.1%, 18.4%, 1) !default;
|
||||
$ibo-color-blue-grey-950: hsla(200, 20.2%, 13.1%, 1) !default;
|
||||
|
||||
$ibo-color-blue-100: hsla(201, 100%, 96.1%, 1) !default;
|
||||
$ibo-color-blue-200: hsla(202, 80.6%, 85.9%, 1) !default;
|
||||
@@ -39,6 +41,7 @@ $ibo-color-blue-600: hsla(209, 61.6%, 50%, 1) !default;
|
||||
$ibo-color-blue-700: hsla(211, 60.7%, 42.9%, 1) !default;
|
||||
$ibo-color-blue-800: hsla(213, 49.4%, 34.1%, 1) !default;
|
||||
$ibo-color-blue-900: hsla(215, 41.3%, 28%, 1) !default;
|
||||
$ibo-color-blue-950: hsla(215, 36.8%, 23%, 1) !default;
|
||||
|
||||
$ibo-color-cyan-100: hsla(186, 61.2%, 86.9%, 1) !default;
|
||||
$ibo-color-cyan-200: hsla(187, 71.6%, 71%, 1) !default;
|
||||
@@ -49,6 +52,7 @@ $ibo-color-cyan-600: hsla(187, 100%, 37.8%, 1) !default;
|
||||
$ibo-color-cyan-700: hsla(186, 100%, 32.7%, 1) !default;
|
||||
$ibo-color-cyan-800: hsla(185, 100%, 28%, 1) !default;
|
||||
$ibo-color-cyan-900: hsla(182, 100%, 19.6%, 1) !default;
|
||||
$ibo-color-cyan-950: hsla(180, 100%, 10.6%, 1) !default;
|
||||
|
||||
$ibo-color-green-100: hsla(88, 50.7%, 85.7%, 1) !default;
|
||||
$ibo-color-green-200: hsla(88, 50%, 76.5%, 1) !default;
|
||||
@@ -59,6 +63,8 @@ $ibo-color-green-600: hsla(89, 46.1%, 48%, 1) !default;
|
||||
$ibo-color-green-700: hsla(92, 47.9%, 42.2%, 1) !default;
|
||||
$ibo-color-green-800: hsla(95, 49.5%, 36.5%, 1) !default;
|
||||
$ibo-color-green-900: hsla(103, 55.6%, 26.5%, 1) !default;
|
||||
$ibo-color-green-950: hsla(108, 59.6%, 21.5%, 1) !default;
|
||||
|
||||
|
||||
$ibo-color-orange-100: hsla(40, 100%, 97.1%, 1) !default;
|
||||
$ibo-color-orange-200: hsla(39, 96.4%, 89%, 1) !default;
|
||||
@@ -69,6 +75,7 @@ $ibo-color-orange-600: hsla(24, 74.7%, 49.6%, 1) !default;
|
||||
$ibo-color-orange-700: hsla(20, 70.7%, 44.1%, 1) !default;
|
||||
$ibo-color-orange-800: hsla(16, 65.1%, 37.1%, 1) !default;
|
||||
$ibo-color-orange-900: hsla(14, 60.8%, 30%, 1) !default;
|
||||
$ibo-color-orange-950: hsla(14, 55.1%, 22%, 1) !default;
|
||||
|
||||
$ibo-color-red-100: hsla(0, 76.9%, 94.9%, 1) !default;
|
||||
$ibo-color-red-200: hsla(0, 95.1%, 92%, 1) !default;
|
||||
@@ -79,6 +86,7 @@ $ibo-color-red-600: hsla(0, 76.3%, 57.1%, 1) !default;
|
||||
$ibo-color-red-700: hsla(0, 60.8%, 48%, 1) !default;
|
||||
$ibo-color-red-800: hsla(0, 55.8%, 39%, 1) !default;
|
||||
$ibo-color-red-900: hsla(0, 46.8%, 31%, 1) !default;
|
||||
$ibo-color-red-950: hsla(0, 42.9%, 20%, 1) !default;
|
||||
|
||||
$ibo-color-pink-100: hsla(348, 100%, 98%, 1) !default;
|
||||
$ibo-color-pink-200: hsla(343, 95%, 92%, 1) !default;
|
||||
@@ -89,6 +97,7 @@ $ibo-color-pink-600: hsla(329, 64%, 54%, 1) !default;
|
||||
$ibo-color-pink-700: hsla(325, 57%, 46%, 1) !default;
|
||||
$ibo-color-pink-800: hsla(322, 60%, 37%, 1) !default;
|
||||
$ibo-color-pink-900: hsla(318, 51%, 29%, 1) !default;
|
||||
$ibo-color-pink-950: hsla(318, 51%, 21%, 1) !default;
|
||||
|
||||
$ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pink', 'primary', 'secondary', 'information', 'success', 'warning', 'danger');
|
||||
|
||||
@@ -108,6 +117,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
|
||||
--ibo-color-grey-700: #{$ibo-color-grey-700};
|
||||
--ibo-color-grey-800: #{$ibo-color-grey-800};
|
||||
--ibo-color-grey-900: #{$ibo-color-grey-900};
|
||||
--ibo-color-grey-950: #{$ibo-color-grey-950};
|
||||
|
||||
--ibo-color-blue-grey-50: #{$ibo-color-blue-grey-50};
|
||||
--ibo-color-blue-grey-100: #{$ibo-color-blue-grey-100};
|
||||
@@ -119,6 +129,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
|
||||
--ibo-color-blue-grey-700: #{$ibo-color-blue-grey-700};
|
||||
--ibo-color-blue-grey-800: #{$ibo-color-blue-grey-800};
|
||||
--ibo-color-blue-grey-900: #{$ibo-color-blue-grey-900};
|
||||
--ibo-color-blue-grey-950: #{$ibo-color-blue-grey-950};
|
||||
|
||||
--ibo-color-blue-100: #{$ibo-color-blue-100};
|
||||
--ibo-color-blue-200: #{$ibo-color-blue-200};
|
||||
@@ -129,6 +140,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
|
||||
--ibo-color-blue-700: #{$ibo-color-blue-700};
|
||||
--ibo-color-blue-800: #{$ibo-color-blue-800};
|
||||
--ibo-color-blue-900: #{$ibo-color-blue-900};
|
||||
--ibo-color-blue-950: #{$ibo-color-blue-950};
|
||||
|
||||
--ibo-color-cyan-100: #{$ibo-color-cyan-100};
|
||||
--ibo-color-cyan-200: #{$ibo-color-cyan-200};
|
||||
@@ -139,6 +151,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
|
||||
--ibo-color-cyan-700: #{$ibo-color-cyan-700};
|
||||
--ibo-color-cyan-800: #{$ibo-color-cyan-800};
|
||||
--ibo-color-cyan-900: #{$ibo-color-cyan-900};
|
||||
--ibo-color-cyan-950: #{$ibo-color-cyan-950};
|
||||
|
||||
--ibo-color-green-100: #{$ibo-color-green-100};
|
||||
--ibo-color-green-200: #{$ibo-color-green-200};
|
||||
@@ -149,6 +162,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
|
||||
--ibo-color-green-700: #{$ibo-color-green-700};
|
||||
--ibo-color-green-800: #{$ibo-color-green-800};
|
||||
--ibo-color-green-900: #{$ibo-color-green-900};
|
||||
--ibo-color-green-950: #{$ibo-color-green-950};
|
||||
|
||||
--ibo-color-orange-100: #{$ibo-color-orange-100};
|
||||
--ibo-color-orange-200: #{$ibo-color-orange-200};
|
||||
@@ -159,6 +173,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
|
||||
--ibo-color-orange-700: #{$ibo-color-orange-700};
|
||||
--ibo-color-orange-800: #{$ibo-color-orange-800};
|
||||
--ibo-color-orange-900: #{$ibo-color-orange-900};
|
||||
--ibo-color-orange-950: #{$ibo-color-orange-950};
|
||||
|
||||
--ibo-color-red-100: #{$ibo-color-red-100};
|
||||
--ibo-color-red-200: #{$ibo-color-red-200};
|
||||
@@ -169,6 +184,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
|
||||
--ibo-color-red-700: #{$ibo-color-red-700};
|
||||
--ibo-color-red-800: #{$ibo-color-red-800};
|
||||
--ibo-color-red-900: #{$ibo-color-red-900};
|
||||
--ibo-color-red-950: #{$ibo-color-red-950};
|
||||
|
||||
--ibo-color-pink-100: #{$ibo-color-pink-100};
|
||||
--ibo-color-pink-200: #{$ibo-color-pink-200};
|
||||
@@ -179,4 +195,5 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
|
||||
--ibo-color-pink-700: #{$ibo-color-pink-700};
|
||||
--ibo-color-pink-800: #{$ibo-color-pink-800};
|
||||
--ibo-color-pink-900: #{$ibo-color-pink-900};
|
||||
--ibo-color-pink-950: #{$ibo-color-pink-950};
|
||||
}
|
||||
@@ -6,4 +6,7 @@
|
||||
/* Base helpers for colors */
|
||||
/* - These lightness vars are used to force a certain lightness on HSLA colors */
|
||||
$ibo-color-base-lightness-100: 93% !default;
|
||||
$ibo-color-base-lightness-900: 15% !default;
|
||||
$ibo-color-base-lightness-900: 15% !default;
|
||||
|
||||
$ibo-color-base-opacity-for-lightness-100: 1 !default;
|
||||
$ibo-color-base-opacity-for-lightness-900: 1 !default;
|
||||
@@ -14,6 +14,7 @@ $ibo-color-primary-600: $ibo-color-orange-600 !default;
|
||||
$ibo-color-primary-700: $ibo-color-orange-700 !default;
|
||||
$ibo-color-primary-800: $ibo-color-orange-800 !default;
|
||||
$ibo-color-primary-900: $ibo-color-orange-900 !default;
|
||||
$ibo-color-primary-950: $ibo-color-orange-950 !default;
|
||||
|
||||
/* - Secondary color of the brand */
|
||||
$ibo-color-secondary-100: $ibo-color-grey-100 !default;
|
||||
@@ -25,6 +26,7 @@ $ibo-color-secondary-600: $ibo-color-grey-600 !default;
|
||||
$ibo-color-secondary-700: $ibo-color-grey-700 !default;
|
||||
$ibo-color-secondary-800: $ibo-color-grey-800 !default;
|
||||
$ibo-color-secondary-900: $ibo-color-grey-900 !default;
|
||||
$ibo-color-secondary-950: $ibo-color-grey-950 !default;
|
||||
|
||||
/* - Information: messages / actions that should neither seem as success, warning or failure */
|
||||
$ibo-color-information-100: $ibo-color-blue-100 !default;
|
||||
@@ -36,6 +38,7 @@ $ibo-color-information-600: $ibo-color-blue-600 !default;
|
||||
$ibo-color-information-700: $ibo-color-blue-700 !default;
|
||||
$ibo-color-information-800: $ibo-color-blue-800 !default;
|
||||
$ibo-color-information-900: $ibo-color-blue-900 !default;
|
||||
$ibo-color-information-950: $ibo-color-blue-950 !default;
|
||||
|
||||
/* Success: messages of success, safe actions, ... */
|
||||
$ibo-color-success-100: $ibo-color-green-100 !default;
|
||||
@@ -47,6 +50,7 @@ $ibo-color-success-600: $ibo-color-green-600 !default;
|
||||
$ibo-color-success-700: $ibo-color-green-700 !default;
|
||||
$ibo-color-success-800: $ibo-color-green-800 !default;
|
||||
$ibo-color-success-900: $ibo-color-green-900 !default;
|
||||
$ibo-color-success-950: $ibo-color-green-950 !default;
|
||||
|
||||
/* Warning: messages of warning, actions that would be done carefully, ... */
|
||||
$ibo-color-warning-100: $ibo-color-orange-100 !default;
|
||||
@@ -58,6 +62,7 @@ $ibo-color-warning-600: $ibo-color-orange-600 !default;
|
||||
$ibo-color-warning-700: $ibo-color-orange-700 !default;
|
||||
$ibo-color-warning-800: $ibo-color-orange-800 !default;
|
||||
$ibo-color-warning-900: $ibo-color-orange-900 !default;
|
||||
$ibo-color-warning-950: $ibo-color-orange-950 !default;
|
||||
|
||||
/* Danger: messages of failure, error, ... */
|
||||
$ibo-color-error-100: $ibo-color-red-100 !default;
|
||||
@@ -69,6 +74,7 @@ $ibo-color-error-600: $ibo-color-red-600 !default;
|
||||
$ibo-color-error-700: $ibo-color-red-700 !default;
|
||||
$ibo-color-error-800: $ibo-color-red-800 !default;
|
||||
$ibo-color-error-900: $ibo-color-red-900 !default;
|
||||
$ibo-color-error-950: $ibo-color-red-950 !default;
|
||||
|
||||
/* Danger: messages of danger, actions that cannot be undone, ... */
|
||||
$ibo-color-danger-100: $ibo-color-red-100 !default;
|
||||
@@ -80,6 +86,7 @@ $ibo-color-danger-600: $ibo-color-red-600 !default;
|
||||
$ibo-color-danger-700: $ibo-color-red-700 !default;
|
||||
$ibo-color-danger-800: $ibo-color-red-800 !default;
|
||||
$ibo-color-danger-900: $ibo-color-red-900 !default;
|
||||
$ibo-color-danger-950: $ibo-color-red-950 !default;
|
||||
|
||||
$ibo-semantic-colors: ('primary', 'secondary', 'information', 'success', 'warning', 'danger');
|
||||
|
||||
@@ -103,6 +110,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
|
||||
--ibo-color-primary-700: #{$ibo-color-primary-700};
|
||||
--ibo-color-primary-800: #{$ibo-color-primary-800};
|
||||
--ibo-color-primary-900: #{$ibo-color-primary-900};
|
||||
--ibo-color-primary-950: #{$ibo-color-primary-950};
|
||||
|
||||
--ibo-color-secondary-100: #{$ibo-color-secondary-100};
|
||||
--ibo-color-secondary-200: #{$ibo-color-secondary-200};
|
||||
@@ -113,6 +121,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
|
||||
--ibo-color-secondary-700: #{$ibo-color-secondary-700};
|
||||
--ibo-color-secondary-800: #{$ibo-color-secondary-800};
|
||||
--ibo-color-secondary-900: #{$ibo-color-secondary-900};
|
||||
--ibo-color-secondary-950: #{$ibo-color-secondary-950};
|
||||
|
||||
--ibo-color-information-100: #{$ibo-color-information-100};
|
||||
--ibo-color-information-200: #{$ibo-color-information-200};
|
||||
@@ -123,6 +132,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
|
||||
--ibo-color-information-700: #{$ibo-color-information-700};
|
||||
--ibo-color-information-800: #{$ibo-color-information-800};
|
||||
--ibo-color-information-900: #{$ibo-color-information-900};
|
||||
--ibo-color-information-950: #{$ibo-color-information-950};
|
||||
|
||||
--ibo-color-success-100: #{$ibo-color-success-100};
|
||||
--ibo-color-success-200: #{$ibo-color-success-200};
|
||||
@@ -133,6 +143,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
|
||||
--ibo-color-success-700: #{$ibo-color-success-700};
|
||||
--ibo-color-success-800: #{$ibo-color-success-800};
|
||||
--ibo-color-success-900: #{$ibo-color-success-900};
|
||||
--ibo-color-success-950: #{$ibo-color-success-950};
|
||||
|
||||
--ibo-color-warning-100: #{$ibo-color-warning-100};
|
||||
--ibo-color-warning-200: #{$ibo-color-warning-200};
|
||||
@@ -143,6 +154,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
|
||||
--ibo-color-warning-700: #{$ibo-color-warning-700};
|
||||
--ibo-color-warning-800: #{$ibo-color-warning-800};
|
||||
--ibo-color-warning-900: #{$ibo-color-warning-900};
|
||||
--ibo-color-warning-950: #{$ibo-color-warning-950};
|
||||
|
||||
--ibo-color-danger-100: #{$ibo-color-danger-100};
|
||||
--ibo-color-danger-200: #{$ibo-color-danger-200};
|
||||
@@ -153,6 +165,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
|
||||
--ibo-color-danger-700: #{$ibo-color-danger-700};
|
||||
--ibo-color-danger-800: #{$ibo-color-danger-800};
|
||||
--ibo-color-danger-900: #{$ibo-color-danger-900};
|
||||
--ibo-color-danger-950: #{$ibo-color-danger-950};
|
||||
|
||||
--ibo-color-error-100: #{$ibo-color-error-100};
|
||||
--ibo-color-error-200: #{$ibo-color-error-200};
|
||||
@@ -163,6 +176,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
|
||||
--ibo-color-error-700: #{$ibo-color-error-700};
|
||||
--ibo-color-error-800: #{$ibo-color-error-800};
|
||||
--ibo-color-error-900: #{$ibo-color-error-900};
|
||||
--ibo-color-error-950: #{$ibo-color-error-950};
|
||||
|
||||
--ibo-caselog-color-highlight-1: #{$ibo-caselog-highlight-color-1};
|
||||
--ibo-caselog-color-highlight-2: #{$ibo-caselog-highlight-color-2};
|
||||
|
||||
3
css/backoffice/vendors/_all.scss
vendored
3
css/backoffice/vendors/_all.scss
vendored
@@ -15,4 +15,5 @@
|
||||
@import "datatables";
|
||||
@import "jquery-treeview";
|
||||
@import "jquery-blockui";
|
||||
@import "magnific-popup";
|
||||
@import "magnific-popup";
|
||||
@import "selectize";
|
||||
15
css/backoffice/vendors/_ckeditor.scss
vendored
15
css/backoffice/vendors/_ckeditor.scss
vendored
@@ -85,4 +85,19 @@ ul.cke_autocomplete_panel{
|
||||
color: $ibo-vendors-ckeditor--autocomplete-item-title--text-color;
|
||||
@extend %ibo-font-weight-700;
|
||||
}
|
||||
}
|
||||
|
||||
/* These rules should be the same as those in css/ckeditor/contents.css */
|
||||
.ibo-vendors-ckeditor--display-content{
|
||||
.cke_editable{
|
||||
line-height: 1.4;
|
||||
}
|
||||
figure{
|
||||
border: solid 1px #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
p{
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
}
|
||||
6
css/backoffice/vendors/_jquery-blockui.scss
vendored
6
css/backoffice/vendors/_jquery-blockui.scss
vendored
@@ -4,15 +4,15 @@
|
||||
*/
|
||||
|
||||
$ibo-vendors-blockui--blockoverlay--background-color: $ibo-color-white-200 !default;
|
||||
$ibo-vendors-blockui--blockomsg--color: $ibo-color-grey-700 !default;
|
||||
$ibo-vendors-blockui--blockmsg--color: $ibo-color-grey-700 !default;
|
||||
|
||||
.blockUI.blockOverlay{
|
||||
background-color: $ibo-color-white-200;
|
||||
background-color: $ibo-vendors-blockui--blockoverlay--background-color;
|
||||
}
|
||||
.blockUI.blockMsg{
|
||||
font-size: 6em;
|
||||
text-align: center;
|
||||
color: $ibo-color-grey-700;
|
||||
color: $ibo-vendors-blockui--blockmsg--color;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
@@ -21,15 +21,16 @@ $ibo-vendors-jquery-multiselect--ui-multiselect-menu--z-index: 10000 !default;
|
||||
|
||||
|
||||
|
||||
.ui-multiselect {
|
||||
.ui-multiselect {
|
||||
@extend .ibo-input;
|
||||
@extend .ibo-input-select;
|
||||
width: auto !important;
|
||||
padding-left: $ibo-vendors-jquery-multiselect--ui-multiselect--padding-left !important;
|
||||
padding-right: $ibo-vendors-jquery-multiselect--ui-multiselect--padding-right !important;
|
||||
text-align:left;
|
||||
white-space:nowrap;
|
||||
overflow:hidden;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
button.ui-multiselect > span {
|
||||
|
||||
6
css/backoffice/vendors/_jqueryui.scss
vendored
6
css/backoffice/vendors/_jqueryui.scss
vendored
@@ -66,6 +66,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
|
||||
// Tabs
|
||||
|
||||
.ui-dialog{
|
||||
box-sizing: content-box; /* Necessary to overload our theme default setting */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
@@ -104,6 +105,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
|
||||
height: 20px;
|
||||
}
|
||||
.ui-dialog-content {
|
||||
box-sizing: content-box; /* Necessary to overload our theme default setting */
|
||||
position: relative;
|
||||
padding: $ibo-vendors-jqueryui--ui-dialog-content--padding-y $ibo-vendors-jqueryui--ui-dialog-content--padding-x;
|
||||
overflow: auto;
|
||||
@@ -185,7 +187,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
|
||||
}
|
||||
&.ui-dialog-titlebar-close{
|
||||
@extend .ibo-is-alternative;
|
||||
@extend .ibo-is-grey;
|
||||
@extend .ibo-is-neutral;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -578,7 +580,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
|
||||
}
|
||||
|
||||
.ui-autocomplete-category{
|
||||
@extend .ibo-popover-menu--separator
|
||||
@extend .ibo-popover-menu--item-separator
|
||||
}
|
||||
// Menu
|
||||
|
||||
|
||||
8
css/backoffice/vendors/_selectize.scss
vendored
Normal file
8
css/backoffice/vendors/_selectize.scss
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
.selectize-dropdown-content {
|
||||
max-height: unset; /* Overloaded as it will be handled by the _input-select.scss partial */
|
||||
}
|
||||
20
css/ckeditor/contents.css
Normal file
20
css/ckeditor/contents.css
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/* These rules should be the same as those in css/backoffice/vendors/ckeditor.css */
|
||||
body{
|
||||
margin: 5px 10px;
|
||||
}
|
||||
.cke_editable{
|
||||
line-height: 1.4;
|
||||
}
|
||||
figure{
|
||||
border: solid 1px #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
p{
|
||||
margin-top: 0.25em;
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
@@ -2,12 +2,35 @@
|
||||
@media print {
|
||||
html {
|
||||
font-size: 10px;
|
||||
break-after:auto;
|
||||
break-after: auto;
|
||||
}
|
||||
|
||||
.not-printable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ibo-panel--icon {
|
||||
-webkit-print-color-adjust: exact !important; /* Chrome, Safari, Edge */
|
||||
color-adjust: exact !important; /*Firefox*/
|
||||
}
|
||||
|
||||
.printable-content legend::before,
|
||||
.printable-content .ibo-activity-panel--tab-toolbar-action::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.printable-content .strikethrough {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#hiddeable_chapters .ibo-block-print--tab.strikethrough {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#hiddeable_chapters .ibo-block-print--tab span::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
span.ui-layout-resizer {
|
||||
display: none;
|
||||
}
|
||||
@@ -97,6 +120,10 @@
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.ibo-field--label > .ibo-has-description::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* PRINT Stylesheet
|
||||
*
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -391,10 +391,6 @@ body {
|
||||
height: 100%;
|
||||
.ibo-setup--wizard--content{
|
||||
overflow: auto;
|
||||
|
||||
input#db_backup {
|
||||
margin-right: 0.4rem;
|
||||
}
|
||||
}
|
||||
.ibo-setup--wizard--buttons-container{
|
||||
margin-top: auto;
|
||||
@@ -565,9 +561,6 @@ body {
|
||||
}
|
||||
.setup-accept-licenses{
|
||||
margin-top: 18px;
|
||||
input{
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.module-selection-banner{
|
||||
display: flex;
|
||||
@@ -599,6 +592,9 @@ body {
|
||||
color: $ibo-color-blue-700;
|
||||
font-size: $ibo-font-size-200;
|
||||
}
|
||||
.setup--wizard-choice--label + .setup--wizard-choice--more-info {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
/* integrityCheck: end (do not remove/edit) */
|
||||
|
||||
/* Legacy inline stuff */
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2013 XXXXX
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
* @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'CAS:Error:UserNotAllowed' => 'User not allowed~~',
|
||||
'CAS:Login:SignIn' => 'Sign in with CAS~~',
|
||||
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~',
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'CAS:Error:UserNotAllowed' => 'Usuario no permitido',
|
||||
'CAS:Login:SignIn' => 'Iniciar sesión con CAS',
|
||||
'CAS:Login:SignInTooltip' => 'Click para autenticarse con servidor CAS',
|
||||
));
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
/**
|
||||
* Localized data
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
@@ -34,7 +34,7 @@
|
||||
//
|
||||
// Class: UserExternal
|
||||
//
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:UserExternal' => 'Usuario Externo',
|
||||
'Class:UserExternal+' => 'Usuario Autenticado fuera de '.ITOP_APPLICATION_SHORT,
|
||||
));
|
||||
|
||||
@@ -37,7 +37,7 @@ class UserExternal extends User
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/authentication,grant_by_profile",
|
||||
"category" => "addon/authentication,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "login",
|
||||
"state_attcode" => "",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* @copyright Copyright (C) 2010-2020 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/</comment>
|
||||
<category>addon/authentication,grant_by_profile</category>
|
||||
<category>addon/authentication,grant_by_profile,silo</category>
|
||||
<abstract>false</abstract>
|
||||
<key_type>autoincrement</key_type>
|
||||
<db_table>priv_user_ldap</db_table>
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
/**
|
||||
* Localized data
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
@@ -34,7 +34,7 @@
|
||||
//
|
||||
// Class: UserLDAP
|
||||
//
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:UserLDAP' => 'Usuario LDAP',
|
||||
'Class:UserLDAP+' => 'Usuario Autenticado vía LDAP',
|
||||
));
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
/**
|
||||
* Localized data
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
@@ -34,24 +34,24 @@
|
||||
//
|
||||
// Class: UserLocal
|
||||
//
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:UserLocal' => 'Usuario de '.ITOP_APPLICATION_SHORT,
|
||||
'Class:UserLocal+' => 'Usuario Autenticado vía '.ITOP_APPLICATION_SHORT,
|
||||
'Class:UserLocal/Attribute:password' => 'Contraseña',
|
||||
'Class:UserLocal/Attribute:password+' => 'Contraseña',
|
||||
|
||||
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
|
||||
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Never expire~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expired~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Class:UserLocal/Attribute:expiration' => 'Expiración de contraseña',
|
||||
'Class:UserLocal/Attribute:expiration+' => 'Estatus de expiración de contraseña (requiere de una extensión para que tenga efecto)',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Puede expirar',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Nunca expirar',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expirado',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Renovación de contraseña',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Cuando fue el último cambio de contraseña',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 8 caracteres e incluír mayúsculas, minúsculas, números y caracteres especiales.',
|
||||
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~'
|
||||
'UserLocal:password:expiration' => 'El siguiente campo requiere una extensión'
|
||||
));
|
||||
|
||||
@@ -77,7 +77,7 @@ class UserLocal extends UserInternal
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/authentication,grant_by_profile",
|
||||
"category" => "addon/authentication,grant_by_profile,silo",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "login",
|
||||
"state_attcode" => "",
|
||||
|
||||
34
datamodels/2.x/combodo-db-tools/bin/rebuildhk.php
Normal file
34
datamodels/2.x/combodo-db-tools/bin/rebuildhk.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Rebuild the hierarchical keys control data
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Core\MetaModel\HierarchicalKey;
|
||||
|
||||
require_once ('../../../approot.inc.php');
|
||||
require_once APPROOT.'application/startup.inc.php';
|
||||
|
||||
foreach(MetaModel::GetClasses() as $sClass)
|
||||
{
|
||||
if (!MetaModel::HasTable($sClass))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
// Check (once) all the attributes that are hierarchical keys
|
||||
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey())
|
||||
{
|
||||
echo "Rebuild hierarchical key $sAttCode from $sClass.\n";
|
||||
HierarchicalKey::Rebuild($sClass, $sAttCode, $oAttDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
echo "Done\n";
|
||||
@@ -6,10 +6,7 @@
|
||||
|
||||
use Combodo\iTop\DBTools\Service\DBAnalyzerUtils;
|
||||
|
||||
@include_once('../approot.inc.php');
|
||||
@include_once('../../approot.inc.php');
|
||||
@include_once('../../../approot.inc.php');
|
||||
|
||||
require_once ('../../../approot.inc.php');
|
||||
require_once(APPROOT.'application/startup.inc.php');
|
||||
|
||||
require_once('../db_analyzer.class.inc.php');
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
use Combodo\iTop\Core\MetaModel\HierarchicalKey;
|
||||
|
||||
class DatabaseAnalyzer
|
||||
{
|
||||
var $iTimeLimitPerOperation;
|
||||
@@ -162,6 +164,9 @@ class DatabaseAnalyzer
|
||||
if ($oAttDef->IsExternalKey())
|
||||
{
|
||||
$this->CheckExternalKeys($oAttDef, $sTable, $sKeyField, $sAttCode, $sClass, $aErrorsAndFixes);
|
||||
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey()) {
|
||||
$this->CheckHK($sClass, $sAttCode, $aErrorsAndFixes);
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef->IsDirectField() && !($oAttDef instanceof AttributeTagSet))
|
||||
{
|
||||
@@ -506,5 +511,24 @@ class DatabaseAnalyzer
|
||||
$this->ExecQuery($sSelWrongRecs, $sFixit, $sErrorDesc, $sClass, $aErrorsAndFixes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check hierarchical keys
|
||||
*
|
||||
* @param $sClass
|
||||
* @param $sAttCode
|
||||
* @param $aErrorsAndFixes
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function CheckHK($sClass, $sAttCode, &$aErrorsAndFixes)
|
||||
{
|
||||
try {
|
||||
HierarchicalKey::VerifyIntegrity($sClass, $sAttCode, MetaModel::GetAttributeDef($sClass, $sAttCode));
|
||||
} catch (CoreException $e) {
|
||||
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-HKInvalid', $sAttCode);
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['count'] = 1;
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['query'] = '-- N/A';
|
||||
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = ['-- Run script env-'.utils::GetCurrentEnvironment().DIRECTORY_SEPARATOR.'combodo-db-tools'.DIRECTORY_SEPARATOR.'bin'.DIRECTORY_SEPARATOR.'rebuildhk.php' ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,17 +234,19 @@ function DisplayErrorDetails($aResults, $bVerbose)
|
||||
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:SQLquery'));
|
||||
$oCollapsible->AddSubBlock($oFieldSet);
|
||||
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($aError['query']);
|
||||
$oFieldSet->AddSubBlock($oCode);
|
||||
if (array_key_exists('query', $aError)) {
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($aError['query']);
|
||||
$oFieldSet->AddSubBlock($oCode);
|
||||
|
||||
if (isset($aError['fixit'])) {
|
||||
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:FixitSQLquery'));
|
||||
$oCollapsible->AddSubBlock($oFieldSet);
|
||||
if (isset($aError['fixit'])) {
|
||||
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:FixitSQLquery'));
|
||||
$oCollapsible->AddSubBlock($oFieldSet);
|
||||
|
||||
$aQueries = $aError['fixit'];
|
||||
foreach ($aQueries as $sFixQuery) {
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($sFixQuery);
|
||||
$oFieldSet->AddSubBlock($oCode);
|
||||
$aQueries = $aError['fixit'];
|
||||
foreach ($aQueries as $sFixQuery) {
|
||||
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($sFixQuery);
|
||||
$oFieldSet->AddSubBlock($oCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'DBTools:Class' => 'Klasse',
|
||||
'DBTools:Title' => 'Datenbankpflege-Tools',
|
||||
'DBTools:ErrorsFound' => 'Fehler gefunden',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
'DBTools:Indication' => 'Wichtig: Nach dem Fixen der Errors in der Datenbank müssen Sie die Analyse erneut laufen lassen, weil durch die Fixes eventuell weitere Inkonsistenzen enstanden sind',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: FERTIGEN SIE EIN BACKUP IHRER DATENBANK AN, BEVOR SIE DIE FIXES ANWENDEN!',
|
||||
'DBTools:Error' => 'Fehler',
|
||||
'DBTools:Count' => 'Anzahl',
|
||||
'DBTools:SQLquery' => 'SQL Query',
|
||||
|
||||
@@ -55,6 +55,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all',
|
||||
'DBAnalyzer-Integrity-HKInvalid' => 'Broken hierarchical key `%1$s`',
|
||||
'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`',
|
||||
'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class',
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Localized data
|
||||
* Spanish localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
@@ -21,11 +22,11 @@
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
// Database inconsistencies
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'Herramientas de bases de datos',
|
||||
'Menu:DBToolsMenu' => 'Integridad de Base de Datos',
|
||||
'DBTools:Class' => 'Clase',
|
||||
'DBTools:Title' => 'Herramientas de mantenimiento de base de datos~~',
|
||||
'DBTools:Title' => 'Herramientas de Mantenimiento de Base de Datos',
|
||||
'DBTools:ErrorsFound' => 'Errores encontrados',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
@@ -39,14 +40,14 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'DBTools:ShowIds' => 'Vista detallada',
|
||||
'DBTools:ShowReport' => 'Reporte',
|
||||
'DBTools:IntegrityCheck' => 'Verificación de integridad',
|
||||
'DBTools:FetchCheck' => 'Fetch Check (long)~~',
|
||||
'DBTools:FetchCheck' => 'Verificación de búsqueda (larga)',
|
||||
'DBTools:SelectAnalysisType' => 'Select analysis type~~',
|
||||
|
||||
'DBTools:Analyze' => 'Analizar',
|
||||
'DBTools:Details' => 'Mostrar detalles',
|
||||
'DBTools:ShowAll' => 'Mostrar todos los errores',
|
||||
|
||||
'DBTools:Inconsistencies' => 'Inconsistencias de base de datos',
|
||||
'DBTools:Inconsistencies' => 'Inconsistencias de Base de Datos',
|
||||
'DBTools:DetailedErrorTitle' => '%2$s error(s) in class %1$s: %3$s~~',
|
||||
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Registro huérfano en `%1$s`, debería tener su contraparte en la tabla `%2$s`',
|
||||
@@ -54,32 +55,32 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'DBAnalyzer-Integrity-MissingExtKey' => 'Llave externa perdida %1$s (columna: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-InvalidValue' => 'Valor inválido para %1$s (columna: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Algunas cuentas de usuario no tienen perfil asignado',
|
||||
'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~',
|
||||
'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~',
|
||||
'DBAnalyzer-Fetch-Count-Error' => 'Obtener cuenta de errores en `%1$s`, %2$d entradas recuperadas / %3$d contadas',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'Campo `%2$s`.`%1$s` debe tener los mismos valores que `%3$s`.`%1$s`',
|
||||
'DBAnalyzer-Integrity-RootFinalClass' => 'Campo `%2$s`.`%1$s` debe contener un caracter válido',
|
||||
));
|
||||
|
||||
// Database Info
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'DBTools:DatabaseInfo' => 'Información de base de datos',
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'DBTools:DatabaseInfo' => 'Información de Base de Datos',
|
||||
'DBTools:Base' => 'Base',
|
||||
'DBTools:Size' => 'Tamaño',
|
||||
));
|
||||
|
||||
// Lost attachments
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'DBTools:LostAttachments' => 'Adjuntos perdidos',
|
||||
'DBTools:LostAttachments:Disclaimer' => 'Aquí usted puede buscar adjuntos perdidos o desplazados. Esta NO es una herramienta de recuperación de datos, no obtiene datos borrados.',
|
||||
'DBTools:LostAttachments:Disclaimer' => 'Aquí usted puede buscar anexos perdidos o fuera de lugar. Esta NO es una herramienta de recuperación de datos, no obtiene datos borrados.',
|
||||
|
||||
'DBTools:LostAttachments:Button:Analyze' => 'Analizar',
|
||||
'DBTools:LostAttachments:Button:Restore' => 'Restaurar',
|
||||
'DBTools:LostAttachments:Button:Restore:Confirm' => 'Esta acción no se puede deshacer, por favor confirme que quiere restaurar los archivos seleccionados.',
|
||||
'DBTools:LostAttachments:Button:Busy' => 'Por favor espere...',
|
||||
|
||||
'DBTools:LostAttachments:Step:Analyze' => 'Primero, buscaremos adjuntos perdidos/desplazados analizando la base de datos.',
|
||||
'DBTools:LostAttachments:Step:Analyze' => 'Primero, buscar anexos perdidos o fuera de lugar analizando la base de datos.',
|
||||
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analizar resultados:',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Genial! Todo parece estar en el lugar correcto.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:None' => '¡Genial! Todo parece estar en el lugar correcto.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Algunos adjuntos (%1$d) parecen estar desplazados. Mire la siguiente lista y verifique los que quiera mover.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Nombre de archivo',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Ubicación actual',
|
||||
|
||||
@@ -46,10 +46,11 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'DBTools:DetailedErrorTitle' => '%2$s erreur(s) dans la classe %1$s : %3$s',
|
||||
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Enregistrement orphelin dans `%1$s`, il devrait avoir son équivalent dans la table `%2$s`',
|
||||
'DBAnalyzer-Integrity-InvalidExtKey' => 'Clef externe invalide %1$s (colonne: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-MissingExtKey' => 'Clef externe manquante %1$s (colonne: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-InvalidExtKey' => 'Clé externe invalide %1$s (colonne: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-MissingExtKey' => 'Clé externe manquante %1$s (colonne: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-InvalidValue' => 'Valeur invalide pour %1$s (colonne: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Certains comptes utilisateurs n\'ont aucun profile',
|
||||
'DBAnalyzer-Integrity-HKInvalid' => 'Clé hiérarchique `%1$s` invalide',
|
||||
'DBAnalyzer-Fetch-Count-Error' => 'Erreur de récupération dans `%1$s`, %2$d enregistrements récupérés / %3$d comptés',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'Le champ `%2$s`.`%1$s` doit avoir la même valeur que `%3$s`.`%1$s`',
|
||||
'DBAnalyzer-Integrity-RootFinalClass' => 'Le champ `%2$s`.`%1$s` doit contenir une classe valide',
|
||||
|
||||
@@ -34,6 +34,7 @@ SetupWebPage::AddModule(
|
||||
// Setup
|
||||
//
|
||||
'dependencies' => array(
|
||||
'itop-structure/3.0.0',
|
||||
),
|
||||
'mandatory' => false,
|
||||
'visible' => true,
|
||||
|
||||
@@ -40,9 +40,11 @@ class DBAnalyzerUtils
|
||||
$iCount = $aError['count'];
|
||||
fwrite($fReport, '-- Count: '.$iCount."\r\n");
|
||||
fwrite($fReport, '-- Error: '.$sErrorLabel."\r\n");
|
||||
$sQuery = $aError['query'];
|
||||
fwrite($fReport, '-- Query: '.$sQuery."\r\n");
|
||||
|
||||
if (array_key_exists('query', $aError)) {
|
||||
$sQuery = $aError['query'];
|
||||
fwrite($fReport, '-- Query: '.$sQuery."\r\n");
|
||||
}
|
||||
|
||||
if (isset($aError['fixit']))
|
||||
{
|
||||
fwrite($fReport, "\r\n-- Fix it (indication):\r\n\r\n");
|
||||
|
||||
@@ -195,14 +195,15 @@
|
||||
<banner>/images/icons/icons8-important-book.svg</banner>
|
||||
<options type="array">
|
||||
<choice>
|
||||
<extension_code>itop-kown-error-mgmt</extension_code>
|
||||
<title>Known Errors Management and FAQ</title>
|
||||
<description>Select this option to track "Known Errors" and FAQs in iTop.</description>
|
||||
<modules type="array">
|
||||
<module>itop-faq-light</module>
|
||||
<module>itop-knownerror-mgmt</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<!-- Extension code has a typo but fixing it would remove that setup option for all existing iTop -->
|
||||
<extension_code>itop-kown-error-mgmt</extension_code>
|
||||
<title>Known Errors Management and FAQ</title>
|
||||
<description>Select this option to track "Known Errors" and FAQs in iTop.</description>
|
||||
<modules type="array">
|
||||
<module>itop-faq-light</module>
|
||||
<module>itop-knownerror-mgmt</module>
|
||||
</modules>
|
||||
</choice>
|
||||
<choice>
|
||||
<extension_code>itop-problem-mgmt</extension_code>
|
||||
<title>Problem Management</title>
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
/**
|
||||
* Localized data
|
||||
/**
|
||||
* Spanish Localized data
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
|
||||
*/
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Attachments:TabTitle_Count' => 'Anexos (%1$d)',
|
||||
'Attachments:EmptyTabTitle' => 'Anexos',
|
||||
'Attachments:FieldsetTitle' => 'Anexos',
|
||||
@@ -36,12 +36,12 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Attachment:Max_Ko' => '(Tamaño Máximo de Archivo: %1$s Kb)',
|
||||
'Attachments:NoAttachment' => 'No hay Anexo. ',
|
||||
'Attachments:PreviewNotAvailable' => 'Vista preliminar no disponible para este tipo de Anexo.',
|
||||
'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~',
|
||||
'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached.
|
||||
Either you have pushed an empty file,
|
||||
or ask your iTop administrator if the iTop server disk is full.~~',
|
||||
'Attachments:Render:Icons' => 'Display as icons~~',
|
||||
'Attachments:Render:Table' => 'Display as list~~',
|
||||
'Attachments:Error:FileTooLarge' => 'El archivo es demasiado grande para ser cargado. %1$s',
|
||||
'Attachments:Error:UploadedFileEmpty' => 'El archivo recibido está vacío y no puede ser anexado.
|
||||
Puede ser que haya enviado un archivo vació,
|
||||
o pregunte al administador de iTop si el servidor que ha quedado sin espacio en disco.',
|
||||
'Attachments:Render:Icons' => 'Desplegar como icono',
|
||||
'Attachments:Render:Table' => 'Desplegar como lista',
|
||||
'UI:Attachments:DropYourFileHint' => 'Drop files anywhere in this area~~',
|
||||
));
|
||||
|
||||
@@ -49,41 +49,41 @@ or ask your iTop administrator if the iTop server disk is full.~~',
|
||||
// Class: Attachment
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Class:Attachment' => 'Attachment~~',
|
||||
'Class:Attachment+' => '~~',
|
||||
'Class:Attachment/Attribute:expire' => 'Expire~~',
|
||||
'Class:Attachment/Attribute:expire+' => '~~',
|
||||
'Class:Attachment/Attribute:temp_id' => 'Temporary id~~',
|
||||
'Class:Attachment/Attribute:temp_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_class' => 'Item class~~',
|
||||
'Class:Attachment/Attribute:item_class+' => '~~',
|
||||
'Class:Attachment/Attribute:item_id' => 'Item~~',
|
||||
'Class:Attachment/Attribute:item_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_org_id' => 'Item organization~~',
|
||||
'Class:Attachment/Attribute:item_org_id+' => '~~',
|
||||
'Class:Attachment/Attribute:contents' => 'Contents~~',
|
||||
'Class:Attachment/Attribute:contents+' => '~~',
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:Attachment' => 'Anexo',
|
||||
'Class:Attachment+' => 'Anexo',
|
||||
'Class:Attachment/Attribute:expire' => 'Expira',
|
||||
'Class:Attachment/Attribute:expire+' => '',
|
||||
'Class:Attachment/Attribute:temp_id' => 'Id Temporal',
|
||||
'Class:Attachment/Attribute:temp_id+' => '',
|
||||
'Class:Attachment/Attribute:item_class' => 'Clase de Elemento',
|
||||
'Class:Attachment/Attribute:item_class+' => '',
|
||||
'Class:Attachment/Attribute:item_id' => 'Elemento',
|
||||
'Class:Attachment/Attribute:item_id+' => '',
|
||||
'Class:Attachment/Attribute:item_org_id' => 'Organización de Elemento',
|
||||
'Class:Attachment/Attribute:item_org_id+' => '',
|
||||
'Class:Attachment/Attribute:contents' => 'Contenido',
|
||||
'Class:Attachment/Attribute:contents+' => '',
|
||||
));
|
||||
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Attachments:File:Thumbnail' => 'Icon~~',
|
||||
'Attachments:File:Name' => 'File name~~',
|
||||
'Attachments:File:Date' => 'Upload date~~',
|
||||
'Attachments:File:Uploader' => 'Uploaded by~~',
|
||||
'Attachments:File:Size' => 'Size~~',
|
||||
'Attachments:File:MimeType' => 'Type~~',
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Attachments:File:Thumbnail' => 'Ícono',
|
||||
'Attachments:File:Name' => 'Nombre de Archivo',
|
||||
'Attachments:File:Date' => 'Fecha de Carga',
|
||||
'Attachments:File:Uploader' => 'Cargado por',
|
||||
'Attachments:File:Size' => 'Tamaño',
|
||||
'Attachments:File:MimeType' => 'Tipo',
|
||||
));
|
||||
//
|
||||
// Class: Attachment
|
||||
//
|
||||
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||
'Class:Attachment/Attribute:creation_date' => 'Creation date~~',
|
||||
'Class:Attachment/Attribute:creation_date+' => '~~',
|
||||
'Class:Attachment/Attribute:user_id' => 'User id~~',
|
||||
'Class:Attachment/Attribute:user_id+' => '~~',
|
||||
'Class:Attachment/Attribute:contact_id' => 'Contact id~~',
|
||||
'Class:Attachment/Attribute:contact_id+' => '~~',
|
||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
|
||||
'Class:Attachment/Attribute:creation_date' => 'Fecha de Creación',
|
||||
'Class:Attachment/Attribute:creation_date+' => '',
|
||||
'Class:Attachment/Attribute:user_id' => 'Id del Usuario',
|
||||
'Class:Attachment/Attribute:user_id+' => '',
|
||||
'Class:Attachment/Attribute:contact_id' => 'Id del Contacto',
|
||||
'Class:Attachment/Attribute:contact_id+' => '',
|
||||
));
|
||||
|
||||
@@ -28,6 +28,7 @@ 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\DataTable\DataTableUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\FileSelect\FileSelectUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
|
||||
define('ATTACHMENT_DOWNLOAD_URL', 'pages/ajax.document.php?operation=download_document&class=Attachment&field=contents&id=');
|
||||
@@ -448,10 +449,13 @@ class TableDetailsAttachmentsRenderer extends AbstractAttachmentsRenderer
|
||||
if ($bWithDeleteButton) {
|
||||
$aAttribs['delete'] = array('label' => '', 'description' => '');
|
||||
}
|
||||
|
||||
$oPanel = PanelUIBlockFactory::MakeNeutral('');
|
||||
$oPanel->AddCSSClass('ibo-datatable-panel');
|
||||
$oAttachmentTableBlock = DataTableUIBlockFactory::MakeForStaticData('', $aAttribs, $aData);
|
||||
$oAttachmentTableBlock->AddCSSClass('ibo-attachment--datatable');
|
||||
$this->oPage->AddUiBlock($oAttachmentTableBlock);
|
||||
$oPanel->AddSubBlock($oAttachmentTableBlock);
|
||||
|
||||
$this->oPage->AddUiBlock($oPanel);
|
||||
|
||||
$sTableId = $oAttachmentTableBlock->GetId();
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user