mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 18:48:51 +02:00
Compare commits
234 Commits
3.0.0-beta
...
3.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e0ae67803 | ||
|
|
095c975ec6 | ||
|
|
9e1e5a8a47 | ||
|
|
2c026fa891 | ||
|
|
00d65aeb45 | ||
|
|
5702f603ac | ||
|
|
e3dc1b77cc | ||
|
|
c304f70ff4 | ||
|
|
53fd41e748 | ||
|
|
7577fbb8bf | ||
|
|
0fc912b357 | ||
|
|
c475e66176 | ||
|
|
cd1ba097cb | ||
|
|
fdca4d4cc3 | ||
|
|
4379b4d908 | ||
|
|
5b42f67a99 | ||
|
|
0b9ccc8e67 | ||
|
|
2d98ca2318 | ||
|
|
92add2bbfe | ||
|
|
f15bdb75ca | ||
|
|
a66830de17 | ||
|
|
714294e1b4 | ||
|
|
e3d2c1d761 | ||
|
|
59d95cc14b | ||
|
|
ddc5bbd1bb | ||
|
|
9bbee0d603 | ||
|
|
be1ef5b452 | ||
|
|
cbdc48b7e1 | ||
|
|
bb5679959e | ||
|
|
cdbb4470fc | ||
|
|
252d752bf5 | ||
|
|
99d0c05c1c | ||
|
|
5926e9d110 | ||
|
|
c8dd8c3806 | ||
|
|
70b07721e6 | ||
|
|
bd050dfe69 | ||
|
|
9eb477ce83 | ||
|
|
a742b6c610 | ||
|
|
fcf769666e | ||
|
|
72e628c5d5 | ||
|
|
bf28602ae6 | ||
|
|
b8a0d899f4 | ||
|
|
d335736cfc | ||
|
|
80f7d07378 | ||
|
|
0be6a8aef4 | ||
|
|
0fe857071d | ||
|
|
05981c8af8 | ||
|
|
0abea749fa | ||
|
|
78af6fdc84 | ||
|
|
1e97b5a8c0 | ||
|
|
0214243b63 | ||
|
|
d30871ac59 | ||
|
|
d247ea915d | ||
|
|
0e0aed1ba4 | ||
|
|
bc770ef3d5 | ||
|
|
56a4fb0b42 | ||
|
|
3139628dd8 | ||
|
|
7d9b19cd9e | ||
|
|
b3cb95d2f1 | ||
|
|
a6765cdc3a | ||
|
|
97d52fb539 | ||
|
|
ad936d7a2f | ||
|
|
090eb9a560 | ||
|
|
e4f58b5b98 | ||
|
|
c99a22c9f7 | ||
|
|
29cf20beaf | ||
|
|
e325d535ae | ||
|
|
11cfdb2a17 | ||
|
|
66720b9731 | ||
|
|
234f46cafa | ||
|
|
27c3ce0389 | ||
|
|
9d0e2fa64a | ||
|
|
e211633fed | ||
|
|
29967aa41a | ||
|
|
3ebb1cfc66 | ||
|
|
6c2221b8b6 | ||
|
|
c0b3aed12c | ||
|
|
53efc9f75e | ||
|
|
0d566f2e47 | ||
|
|
b294e3c734 | ||
|
|
6704f9eccf | ||
|
|
90cb6e1d49 | ||
|
|
c0aa4f2d69 | ||
|
|
01984c24c0 | ||
|
|
1da1e0b1bd | ||
|
|
39bcd3e4cd | ||
|
|
156cce6098 | ||
|
|
3130e95f4f | ||
|
|
e28f704f3e | ||
|
|
5c6c59941a | ||
|
|
bc9d47933e | ||
|
|
07a10e4146 | ||
|
|
e8a21870ad | ||
|
|
2e132d5c53 | ||
|
|
3359609349 | ||
|
|
2966759466 | ||
|
|
25395405e5 | ||
|
|
b589e2d001 | ||
|
|
27f3619cf5 | ||
|
|
616fc436d0 | ||
|
|
4f2f765207 | ||
|
|
9931fa1a6b | ||
|
|
a13f2750ea | ||
|
|
243d105f59 | ||
|
|
7783ba570e | ||
|
|
5a9fa2ac32 | ||
|
|
619e3de5a8 | ||
|
|
83064d68c7 | ||
|
|
f3c11e72cf | ||
|
|
c9e887a264 | ||
|
|
49fd482389 | ||
|
|
96de4e1796 | ||
|
|
44f413583c | ||
|
|
91104002ea | ||
|
|
f98ba1594c | ||
|
|
431dc5532b | ||
|
|
df473ae313 | ||
|
|
40ce74cffa | ||
|
|
7598c18ad6 | ||
|
|
d38b655f5f | ||
|
|
f4345ef312 | ||
|
|
13b548e95d | ||
|
|
3a988ab499 | ||
|
|
14a5f87d62 | ||
|
|
8dae459b12 | ||
|
|
8dc10424e8 | ||
|
|
54a6573948 | ||
|
|
1d5e0b6fe9 | ||
|
|
acb8a377dd | ||
|
|
d86f489c89 | ||
|
|
2c154b601d | ||
|
|
7dad079688 | ||
|
|
ace2eb6dab | ||
|
|
25f3c1cbc4 | ||
|
|
8f7e7c136d | ||
|
|
71a7e060e4 | ||
|
|
c450f1d02f | ||
|
|
4431762c10 | ||
|
|
97170892e6 | ||
|
|
5137d634e3 | ||
|
|
146a95baec | ||
|
|
a7ac9126a2 | ||
|
|
ee544b646d | ||
|
|
344cce9fdd | ||
|
|
bfcfcdd4cc | ||
|
|
4410bf7e1f | ||
|
|
d1fda1dbc6 | ||
|
|
5c702be641 | ||
|
|
8a9ad78e9c | ||
|
|
778be8abce | ||
|
|
0760adcef6 | ||
|
|
29ca291860 | ||
|
|
93d0e4ae7c | ||
|
|
61bc07b598 | ||
|
|
00ee36f938 | ||
|
|
1aa5185c93 | ||
|
|
834ac00d37 | ||
|
|
957cb87b8d | ||
|
|
d0813f6607 | ||
|
|
cf4673d284 | ||
|
|
e3422f5fd9 | ||
|
|
d7a0878a39 | ||
|
|
076f0e00a7 | ||
|
|
eb04ecb5b4 | ||
|
|
3323d5532b | ||
|
|
d68d8204f2 | ||
|
|
4a50b95069 | ||
|
|
fd9e4f413c | ||
|
|
dca3fc996c | ||
|
|
c58c1bbf1d | ||
|
|
524919b946 | ||
|
|
e15953524a | ||
|
|
68f3c53faa | ||
|
|
82e3ddaacd | ||
|
|
dfbbee2a1c | ||
|
|
4843545171 | ||
|
|
4e58b8616a | ||
|
|
1a79dcd773 | ||
|
|
a4104d4315 | ||
|
|
4e1db7d7e2 | ||
|
|
8e6379a112 | ||
|
|
da217a1cb3 | ||
|
|
a683634a05 | ||
|
|
2f1b5d2736 | ||
|
|
c9bf784fce | ||
|
|
4ef10ae7c9 | ||
|
|
5174250ff1 | ||
|
|
7b6ac202c6 | ||
|
|
d960183403 | ||
|
|
ece3e0490d | ||
|
|
1562cb1f38 | ||
|
|
11a22abfd5 | ||
|
|
5254c9a633 | ||
|
|
f7a35072f5 | ||
|
|
b5f5780f35 | ||
|
|
f9064084f9 | ||
|
|
67afbd1d8d | ||
|
|
d6b9172e26 | ||
|
|
8e1e71c740 | ||
|
|
ebbf6e56be | ||
|
|
bd67b71f3d | ||
|
|
69ad10785b | ||
|
|
9aead898e2 | ||
|
|
a48ebfefba | ||
|
|
e2a6e6b846 | ||
|
|
7ca689e190 | ||
|
|
d8f36a8aa9 | ||
|
|
e279799bf8 | ||
|
|
a117906ff6 | ||
|
|
c76d4f12fd | ||
|
|
b16529e337 | ||
|
|
67e9212008 | ||
|
|
35b70bfc00 | ||
|
|
418312bca5 | ||
|
|
4748717e50 | ||
|
|
d90b1a3d82 | ||
|
|
76a237aad4 | ||
|
|
3694108f42 | ||
|
|
1a7755365c | ||
|
|
8cf75f826f | ||
|
|
a1da086a64 | ||
|
|
5d1d6d07a6 | ||
|
|
e6a38a8055 | ||
|
|
3b3f1806ce | ||
|
|
e46743af2a | ||
|
|
9048d09bf6 | ||
|
|
3cd03729b9 | ||
|
|
ff760dacbe | ||
|
|
94f662c71a | ||
|
|
c7d87ad5b0 | ||
|
|
ad9726b64c | ||
|
|
e32e275f40 | ||
|
|
195056492e | ||
|
|
5691ca0327 |
@@ -280,7 +280,7 @@ ij_javascript_while_brace_force = always
|
|||||||
ij_javascript_while_on_new_line = false
|
ij_javascript_while_on_new_line = false
|
||||||
ij_javascript_wrap_comments = false
|
ij_javascript_wrap_comments = false
|
||||||
|
|
||||||
[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}]
|
[{*.ctp, *.hphp, *.inc, *.module, *.php, *.php4, *.php5, *.phtml}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
ij_continuation_indent_size = 4
|
ij_continuation_indent_size = 4
|
||||||
ij_smart_tabs = true
|
ij_smart_tabs = true
|
||||||
@@ -289,8 +289,8 @@ ij_php_align_assignments = false
|
|||||||
ij_php_align_class_constants = false
|
ij_php_align_class_constants = false
|
||||||
ij_php_align_group_field_declarations = false
|
ij_php_align_group_field_declarations = false
|
||||||
ij_php_align_inline_comments = false
|
ij_php_align_inline_comments = false
|
||||||
ij_php_align_key_value_pairs = false
|
ij_php_align_key_value_pairs = true
|
||||||
ij_php_align_multiline_array_initializer_expression = false
|
ij_php_align_multiline_array_initializer_expression = true
|
||||||
ij_php_align_multiline_binary_operation = false
|
ij_php_align_multiline_binary_operation = false
|
||||||
ij_php_align_multiline_chained_methods = false
|
ij_php_align_multiline_chained_methods = false
|
||||||
ij_php_align_multiline_extends_list = false
|
ij_php_align_multiline_extends_list = false
|
||||||
|
|||||||
@@ -10,10 +10,6 @@
|
|||||||
* `curl -L -o /usr/bin/jq.exe https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe`
|
* `curl -L -o /usr/bin/jq.exe https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe`
|
||||||
* this is a Windows port : https://stedolan.github.io/jq/
|
* this is a Windows port : https://stedolan.github.io/jq/
|
||||||
*
|
*
|
||||||
* Known bug on Windows :
|
|
||||||
* Licenses added from Composer contains a path in the product node (N°3870)
|
|
||||||
* `<product scope="lib">C:\Dev\wamp64\www\itop-dev\.make\license/../..//lib/symfony/console</product>`
|
|
||||||
*
|
|
||||||
* Licenses sources :
|
* Licenses sources :
|
||||||
* * `composer licenses --format json` (see https://getcomposer.org/doc/03-cli.md#licenses)
|
* * `composer licenses --format json` (see https://getcomposer.org/doc/03-cli.md#licenses)
|
||||||
* * keep every existing nodes with `/licenses/license[11]/product/@scope` not in ['lib', 'datamodels']
|
* * keep every existing nodes with `/licenses/license[11]/product/@scope` not in ['lib', 'datamodels']
|
||||||
@@ -70,39 +66,83 @@ function get_license_nodes($file_path)
|
|||||||
$xp = new DOMXPath($dom);
|
$xp = new DOMXPath($dom);
|
||||||
|
|
||||||
$licenseList = $xp->query('/licenses/license');
|
$licenseList = $xp->query('/licenses/license');
|
||||||
$licenses = iterator_to_array($licenseList);
|
$licenses = iterator_to_array($licenseList);
|
||||||
|
|
||||||
usort($licenses, 'sort_by_product');
|
usort($licenses, 'sort_by_product');
|
||||||
|
|
||||||
return $licenses;
|
return $licenses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @noinspection SuspiciousAssignmentsInspection */
|
||||||
|
function fix_product_name(DOMNode &$oProductNode)
|
||||||
|
{
|
||||||
|
$sProductNameOrig = $oProductNode->nodeValue;
|
||||||
|
|
||||||
|
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//lib/symfony/polyfill-ctype`
|
||||||
|
$sProductNameFixed = remove_dir_from_string($sProductNameOrig, 'lib/');
|
||||||
|
|
||||||
|
// sample : `C:\Dev\wamp64\www\itop-27\.make\license/../..//datamodels/2.x/authent-cas/vendor/apereo/phpcas`
|
||||||
|
$sProductNameFixed = remove_dir_from_string($sProductNameFixed, 'vendor/');
|
||||||
|
|
||||||
|
$oProductNode->nodeValue = $sProductNameFixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_dir_from_string($sString, $sNeedle)
|
||||||
|
{
|
||||||
|
if (strpos($sString, $sNeedle) === false) {
|
||||||
|
return $sString;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sStringTmp = strstr($sString, $sNeedle);
|
||||||
|
$sStringFixed = str_replace($sNeedle, '', $sStringTmp);
|
||||||
|
|
||||||
|
// DEBUG trace O:)
|
||||||
|
// echo "$sNeedle = $sString => $sStringFixed\n";
|
||||||
|
|
||||||
|
return $sStringFixed;
|
||||||
|
}
|
||||||
|
|
||||||
$old_licenses = get_license_nodes($xmlFilePath);
|
$old_licenses = get_license_nodes($xmlFilePath);
|
||||||
|
|
||||||
//generate file with updated licenses
|
//generate file with updated licenses
|
||||||
$generated_license_file_path = __DIR__."/provfile.xml";
|
$generated_license_file_path = __DIR__."/provfile.xml";
|
||||||
exec("bash " . __DIR__ . "/gen-community-license.sh $iTopFolder > ". $generated_license_file_path);
|
echo "- Generating licences...";
|
||||||
|
exec("bash ".__DIR__."/gen-community-license.sh $iTopFolder > ".$generated_license_file_path);
|
||||||
|
echo "OK!\n";
|
||||||
|
|
||||||
|
echo "- Get licenses nodes...";
|
||||||
$new_licenses = get_license_nodes($generated_license_file_path);
|
$new_licenses = get_license_nodes($generated_license_file_path);
|
||||||
exec("rm -f ". $generated_license_file_path);
|
unlink($generated_license_file_path);
|
||||||
|
|
||||||
foreach ($old_licenses as $b) {
|
foreach ($old_licenses as $b) {
|
||||||
$aProductNode = get_product_node($b);
|
$aProductNode = get_product_node($b);
|
||||||
|
|
||||||
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels" )
|
if (get_scope($aProductNode) !== "lib" && get_scope($aProductNode) !== "datamodels") {
|
||||||
{
|
|
||||||
$new_licenses[] = $b;
|
$new_licenses[] = $b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
usort($new_licenses, 'sort_by_product');
|
usort($new_licenses, 'sort_by_product');
|
||||||
|
echo "OK!\n";
|
||||||
|
|
||||||
|
echo "- Overwritting Combodo license file...";
|
||||||
$new_dom = new DOMDocument("1.0");
|
$new_dom = new DOMDocument("1.0");
|
||||||
$new_dom->formatOutput = true;
|
$new_dom->formatOutput = true;
|
||||||
$root = $new_dom->createElement("licenses");
|
$root = $new_dom->createElement("licenses");
|
||||||
$new_dom->appendChild($root);
|
$new_dom->appendChild($root);
|
||||||
|
|
||||||
foreach ($new_licenses as $b) {
|
foreach ($new_licenses as $b) {
|
||||||
$node = $new_dom->importNode($b,true);
|
$node = $new_dom->importNode($b, true);
|
||||||
$root->appendChild($new_dom->importNode($b,true));
|
|
||||||
|
// N°3870 fix when running script in Windows
|
||||||
|
// fix should be in gen-community-license.sh but it is easier to do it here !
|
||||||
|
if (strncasecmp(PHP_OS, 'WIN', 3) === 0) {
|
||||||
|
$oProductNodeOrig = get_product_node($node);
|
||||||
|
fix_product_name($oProductNodeOrig);
|
||||||
|
}
|
||||||
|
|
||||||
|
$root->appendChild($node);
|
||||||
}
|
}
|
||||||
|
|
||||||
$new_dom->save($xmlFilePath);
|
$new_dom->save($xmlFilePath);
|
||||||
|
echo "OK!\n";
|
||||||
@@ -202,8 +202,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
|||||||
$sParams .= $sName.'='.urlencode($value).'&'; // Always add a trailing &
|
$sParams .= $sName.'='.urlencode($value).'&'; // Always add a trailing &
|
||||||
}
|
}
|
||||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink().'&a=1';
|
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink().'&a=1';
|
||||||
$oPage->add_script(
|
$oPage->add_early_script(<<<JS
|
||||||
<<<EOF
|
|
||||||
if (!sessionStorage.getItem('$sSessionStorageKey'))
|
if (!sessionStorage.getItem('$sSessionStorageKey'))
|
||||||
{
|
{
|
||||||
sessionStorage.setItem('$sSessionStorageKey', 1);
|
sessionStorage.setItem('$sSessionStorageKey', 1);
|
||||||
@@ -213,7 +212,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
sessionStorage.removeItem('$sSessionStorageKey');
|
sessionStorage.removeItem('$sSessionStorageKey');
|
||||||
}
|
}
|
||||||
EOF
|
JS
|
||||||
);
|
);
|
||||||
|
|
||||||
$oObj->Reload();
|
$oObj->Reload();
|
||||||
@@ -662,7 +661,7 @@ HTML
|
|||||||
}
|
}
|
||||||
|
|
||||||
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sTargetClass, false));
|
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sTargetClass, false));
|
||||||
$oClassIcon->SetDescription($oAttDef->GetDescription())->AddCSSClass('ibo-blocklist--medallion');
|
$oClassIcon->SetDescription($oAttDef->GetDescription())->AddCSSClass('ibo-block-list--medallion');
|
||||||
$oPage->AddUiBlock($oClassIcon);
|
$oPage->AddUiBlock($oClassIcon);
|
||||||
|
|
||||||
$sDisplayValue = ''; // not used
|
$sDisplayValue = ''; // not used
|
||||||
@@ -740,7 +739,7 @@ HTML
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sTargetClass, false));
|
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sTargetClass, false));
|
||||||
$oClassIcon->SetDescription($oAttDef->GetDescription())->AddCSSClass('ibo-blocklist--medallion');
|
$oClassIcon->SetDescription($oAttDef->GetDescription())->AddCSSClass('ibo-block-list--medallion');
|
||||||
$oPage->AddUiBlock($oClassIcon);
|
$oPage->AddUiBlock($oClassIcon);
|
||||||
$oBlock = new DisplayBlock($oLinkSet->GetFilter(), 'list', false);
|
$oBlock = new DisplayBlock($oLinkSet->GetFilter(), 'list', false);
|
||||||
$oBlock->Display($oPage, 'rel_'.$sAttCode, $aParams);
|
$oBlock->Display($oPage, 'rel_'.$sAttCode, $aParams);
|
||||||
@@ -797,7 +796,7 @@ HTML
|
|||||||
|
|
||||||
foreach($aNotificationClasses as $sNotifClass) {
|
foreach($aNotificationClasses as $sNotifClass) {
|
||||||
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sNotifClass, false));
|
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sNotifClass, false));
|
||||||
$oClassIcon->SetDescription(MetaModel::GetName($sNotifClass))->AddCSSClass('ibo-blocklist--medallion');
|
$oClassIcon->SetDescription(MetaModel::GetName($sNotifClass))->AddCSSClass('ibo-block-list--medallion');
|
||||||
$oPage->AddUiBlock($oClassIcon);
|
$oPage->AddUiBlock($oClassIcon);
|
||||||
|
|
||||||
$oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false);
|
$oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false);
|
||||||
@@ -1036,9 +1035,32 @@ HTML
|
|||||||
*/
|
*/
|
||||||
public function DisplayDetails(WebPage $oPage, $bEditMode = false, $sMode = self::ENUM_OBJECT_MODE_VIEW)
|
public function DisplayDetails(WebPage $oPage, $bEditMode = false, $sMode = self::ENUM_OBJECT_MODE_VIEW)
|
||||||
{
|
{
|
||||||
|
// 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;
|
||||||
|
|
||||||
$sClass = get_class($this);
|
$sClass = get_class($this);
|
||||||
$iKey = $this->GetKey();
|
$iKey = $this->GetKey();
|
||||||
|
|
||||||
|
if ($sMode === static::ENUM_OBJECT_MODE_VIEW)
|
||||||
|
{
|
||||||
|
// The concurrent access lock makes sense only for already existing objects
|
||||||
|
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
|
||||||
|
if ($LockEnabled)
|
||||||
|
{
|
||||||
|
$aLockInfo = iTopOwnershipLock::IsLocked($sClass, $iKey);
|
||||||
|
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();
|
||||||
|
$bBlockReentrance = true;
|
||||||
|
self::ReloadAndDisplay($oPage, $this, array('operation' => 'details'));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Object's details
|
// Object's details
|
||||||
$oObjectDetails = ObjectFactory::MakeDetails($this);
|
$oObjectDetails = ObjectFactory::MakeDetails($this);
|
||||||
|
|
||||||
@@ -1123,25 +1145,11 @@ HTML
|
|||||||
*/
|
*/
|
||||||
public static function GetDisplaySetForPrinting(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
|
public static function GetDisplaySetForPrinting(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
|
||||||
{
|
{
|
||||||
$sTableId = isset($aExtraParams['table_id']) ? $aExtraParams['table_id'] : null;
|
$sTableId = isset($aExtraParams['table_id']) ? $aExtraParams['table_id'] : utils::GetUniqueId();;
|
||||||
|
$aExtraParams['view_link'] = true;
|
||||||
$bViewLink = true;
|
$aExtraParams['select_mode'] = 'none';
|
||||||
$sSelectMode = 'none';
|
|
||||||
$iListId = $sTableId;
|
|
||||||
$sClassAlias = $oSet->GetClassAlias();
|
|
||||||
$sClassName = $oSet->GetClass();
|
|
||||||
$sZListName = 'list';
|
|
||||||
$aClassAliases = array($sClassAlias => $sClassName);
|
|
||||||
$aList = cmdbAbstractObject::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName));
|
|
||||||
|
|
||||||
$oDataTable = new PrintableDataTable($iListId, $oSet, $aClassAliases, $sTableId);
|
|
||||||
$oSettings = DataTableSettings::GetDataModelSettings($aClassAliases, $bViewLink, array($sClassAlias => $aList));
|
|
||||||
$oSettings->iDefaultPageSize = 0;
|
|
||||||
$oSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName);
|
|
||||||
|
|
||||||
return $oDataTable->Display($oPage, $oSettings, false /* $bDisplayMenu */, $sSelectMode, $bViewLink,
|
|
||||||
$aExtraParams);
|
|
||||||
|
|
||||||
|
return DataTableUIBlockFactory::MakeForObject($oPage, $sTableId, $oSet, $aExtraParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2403,6 +2411,7 @@ HTML;
|
|||||||
case 'CustomFields':
|
case 'CustomFields':
|
||||||
$sHTMLValue .= '<div id="'.$iId.'_console_form">';
|
$sHTMLValue .= '<div id="'.$iId.'_console_form">';
|
||||||
$sHTMLValue .= '<div id="'.$iId.'_field_set">';
|
$sHTMLValue .= '<div id="'.$iId.'_field_set">';
|
||||||
|
$sHTMLValue .= '</div></div>';
|
||||||
$sHTMLValue .= '<div>'.$sReloadSpan.'</div>'; // No validation span for this one: it does handle its own validation!
|
$sHTMLValue .= '<div>'.$sReloadSpan.'</div>'; // No validation span for this one: it does handle its own validation!
|
||||||
$sHTMLValue .= "<input name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" type=\"hidden\" id=\"$iId\" value=\"\"/>\n";
|
$sHTMLValue .= "<input name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" type=\"hidden\" id=\"$iId\" value=\"\"/>\n";
|
||||||
|
|
||||||
@@ -2533,14 +2542,13 @@ EOF
|
|||||||
$sDisplayValueForHtml = utils::EscapeHtml($sDisplayValue);
|
$sDisplayValueForHtml = utils::EscapeHtml($sDisplayValue);
|
||||||
|
|
||||||
// Adding tooltip so we can read the whole value when its very long (eg. URL)
|
// Adding tooltip so we can read the whole value when its very long (eg. URL)
|
||||||
$sTip = '';
|
$sTip = '';
|
||||||
if (!empty($sDisplayValue)) {
|
if (!empty($sDisplayValue)) {
|
||||||
$sTip = 'data-tooltip-content="'.$sDisplayValueForHtml.'"';
|
$sTip = 'data-tooltip-content="'.$sDisplayValueForHtml.'"';
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(<<<JS
|
||||||
<<<EOF
|
|
||||||
$('#{$iId}').on('keyup', function(evt, sFormId){
|
$('#{$iId}').on('keyup', function(evt, sFormId){
|
||||||
var sVal = $('#{$iId}').val();
|
let sVal = $('#{$iId}').val();
|
||||||
var oTippy = this._tippy;
|
const oTippy = this._tippy;
|
||||||
|
|
||||||
if(sVal === '')
|
if(sVal === '')
|
||||||
{
|
{
|
||||||
@@ -2553,7 +2561,7 @@ EOF
|
|||||||
}
|
}
|
||||||
oTippy.setContent(sVal);
|
oTippy.setContent(sVal);
|
||||||
});
|
});
|
||||||
EOF
|
JS
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3096,55 +3104,45 @@ EOF
|
|||||||
|
|
||||||
// The list of candidate fields is made of the ordered list of "details" attributes + other attributes
|
// The list of candidate fields is made of the ordered list of "details" attributes + other attributes
|
||||||
$aAttributes = array();
|
$aAttributes = array();
|
||||||
foreach($this->FlattenZList(MetaModel::GetZListItems($sClass, 'details')) as $sAttCode)
|
foreach ($this->FlattenZList(MetaModel::GetZListItems($sClass, 'details')) as $sAttCode) {
|
||||||
{
|
|
||||||
$aAttributes[$sAttCode] = true;
|
$aAttributes[$sAttCode] = true;
|
||||||
}
|
}
|
||||||
foreach(MetaModel::GetAttributesList($sClass) as $sAttCode)
|
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) {
|
||||||
{
|
if (!array_key_exists($sAttCode, $aAttributes)) {
|
||||||
if (!array_key_exists($sAttCode, $aAttributes))
|
|
||||||
{
|
|
||||||
$aAttributes[$sAttCode] = true;
|
$aAttributes[$sAttCode] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Order the fields based on their dependencies, set the fields for which there is only one possible value
|
// Order the fields based on their dependencies, set the fields for which there is only one possible value
|
||||||
// and perform this in the order of dependencies to avoid dead-ends
|
// and perform this in the order of dependencies to avoid dead-ends
|
||||||
$aDeps = array();
|
$aDeps = array();
|
||||||
foreach($aAttributes as $sAttCode => $trash)
|
foreach ($aAttributes as $sAttCode => $trash) {
|
||||||
{
|
|
||||||
$aDeps[$sAttCode] = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode);
|
$aDeps[$sAttCode] = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode);
|
||||||
}
|
}
|
||||||
$aList = $this->OrderDependentFields($aDeps);
|
$aList = $this->OrderDependentFields($aDeps);
|
||||||
|
|
||||||
foreach($aList as $sAttCode)
|
$bExistFieldToDisplay = false;
|
||||||
{
|
foreach ($aList as $sAttCode) {
|
||||||
// Consider only the "expected" fields for the target state
|
// Consider only the "expected" fields for the target state
|
||||||
if (array_key_exists($sAttCode, $aExpectedAttributes))
|
if (array_key_exists($sAttCode, $aExpectedAttributes)) {
|
||||||
{
|
|
||||||
$iExpectCode = $aExpectedAttributes[$sAttCode];
|
$iExpectCode = $aExpectedAttributes[$sAttCode];
|
||||||
// Prompt for an attribute if
|
// Prompt for an attribute if
|
||||||
// - the attribute must be changed or must be displayed to the user for confirmation
|
// - the attribute must be changed or must be displayed to the user for confirmation
|
||||||
// - or the field is mandatory and currently empty
|
// - or the field is mandatory and currently empty
|
||||||
if (($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) ||
|
if (($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) ||
|
||||||
(($iExpectCode & OPT_ATT_MANDATORY) && ($this->Get($sAttCode) == '')))
|
(($iExpectCode & OPT_ATT_MANDATORY) && ($this->Get($sAttCode) == ''))) {
|
||||||
{
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||||
$aArgs = array('this' => $this);
|
$aArgs = array('this' => $this);
|
||||||
// If the field is mandatory, set it to the only possible value
|
// If the field is mandatory, set it to the only possible value
|
||||||
if ((!$oAttDef->IsNullAllowed()) || ($iExpectCode & OPT_ATT_MANDATORY))
|
if ((!$oAttDef->IsNullAllowed()) || ($iExpectCode & OPT_ATT_MANDATORY)) {
|
||||||
{
|
if ($oAttDef->IsExternalKey()) {
|
||||||
if ($oAttDef->IsExternalKey())
|
|
||||||
{
|
|
||||||
/** @var DBObjectSet $oAllowedValues */
|
/** @var DBObjectSet $oAllowedValues */
|
||||||
$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs, '',
|
$oAllowedValues = MetaModel::GetAllowedValuesAsObjectSet($sClass, $sAttCode, $aArgs, '',
|
||||||
$this->Get($sAttCode));
|
$this->Get($sAttCode));
|
||||||
if ($oAllowedValues->CountWithLimit(2) == 1)
|
if ($oAllowedValues->CountWithLimit(2) == 1) {
|
||||||
{
|
|
||||||
$oRemoteObj = $oAllowedValues->Fetch();
|
$oRemoteObj = $oAllowedValues->Fetch();
|
||||||
$this->Set($sAttCode, $oRemoteObj->GetKey());
|
$this->Set($sAttCode, $oRemoteObj->GetKey());
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
{
|
{
|
||||||
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs);
|
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs);
|
||||||
if (is_array($aAllowedValues) && count($aAllowedValues) == 1)
|
if (is_array($aAllowedValues) && count($aAllowedValues) == 1)
|
||||||
@@ -3180,8 +3178,7 @@ EOF
|
|||||||
$bExcludeRawValue = false;
|
$bExcludeRawValue = false;
|
||||||
foreach (static::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
|
foreach (static::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
|
||||||
{
|
{
|
||||||
if (is_a($sAttDefClass, $sAttDefClassToExclude, true))
|
if (is_a($sAttDefClass, $sAttDefClassToExclude, true)) {
|
||||||
{
|
|
||||||
$bExcludeRawValue = true;
|
$bExcludeRawValue = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3191,88 +3188,105 @@ EOF
|
|||||||
$aDetails[] = $aAttrib;
|
$aDetails[] = $aAttrib;
|
||||||
$aFieldsMap[$sAttCode] = 'att_'.$iFieldIndex;
|
$aFieldsMap[$sAttCode] = 'att_'.$iFieldIndex;
|
||||||
$iFieldIndex++;
|
$iFieldIndex++;
|
||||||
|
$bExistFieldToDisplay = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$oPage->set_title($sActionLabel);
|
if ($bExistFieldToDisplay) {
|
||||||
$oPage->add(<<<HTML
|
$oPage->set_title($sActionLabel);
|
||||||
<!-- Beginning of object-transition -->
|
$oPage->add(<<<HTML
|
||||||
<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">
|
<!-- 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">
|
||||||
HTML
|
HTML
|
||||||
);
|
);
|
||||||
|
|
||||||
// Page title and subtitles
|
// Page title and subtitles
|
||||||
$oPage->AddUiBlock(TitleUIBlockFactory::MakeForPage($sActionLabel.' - '.$this->GetName()));
|
$oPage->AddUiBlock(TitleUIBlockFactory::MakeForPage($sActionLabel.' - '.$this->GetRawName()));
|
||||||
if (!empty($sActionDetails)) {
|
if (!empty($sActionDetails)) {
|
||||||
$oPage->AddUiBlock(TitleUIBlockFactory::MakeForPage($sActionDetails));
|
$oPage->AddUiBlock(TitleUIBlockFactory::MakeForPage($sActionDetails));
|
||||||
}
|
}
|
||||||
|
|
||||||
$oFormContainer = new UIContentBlock(null, ['ibo-wizard-container']);
|
$oFormContainer = new UIContentBlock(null, ['ibo-wizard-container']);
|
||||||
$oPage->AddUiBlock($oFormContainer);
|
$oPage->AddUiBlock($oFormContainer);
|
||||||
$oForm = new Combodo\iTop\Application\UI\Base\Component\Form\Form('apply_stimulus');
|
$oForm = new Combodo\iTop\Application\UI\Base\Component\Form\Form('apply_stimulus');
|
||||||
$oFormContainer->AddSubBlock($oForm);
|
$oFormContainer->AddSubBlock($oForm);
|
||||||
|
|
||||||
$oForm->SetOnSubmitJsCode("return OnSubmit('apply_stimulus');")
|
$oForm->SetOnSubmitJsCode("return OnSubmit('apply_stimulus');")
|
||||||
->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $this->GetKey(), 'id'))
|
->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $this->GetKey(), 'id'))
|
||||||
->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass))
|
->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass))
|
||||||
->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', 'apply_stimulus'))
|
->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', 'apply_stimulus'))
|
||||||
->AddSubBlock(InputUIBlockFactory::MakeForHidden('stimulus', $sStimulus))
|
->AddSubBlock(InputUIBlockFactory::MakeForHidden('stimulus', $sStimulus))
|
||||||
->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
|
->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
|
||||||
|
|
||||||
if ($sOwnershipToken !== null) {
|
if ($sOwnershipToken !== null) {
|
||||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('ownership_token', utils::HtmlEntities($sOwnershipToken)));
|
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('ownership_token', utils::HtmlEntities($sOwnershipToken)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Remove the table is we want fields to occupy the whole width of the container
|
// Note: Remove the table is we want fields to occupy the whole width of the container
|
||||||
$oForm->AddHtml('<table><tr><td>');
|
$oForm->AddHtml('<table><tr><td>');
|
||||||
$oForm->AddHtml($oPage->GetDetails($aDetails));
|
$oForm->AddHtml($oPage->GetDetails($aDetails));
|
||||||
$oForm->AddHtml('</td></tr></table>');
|
$oForm->AddHtml('</td></tr></table>');
|
||||||
|
|
||||||
$oAppContext = new ApplicationContext();
|
$oAppContext = new ApplicationContext();
|
||||||
$oForm->AddHtml($oAppContext->GetForForm());
|
$oForm->AddHtml($oAppContext->GetForForm());
|
||||||
|
|
||||||
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel');
|
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel');
|
||||||
$oCancelButton->SetOnClickJsCode("BackToDetails('{$sClass}', '{$this->GetKey()}', '', '{$sOwnershipToken}');");
|
$oCancelButton->SetOnClickJsCode("BackToDetails('{$sClass}', '{$this->GetKey()}', '', '{$sOwnershipToken}');");
|
||||||
$oForm->AddSubBlock($oCancelButton);
|
$oForm->AddSubBlock($oCancelButton);
|
||||||
|
|
||||||
$oSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction($sActionLabel, 'submit', 'submit', true);
|
$oSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction($sActionLabel, 'submit', 'submit', true);
|
||||||
$oForm->AddSubBlock($oSubmitButton);
|
$oForm->AddSubBlock($oSubmitButton);
|
||||||
|
|
||||||
$oPage->add(<<<HTML
|
$oPage->add(<<<HTML
|
||||||
<!-- End of object-transition -->
|
<!-- End of object-transition -->
|
||||||
</div>
|
</div>
|
||||||
HTML
|
HTML
|
||||||
);
|
);
|
||||||
|
|
||||||
$iFieldsCount = count($aFieldsMap);
|
$iFieldsCount = count($aFieldsMap);
|
||||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||||
|
|
||||||
$oPage->add_script(
|
$oPage->add_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
// Initializes the object once at the beginning of the page...
|
// Initializes the object once at the beginning of the page...
|
||||||
var oWizardHelper = new WizardHelper('$sClass', '', '$sTargetState', '{$this->GetState()}', '$sStimulus');
|
var oWizardHelper = new WizardHelper('$sClass', '', '$sTargetState', '{$this->GetState()}', '$sStimulus');
|
||||||
oWizardHelper.SetFieldsMap($sJsonFieldsMap);
|
oWizardHelper.SetFieldsMap($sJsonFieldsMap);
|
||||||
oWizardHelper.SetFieldsCount($iFieldsCount);
|
oWizardHelper.SetFieldsCount($iFieldsCount);
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
$sJSToken = json_encode($sOwnershipToken);
|
$sJSToken = json_encode($sOwnershipToken);
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
// Starts the validation when the page is ready
|
// Starts the validation when the page is ready
|
||||||
CheckFields('apply_stimulus', false);
|
CheckFields('apply_stimulus', false);
|
||||||
$(window).on('unload', function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
|
$(window).on('unload', function() { return OnUnload('$iTransactionId', '$sClass', $iKey, $sJSToken) } );
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($sOwnershipToken !== null)
|
if ($sOwnershipToken !== null) {
|
||||||
{
|
$this->GetOwnershipJSHandler($oPage, $sOwnershipToken);
|
||||||
$this->GetOwnershipJSHandler($oPage, $sOwnershipToken);
|
}
|
||||||
|
|
||||||
|
// Note: This part (inline images activation) is duplicated in self::DisplayModifyForm and several other places. Maybe it should be refactored so it automatically activates when an HTML field is present, or be an option of the attribute. See bug N°1240.
|
||||||
|
$sTempId = utils::GetUploadTempId($iTransactionId);
|
||||||
|
$oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId));
|
||||||
|
} else {
|
||||||
|
//we can directly apply the stimuli
|
||||||
|
$bApplyStimulus = $this->ApplyStimulus($sStimulus); // will write the object in the DB
|
||||||
|
if (!$bApplyStimulus) {
|
||||||
|
throw new ApplicationException(Dict::S('UI:FailedToApplyStimuli'));
|
||||||
|
} else {
|
||||||
|
if ($sOwnershipToken !== null) {
|
||||||
|
// Release the concurrent lock, if any
|
||||||
|
iTopOwnershipLock::ReleaseLock($sClass, $iKey, $sOwnershipToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: This part (inline images activation) is duplicated in self::DisplayModifyForm and several other places. Maybe it should be refactored so it automatically activates when an HTML field is present, or be an option of the attribute. See bug N°1240.
|
return false;
|
||||||
$sTempId = utils::GetUploadTempId($iTransactionId);
|
|
||||||
$oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function ProcessZlist($aList, $aDetails, $sCurrentTab, $sCurrentCol, $sCurrentSet)
|
public static function ProcessZlist($aList, $aDetails, $sCurrentTab, $sCurrentCol, $sCurrentSet)
|
||||||
@@ -3587,7 +3601,7 @@ HTML;
|
|||||||
* @api
|
* @api
|
||||||
* @overwritable-hook
|
* @overwritable-hook
|
||||||
*
|
*
|
||||||
* @param $sFinalClass string The actual class of the objects for which to display the menu
|
* @param string $sFinalClass The actual class of the objects for which to display the menu
|
||||||
*
|
*
|
||||||
* @return array the list of menu codes (i.e dictionary entries) that can be displayed as shortcuts next to the
|
* @return array the list of menu codes (i.e dictionary entries) that can be displayed as shortcuts next to the
|
||||||
* actions menu
|
* actions menu
|
||||||
@@ -5343,6 +5357,20 @@ EOF
|
|||||||
$sJSOk = json_encode(Dict::S('UI:Button:Ok'));
|
$sJSOk = json_encode(Dict::S('UI:Button:Ok'));
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<JS
|
<<<JS
|
||||||
|
// Prepare reusable modal
|
||||||
|
const oOwnershipLockModal = $('<div></div>').dialog({
|
||||||
|
title: $sJSTitle,
|
||||||
|
modal: true,
|
||||||
|
autoOpen: false,
|
||||||
|
minWidth: 600,
|
||||||
|
buttons:[{
|
||||||
|
text: $sJSOk,
|
||||||
|
class: 'ibo-is-alternative',
|
||||||
|
click: function() { $(this).dialog('close'); }
|
||||||
|
}],
|
||||||
|
close: function() { $(this).dialog('close'); }
|
||||||
|
});
|
||||||
|
// Start periodic handler
|
||||||
let hOwnershipLockHandlerInterval = window.setInterval(function() {
|
let hOwnershipLockHandlerInterval = window.setInterval(function() {
|
||||||
if (window.bInSubmit || window.bInCancel) return;
|
if (window.bInSubmit || window.bInCancel) return;
|
||||||
|
|
||||||
@@ -5352,18 +5380,8 @@ EOF
|
|||||||
if ($('.lock_owned').length == 0)
|
if ($('.lock_owned').length == 0)
|
||||||
{
|
{
|
||||||
$('.ui-layout-content').prepend('<div class="header_message message_error lock_owned">'+data.message+'</div>');
|
$('.ui-layout-content').prepend('<div class="header_message message_error lock_owned">'+data.message+'</div>');
|
||||||
$('<div>'+data.popup_message+'</div>').dialog({
|
oOwnershipLockModal.text(data.popup_message);
|
||||||
title: $sJSTitle,
|
oOwnershipLockModal.dialog('open');
|
||||||
modal: true,
|
|
||||||
autoOpen: true,
|
|
||||||
minWidth: 600,
|
|
||||||
buttons:[{
|
|
||||||
text: {$sJSOk},
|
|
||||||
class: 'ibo-is-alternative',
|
|
||||||
click: function() { $(this).dialog('close'); }
|
|
||||||
}],
|
|
||||||
close: function() { $(this).remove(); }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
$('.object-details form .ibo-toolbar .ibo-button:not([name="cancel"])').prop('disabled', true);
|
$('.object-details form .ibo-toolbar .ibo-button:not([name="cancel"])').prop('disabled', true);
|
||||||
clearInterval(hOwnershipLockHandlerInterval);
|
clearInterval(hOwnershipLockHandlerInterval);
|
||||||
@@ -5373,18 +5391,8 @@ EOF
|
|||||||
if ($('.lock_owned').length == 0)
|
if ($('.lock_owned').length == 0)
|
||||||
{
|
{
|
||||||
$('.ui-layout-content').prepend('<div class="header_message message_error lock_owned">'+data.message+'</div>');
|
$('.ui-layout-content').prepend('<div class="header_message message_error lock_owned">'+data.message+'</div>');
|
||||||
$('<div>'+data.popup_message+'</div>').dialog({
|
oOwnershipLockModal.text(data.popup_message);
|
||||||
title: $sJSTitle,
|
oOwnershipLockModal.dialog('open');
|
||||||
modal: true,
|
|
||||||
autoOpen: true,
|
|
||||||
minWidth: 600,
|
|
||||||
buttons:[{
|
|
||||||
text: $sJSOk,
|
|
||||||
class: 'ibo-is-alternative',
|
|
||||||
click: function() { $(this).dialog('close'); }
|
|
||||||
}],
|
|
||||||
close: function() { $(this).remove(); }
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
$('.object-details form .ibo-toolbar .ibo-button:not([name="cancel"])').prop('disabled', true);
|
$('.object-details form .ibo-toolbar .ibo-button:not([name="cancel"])').prop('disabled', true);
|
||||||
clearInterval(hOwnershipLockHandlerInterval);
|
clearInterval(hOwnershipLockHandlerInterval);
|
||||||
|
|||||||
@@ -906,18 +906,22 @@ class DashletObjectList extends Dashlet
|
|||||||
$sShowMenu = $this->aProperties['menu'] ? '1' : '0';
|
$sShowMenu = $this->aProperties['menu'] ? '1' : '0';
|
||||||
$oFilter = $this->GetDBSearch($aExtraParams);
|
$oFilter = $this->GetDBSearch($aExtraParams);
|
||||||
$sClass = $oFilter->GetClass();
|
$sClass = $oFilter->GetClass();
|
||||||
$oPanel = PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle))
|
//$oPanel = PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle))
|
||||||
->AddCSSClass('ibo-datatable-panel');
|
// ->AddCSSClass('ibo-datatable-panel');
|
||||||
|
|
||||||
$oBlock = new DisplayBlock($oFilter, 'list');
|
$oBlock = new DisplayBlock($oFilter, 'list');
|
||||||
$aParams = array(
|
$aParams = array(
|
||||||
'menu' => $sShowMenu,
|
'menu' => $sShowMenu,
|
||||||
'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId,
|
'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId,
|
||||||
'surround_with_panel' => false,
|
'surround_with_panel' => true,
|
||||||
'max_height' => '500px',
|
'max_height' => '500px',
|
||||||
|
"panel_title" => Dict::S($sTitle),
|
||||||
|
"panel_class" => $sClass,
|
||||||
);
|
);
|
||||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
||||||
$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
//$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||||
|
|
||||||
|
$oPanel = $oBlock->GetDisplay($oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||||
|
|
||||||
return $oPanel;
|
return $oPanel;
|
||||||
}
|
}
|
||||||
@@ -984,6 +988,7 @@ HTML;
|
|||||||
|
|
||||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $this->aProperties['query']);
|
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $this->aProperties['query']);
|
||||||
$oField->SetMandatory();
|
$oField->SetMandatory();
|
||||||
|
$oField->AddCSSClass("ibo-queryoql");
|
||||||
$oForm->AddField($oField);
|
$oForm->AddField($oField);
|
||||||
|
|
||||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||||
@@ -1020,6 +1025,7 @@ HTML;
|
|||||||
|
|
||||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $sOQL);
|
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $sOQL);
|
||||||
$oField->SetMandatory();
|
$oField->SetMandatory();
|
||||||
|
$oField->AddCSSClass("ibo-queryoql");
|
||||||
$oForm->AddField($oField);
|
$oForm->AddField($oField);
|
||||||
|
|
||||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||||
@@ -1256,15 +1262,21 @@ abstract class DashletGroupBy extends Dashlet
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$oPanel = PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle));
|
//$oPanel = \Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory::MakeStandard();
|
||||||
|
//PanelUIBlockFactory::MakeForClass($sClass, Dict::S($sTitle));
|
||||||
|
|
||||||
|
|
||||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
||||||
$oBlock = new DisplayBlock($oFilter, $sType);
|
$oBlock = new DisplayBlock($oFilter, $sType);
|
||||||
$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
//$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||||
|
$aExtraParams["surround_with_panel"] = true;
|
||||||
|
$aExtraParams["panel_title"] = Dict::S($sTitle);
|
||||||
|
$aExtraParams["panel_class"] = $sClass;
|
||||||
|
$oPanel = $oBlock->GetDisplay($oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||||
if ($bEditMode) {
|
if ($bEditMode) {
|
||||||
$oPanel->AddHtml('<div class="dashlet-blocker"></div>');
|
$oPanel->AddHtml('<div class="ibo-dashlet-blocker dashlet-blocker"></div>');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $oPanel;
|
return $oPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1363,10 +1375,10 @@ abstract class DashletGroupBy extends Dashlet
|
|||||||
|
|
||||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $this->aProperties['query']);
|
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $this->aProperties['query']);
|
||||||
$oField->SetMandatory();
|
$oField->SetMandatory();
|
||||||
|
$oField->AddCSSClass("ibo-queryoql");
|
||||||
$oForm->AddField($oField);
|
$oForm->AddField($oField);
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
// Group by field: build the list of possible values (attribute codes + ...)
|
// Group by field: build the list of possible values (attribute codes + ...)
|
||||||
$aGroupBy = $this->GetGroupByOptions($this->aProperties['query']);
|
$aGroupBy = $this->GetGroupByOptions($this->aProperties['query']);
|
||||||
|
|
||||||
@@ -1620,16 +1632,14 @@ abstract class DashletGroupBy extends Dashlet
|
|||||||
|
|
||||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $sOQL);
|
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $sOQL);
|
||||||
$oField->SetMandatory();
|
$oField->SetMandatory();
|
||||||
|
$oField->AddCSSClass("ibo-queryoql");
|
||||||
$oForm->AddField($oField);
|
$oForm->AddField($oField);
|
||||||
|
|
||||||
if (!is_null($sOQL))
|
if (!is_null($sOQL)) {
|
||||||
{
|
|
||||||
$oField = new DesignerComboField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null);
|
$oField = new DesignerComboField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null);
|
||||||
$aGroupBy = $this->GetGroupByOptions($sOQL);
|
$aGroupBy = $this->GetGroupByOptions($sOQL);
|
||||||
$oField->SetAllowedValues($aGroupBy);
|
$oField->SetAllowedValues($aGroupBy);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// Creating a form for reading parameters!
|
// Creating a form for reading parameters!
|
||||||
$oField = new DesignerTextField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null);
|
$oField = new DesignerTextField('group_by', Dict::S('UI:DashletGroupBy:Prop-GroupBy'), null);
|
||||||
}
|
}
|
||||||
@@ -2173,6 +2183,7 @@ class DashletHeaderDynamic extends Dashlet
|
|||||||
|
|
||||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletHeaderDynamic:Prop-Query'), $this->aProperties['query']);
|
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletHeaderDynamic:Prop-Query'), $this->aProperties['query']);
|
||||||
$oField->SetMandatory();
|
$oField->SetMandatory();
|
||||||
|
$oField->AddCSSClass("ibo-queryoql");
|
||||||
$oForm->AddField($oField);
|
$oForm->AddField($oField);
|
||||||
|
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
|||||||
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletFactory;
|
use Combodo\iTop\Application\UI\Base\Component\Dashlet\DashletFactory;
|
||||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||||
|
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||||
use Combodo\iTop\Application\UI\Base\Component\Pill\PillFactory;
|
use Combodo\iTop\Application\UI\Base\Component\Pill\PillFactory;
|
||||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\PopoverMenuItemFactory;
|
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\PopoverMenuItemFactory;
|
||||||
@@ -171,6 +172,8 @@ class DisplayBlock
|
|||||||
/**positive or negative*/
|
/**positive or negative*/
|
||||||
'max_height',
|
'max_height',
|
||||||
/** string Max. height of the list, if not specified will occupy all the available height no matter the pagination */
|
/** string Max. height of the list, if not specified will occupy all the available height no matter the pagination */
|
||||||
|
'localize_values',
|
||||||
|
/** param for export.php */
|
||||||
], DataTableUIBlockFactory::GetAllowedParams()),
|
], DataTableUIBlockFactory::GetAllowedParams()),
|
||||||
'list_search' => array_merge([
|
'list_search' => array_merge([
|
||||||
'update_history',
|
'update_history',
|
||||||
@@ -189,6 +192,8 @@ class DisplayBlock
|
|||||||
/** string */
|
/** string */
|
||||||
'open',
|
'open',
|
||||||
/** bool open by default the search */
|
/** bool open by default the search */
|
||||||
|
'submit_on_load',
|
||||||
|
/** bool submit the search on loading page */
|
||||||
'class', /** class name */
|
'class', /** class name */
|
||||||
'search_header_force_dropdown', /** Html for <select> to choose the class to search */
|
'search_header_force_dropdown', /** Html for <select> to choose the class to search */
|
||||||
'this',
|
'this',
|
||||||
@@ -198,6 +203,8 @@ class DisplayBlock
|
|||||||
/** string search root class */
|
/** string search root class */
|
||||||
'open',
|
'open',
|
||||||
/** bool open the search panel by default */
|
/** bool open the search panel by default */
|
||||||
|
'submit_on_load',
|
||||||
|
/** bool submit the search on loading page */
|
||||||
'result_list_outer_selector',
|
'result_list_outer_selector',
|
||||||
/** string js selector of the search result display */
|
/** string js selector of the search result display */
|
||||||
'search_header_force_dropdown',
|
'search_header_force_dropdown',
|
||||||
@@ -257,6 +264,12 @@ class DisplayBlock
|
|||||||
'withJSRefreshCallBack',
|
'withJSRefreshCallBack',
|
||||||
/** true if dashboard page */
|
/** true if dashboard page */
|
||||||
'from_dashboard_page',
|
'from_dashboard_page',
|
||||||
|
/** bool true if list may be render in panel block */
|
||||||
|
'surround_with_panel',
|
||||||
|
/** string title of panel block */
|
||||||
|
'panel_title',
|
||||||
|
/** string class for panel block style */
|
||||||
|
'panel_class',
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isset($aAllowedParams[$sStyle])) {
|
if (isset($aAllowedParams[$sStyle])) {
|
||||||
@@ -1026,10 +1039,12 @@ JS
|
|||||||
$sHyperlink = $aCount['link'];
|
$sHyperlink = $aCount['link'];
|
||||||
$sCountLabel = $aCount['label'];
|
$sCountLabel = $aCount['label'];
|
||||||
$oPill = PillFactory::MakeForState($sClass, $sStateValue)
|
$oPill = PillFactory::MakeForState($sClass, $sStateValue)
|
||||||
->SetUrl($sHyperlink)
|
|
||||||
->SetTooltip($sStateLabel)
|
->SetTooltip($sStateLabel)
|
||||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span>")
|
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span>")
|
||||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">$sStateLabel</span>");
|
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">$sStateLabel</span>");
|
||||||
|
if ($sHyperlink != '-') {
|
||||||
|
$oPill->SetUrl($sHyperlink);
|
||||||
|
}
|
||||||
$oBlock->AddSubBlock($oPill);
|
$oBlock->AddSubBlock($oPill);
|
||||||
}
|
}
|
||||||
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
||||||
@@ -1173,11 +1188,20 @@ JS
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
|
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
|
||||||
$sTitle = Dict::Format($sFormat, $iTotalCount);
|
|
||||||
|
|
||||||
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
||||||
$aOption['dom'] = 'pl';
|
$aOption['dom'] = 'pl';
|
||||||
$oBlock = DataTableUIBlockFactory::MakeForStaticData($sTitle, $aAttribs, $aData, null, $aExtraParams, $this->m_oFilter->ToOQL(), $aOption);
|
|
||||||
|
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||||
|
$sTitle = Dict::Format($sFormat, $iTotalCount);
|
||||||
|
$oBlock = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||||
|
$oBlock->AddSubTitleBlock(new Html($sTitle));
|
||||||
|
$oDataTable = DataTableUIBlockFactory::MakeForStaticData("", $aAttribs, $aData, null, $aExtraParams, $this->m_oFilter->ToOQL(), $aOption);
|
||||||
|
$oBlock->AddSubBlock($oDataTable);
|
||||||
|
} else {
|
||||||
|
$sTitle = Dict::Format($sFormat, $iTotalCount);
|
||||||
|
$oBlock = DataTableUIBlockFactory::MakeForStaticData($sTitle, $aAttribs, $aData, null, $aExtraParams, $this->m_oFilter->ToOQL(), $aOption);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Simply count the number of elements in the set
|
// Simply count the number of elements in the set
|
||||||
@@ -1186,7 +1210,12 @@ JS
|
|||||||
if (isset($aExtraParams['format'])) {
|
if (isset($aExtraParams['format'])) {
|
||||||
$sFormat = $aExtraParams['format'];
|
$sFormat = $aExtraParams['format'];
|
||||||
}
|
}
|
||||||
$oBlock = new Html('<p>'.Dict::Format($sFormat, $iCount).'</p>');
|
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||||
|
$oBlock = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||||
|
$oBlock->AddSubBlock(new Html('<p>'.Dict::Format($sFormat, $iCount).'</p>'));
|
||||||
|
} else {
|
||||||
|
$oBlock = new Html('<p>'.Dict::Format($sFormat, $iCount).'</p>');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $oBlock;
|
return $oBlock;
|
||||||
@@ -1273,6 +1302,22 @@ JS
|
|||||||
$oBlock->bNotAuthorized = true;
|
$oBlock->bNotAuthorized = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (isset($aExtraParams['update_history']) && true == $aExtraParams['update_history']) {
|
||||||
|
$sSearchFilter = $this->m_oSet->GetFilter()->serialize();
|
||||||
|
// Limit the size of the URL (N°1585 - request uri too long)
|
||||||
|
if (strlen($sSearchFilter) < SERVER_MAX_URL_LENGTH) {
|
||||||
|
$oBlock->sEventAttachedData = json_encode(array(
|
||||||
|
'filter' => $sSearchFilter,
|
||||||
|
'breadcrumb_id' => "ui-search-".$this->m_oSet->GetClass(),
|
||||||
|
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
|
||||||
|
'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'),
|
||||||
|
'breadcrumb_instance_id' => MetaModel::GetConfig()->GetItopInstanceid(),
|
||||||
|
'breadcrumb_icon' => 'fas fa-search',
|
||||||
|
'breadcrumb_icon_type' => iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The list is made of only 1 class of objects, actions on the list are possible
|
// The list is made of only 1 class of objects, actions on the list are possible
|
||||||
if (($this->m_oSet->CountWithLimit(1) > 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES)) {
|
if (($this->m_oSet->CountWithLimit(1) > 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES)) {
|
||||||
$oBlock->AddSubBlock(cmdbAbstractObject::GetDisplaySetBlock($oPage, $this->m_oSet, $aExtraParams));
|
$oBlock->AddSubBlock(cmdbAbstractObject::GetDisplaySetBlock($oPage, $this->m_oSet, $aExtraParams));
|
||||||
@@ -1299,25 +1344,16 @@ JS
|
|||||||
$oBlock->bCreateNew = true;
|
$oBlock->bCreateNew = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aExtraParams['update_history']) && true == $aExtraParams['update_history']) {
|
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||||
$sSearchFilter = $this->m_oSet->GetFilter()->serialize();
|
$oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||||
// Limit the size of the URL (N°1585 - request uri too long)
|
$oPanel->AddSubBlock($oBlock);
|
||||||
if (strlen($sSearchFilter) < SERVER_MAX_URL_LENGTH) {
|
|
||||||
$oBlock->sEventAttachedData = json_encode(array(
|
return $oPanel;
|
||||||
'filter' => $sSearchFilter,
|
|
||||||
'breadcrumb_id' => "ui-search-".$this->m_oSet->GetClass(),
|
|
||||||
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
|
|
||||||
'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'),
|
|
||||||
'breadcrumb_instance_id' => MetaModel::GetConfig()->GetItopInstanceid(),
|
|
||||||
'breadcrumb_icon' => 'fas fa-search',
|
|
||||||
'breadcrumb_icon_type' => iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return $oBlock;
|
return $oBlock;
|
||||||
}
|
}
|
||||||
@@ -1506,6 +1542,13 @@ JS
|
|||||||
|
|
||||||
$oBlock->sUrl = $sUrl;
|
$oBlock->sUrl = $sUrl;
|
||||||
|
|
||||||
|
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||||
|
$oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||||
|
$oPanel->AddSubBlock($oBlock);
|
||||||
|
|
||||||
|
return $oPanel;
|
||||||
|
}
|
||||||
|
|
||||||
return $oBlock;
|
return $oBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1587,6 +1630,13 @@ JS
|
|||||||
$oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl);
|
$oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||||
|
$oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
|
||||||
|
$oPanel->AddSubBlock($oBlock);
|
||||||
|
|
||||||
|
return $oPanel;
|
||||||
|
}
|
||||||
|
|
||||||
return $oBlock;
|
return $oBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2158,9 +2208,9 @@ class MenuBlock extends DisplayBlock
|
|||||||
// Extract favorite actions from their menus
|
// Extract favorite actions from their menus
|
||||||
$aFavoriteRegularActions = [];
|
$aFavoriteRegularActions = [];
|
||||||
$aFavoriteTransitionActions = [];
|
$aFavoriteTransitionActions = [];
|
||||||
$aCallSpec = [$sClass, 'GetShortcutActions'];
|
if (is_callable([$sClass, 'GetShortcutActions'])) {
|
||||||
if (is_callable($aCallSpec)) {
|
/** @var cmdbAbstractObject $sClass */
|
||||||
$aShortcutActions = call_user_func($aCallSpec, $sClass);
|
$aShortcutActions = $sClass::GetShortcutActions($sClass);
|
||||||
foreach ($aShortcutActions as $key) {
|
foreach ($aShortcutActions as $key) {
|
||||||
// Regular actions
|
// Regular actions
|
||||||
if (isset($aRegularActions[$key])) {
|
if (isset($aRegularActions[$key])) {
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class DesignerForm
|
|||||||
foreach($aFields as $oField) {
|
foreach($aFields as $oField) {
|
||||||
$aRow = $oField->Render($oP, $sFormId);
|
$aRow = $oField->Render($oP, $sFormId);
|
||||||
if ($oField->IsVisible()) {
|
if ($oField->IsVisible()) {
|
||||||
$sValidation = '<span class="prop_apply ibo-prop--apply">'.$this->GetValidationArea($oField->GetFieldId()).'</span>';
|
$sValidation = '<span class="prop_apply ibo-prop--apply ibo-button ibo-is-alternative">'.$this->GetValidationArea($oField->GetFieldId()).'</span>';
|
||||||
$sField = $aRow['value'].$sValidation;
|
$sField = $aRow['value'].$sValidation;
|
||||||
$aDetails[] = array('label' => $aRow['label'], 'value' => $sField);
|
$aDetails[] = array('label' => $aRow['label'], 'value' => $sField);
|
||||||
} else {
|
} else {
|
||||||
@@ -203,51 +203,41 @@ class DesignerForm
|
|||||||
$sActionUrl = addslashes($this->sSubmitTo);
|
$sActionUrl = addslashes($this->sSubmitTo);
|
||||||
$sJSSubmitParams = json_encode($this->aSubmitParams);
|
$sJSSubmitParams = json_encode($this->aSubmitParams);
|
||||||
$sFormId = $this->GetFormId();
|
$sFormId = $this->GetFormId();
|
||||||
if ($this->oParentForm == null)
|
if ($this->oParentForm == null) {
|
||||||
{
|
|
||||||
$sReturn = '<form id="'.$sFormId.'" onsubmit="return false;">';
|
$sReturn = '<form id="'.$sFormId.'" onsubmit="return false;">';
|
||||||
$sReturn .= '<table class="prop_table">';
|
$sReturn .= '<table class="prop_table">';
|
||||||
$sReturn .= '<thead><tr><th class="prop_header">'.Dict::S('UI:Form:Property').'</th><th class="prop_header">'.Dict::S('UI:Form:Value').'</th><th colspan="2" class="prop_header"> </th></tr></thead><tbody>';
|
$sReturn .= '<thead><tr><th class="ibo-prop-header">'.Dict::S('UI:Form:Property').'</th><th class="ibo-prop-header">'.Dict::S('UI:Form:Value').'</th><th colspan="2" class="ibo-prop-header"> </th></tr></thead><tbody>';
|
||||||
}
|
}
|
||||||
|
|
||||||
$sHiddenFields = '';
|
$sHiddenFields = '';
|
||||||
foreach($this->aFieldSets as $sLabel => $aFields)
|
foreach ($this->aFieldSets as $sLabel => $aFields) {
|
||||||
{
|
|
||||||
$aDetails = array();
|
$aDetails = array();
|
||||||
if ($sLabel != '')
|
if ($sLabel != '') {
|
||||||
{
|
|
||||||
$sReturn .= $this->StartRow().'<th colspan="4">'.$sLabel.'</th>'.$this->EndRow();
|
$sReturn .= $this->StartRow().'<th colspan="4">'.$sLabel.'</th>'.$this->EndRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
foreach($aFields as $oField)
|
foreach ($aFields as $oField) {
|
||||||
{
|
|
||||||
$aRow = $oField->Render($oP, $sFormId, 'property');
|
$aRow = $oField->Render($oP, $sFormId, 'property');
|
||||||
if ($oField->IsVisible())
|
if ($oField->IsVisible()) {
|
||||||
{
|
|
||||||
$sFieldId = $this->GetFieldId($oField->GetCode());
|
$sFieldId = $this->GetFieldId($oField->GetCode());
|
||||||
$sValidation = $this->GetValidationArea($sFieldId, '<span data-tooltip-content="Apply"><i class="fas fa-check"></i></span>');
|
$sValidation = $this->GetValidationArea($sFieldId, '<span data-tooltip-content="'.Dict::Format('UI:DashboardEdit:Apply').'"><i class="fas fa-check"></i></span>');
|
||||||
$sValidationFields = '</td><td class="prop_icon prop_apply ibo-prop--apply">'.$sValidation.'</td><td class="prop_icon prop_cancel ibo-prop--cancel"><span data-tooltip-content="Revert"><i class="fas fa-times"></i></span></td>'.$this->EndRow();
|
$sValidationFields = '</td><td class="prop_icon prop_apply ibo-prop--apply ibo-button ibo-is-alternative" >'.$sValidation.'</td><td class="prop_icon prop_cancel ibo-prop--cancel ibo-button ibo-is-alternative"><span data-tooltip-content="'.Dict::Format('UI:DashboardEdit:Revert').'"><i class="fas fa-times"></i></span></td>'
|
||||||
|
.$this->EndRow();
|
||||||
$sPath = $this->GetHierarchyPath().'/'.$oField->GetCode();
|
|
||||||
|
if (is_null($aRow['label'])) {
|
||||||
if (is_null($aRow['label']))
|
|
||||||
{
|
|
||||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_value ibo-field--value" colspan="2">'.$aRow['value'];
|
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_value ibo-field--value" colspan="2">'.$aRow['value'];
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_label ibo-field--label">'.$aRow['label'].'</td><td class="prop_value ibo-field--value">'.$aRow['value'];
|
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_label ibo-field--label">'.$aRow['label'].'</td><td class="prop_value ibo-field--value">'.$aRow['value'];
|
||||||
}
|
}
|
||||||
if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField))
|
if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField)) {
|
||||||
{
|
|
||||||
$sReturn .= $sValidationFields;
|
$sReturn .= $sValidationFields;
|
||||||
}
|
}
|
||||||
$sNotifyParentSelectorJS = is_null($sNotifyParentSelector) ? 'null' : "'".addslashes($sNotifyParentSelector)."'";
|
$sNotifyParentSelectorJS = is_null($sNotifyParentSelector) ? 'null' : "'".addslashes($sNotifyParentSelector)."'";
|
||||||
$sAutoApply = $oField->IsAutoApply() ? 'true' : 'false';
|
$sAutoApply = $oField->IsAutoApply() ? 'true' : 'false';
|
||||||
$sHandlerEquals = $oField->GetHandlerEquals();
|
$sHandlerEquals = $oField->GetHandlerEquals();
|
||||||
$sHandlerGetValue = $oField->GetHandlerGetValue();
|
$sHandlerGetValue = $oField->GetHandlerGetValue();
|
||||||
|
|
||||||
$sWidgetClass = $oField->GetWidgetClass();
|
$sWidgetClass = $oField->GetWidgetClass();
|
||||||
$sJSExtraParams = '';
|
$sJSExtraParams = '';
|
||||||
if (count($oField->GetWidgetExtraParams()) > 0)
|
if (count($oField->GetWidgetExtraParams()) > 0)
|
||||||
@@ -1423,28 +1413,32 @@ class DesignerIconSelectionField extends DesignerFormField
|
|||||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||||
$idx = 0;
|
$idx = 0;
|
||||||
foreach($this->aAllowedValues as $index => $aValue)
|
$idxFallback = 0;
|
||||||
{
|
foreach ($this->aAllowedValues as $index => $aValue) {
|
||||||
if ($aValue['value'] == $this->defaultValue)
|
if ($aValue['value'] == $this->defaultValue) {
|
||||||
{
|
|
||||||
$idx = $index;
|
$idx = $index;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
//fallback if url of default value contains ../
|
||||||
|
//for contact, icon is http://localhost/env-production/itop-structure/../../images/icons/icons8-customer.svg => not found http://localhost/images/icons/icons8-customer.svg
|
||||||
|
if (basename($aValue['value']) == basename($this->defaultValue)) {
|
||||||
|
$idxFallback = $index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($idx == 0) {
|
||||||
|
$idx = $idxFallback;
|
||||||
}
|
}
|
||||||
$sJSItems = json_encode($this->aAllowedValues);
|
$sJSItems = json_encode($this->aAllowedValues);
|
||||||
$sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
|
$sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
|
||||||
if (!$this->IsReadOnly())
|
if (!$this->IsReadOnly()) {
|
||||||
{
|
|
||||||
$sDefaultValue = ($this->defaultValue !== '') ? $this->defaultValue : $this->aAllowedValues[$idx]['value'];
|
$sDefaultValue = ($this->defaultValue !== '') ? $this->defaultValue : $this->aAllowedValues[$idx]['value'];
|
||||||
$sValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$sDefaultValue}\"/>";
|
$sValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$sDefaultValue}\"/>";
|
||||||
$oP->add_ready_script(
|
$oP->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
|
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
$sValue = '<span style="display:inline-block;line-height:48px;height:48px;"><span><img style="vertical-align:middle" src="'.$this->aAllowedValues[$idx]['icon'].'" /> '.htmlentities($this->aAllowedValues[$idx]['label'], ENT_QUOTES, 'UTF-8').'</span></span>';
|
$sValue = '<span style="display:inline-block;line-height:48px;height:48px;"><span><img style="vertical-align:middle" src="'.$this->aAllowedValues[$idx]['icon'].'" /> '.htmlentities($this->aAllowedValues[$idx]['label'], ENT_QUOTES, 'UTF-8').'</span></span>';
|
||||||
}
|
}
|
||||||
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
|
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
|
||||||
@@ -1459,18 +1453,21 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
|||||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||||
{
|
{
|
||||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||||
|
$aFolderList = [
|
||||||
|
APPROOT.'env-'.utils::GetCurrentEnvironment() => utils::GetAbsoluteUrlModulesRoot(),
|
||||||
|
APPROOT.'images/icons' => utils::GetAbsoluteUrlAppRoot().'images/icons',
|
||||||
|
];
|
||||||
|
if (count(self::$aAllIcons) == 0) {
|
||||||
|
foreach ($aFolderList as $sFolderPath => $sUrlPrefix) {
|
||||||
|
$aIcons = self::FindIconsOnDisk($sFolderPath);
|
||||||
|
ksort($aIcons);
|
||||||
|
|
||||||
if (count(self::$aAllIcons) == 0)
|
foreach ($aIcons as $sFilePath) {
|
||||||
{
|
self::$aAllIcons[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => $sUrlPrefix.$sFilePath);
|
||||||
self::$aAllIcons = self::FindIconsOnDisk(APPROOT.'env-'.utils::GetCurrentEnvironment());
|
}
|
||||||
ksort(self::$aAllIcons);
|
}
|
||||||
}
|
}
|
||||||
$aValues = array();
|
$this->SetAllowedValues(self::$aAllIcons);
|
||||||
foreach(self::$aAllIcons as $sFilePath)
|
|
||||||
{
|
|
||||||
$aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
|
|
||||||
}
|
|
||||||
$this->SetAllowedValues($aValues);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static protected function FindIconsOnDisk($sBaseDir, $sDir = '')
|
static protected function FindIconsOnDisk($sBaseDir, $sDir = '')
|
||||||
@@ -1501,26 +1498,29 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
|||||||
SetupUtils::builddir(dirname($sCacheFile));
|
SetupUtils::builddir(dirname($sCacheFile));
|
||||||
file_put_contents($sCacheFile, $sAvailableIcons, LOCK_EX);
|
file_put_contents($sCacheFile, $sAvailableIcons, LOCK_EX);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $aFiles;
|
return $aFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
static protected function _FindIconsOnDisk($sBaseDir, $sDir = '')
|
static protected function _FindIconsOnDisk($sBaseDir, $sDir = '', &$aFilesSpecs = [])
|
||||||
{
|
{
|
||||||
$aResult = array();
|
$aResult = [];
|
||||||
// Populate automatically the list of icon files
|
// Populate automatically the list of icon files
|
||||||
if ($hDir = @opendir($sBaseDir.'/'.$sDir))
|
if ($hDir = @opendir($sBaseDir.'/'.$sDir)) {
|
||||||
{
|
while (($sFile = readdir($hDir)) !== false) {
|
||||||
while (($sFile = readdir($hDir)) !== false)
|
|
||||||
{
|
|
||||||
$aMatches = array();
|
$aMatches = array();
|
||||||
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
|
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile)) {
|
||||||
{
|
|
||||||
$sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
|
$sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
|
||||||
$aResult = array_merge($aResult, self::_FindIconsOnDisk($sBaseDir, $sDirSubPath));
|
$aResult = array_merge($aResult, self::_FindIconsOnDisk($sBaseDir, $sDirSubPath, $aFilesSpecs));
|
||||||
}
|
}
|
||||||
if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid
|
$sSize = filesize($sBaseDir.'/'.$sDir.'/'.$sFile);
|
||||||
|
if (isset($aFilesSpecs[$sFile]) && $aFilesSpecs[$sFile] == $sSize) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (preg_match("/\.(png|jpg|jpeg|gif|svg)$/i", $sFile, $aMatches)) // png, jp(e)g, gif and svg are considered valid
|
||||||
{
|
{
|
||||||
$aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
|
$aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
|
||||||
|
$aFilesSpecs[$sFile] = $sSize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir($hDir);
|
closedir($hDir);
|
||||||
@@ -1645,27 +1645,23 @@ class DesignerFormSelectorField extends DesignerFormField
|
|||||||
{
|
{
|
||||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||||
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
|
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
|
||||||
|
|
||||||
$this->aCSSClasses[] = 'formSelector';
|
$this->aCSSClasses[] = 'formSelector';
|
||||||
|
|
||||||
$sCSSClasses = '';
|
$sCSSClasses = '';
|
||||||
if (count($this->aCSSClasses) > 0)
|
if (count($this->aCSSClasses) > 0) {
|
||||||
{
|
|
||||||
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->IsSorted())
|
if ($this->IsSorted()) {
|
||||||
{
|
|
||||||
uasort($this->aSubForms, array(get_class($this), 'SortOnFormLabel'));
|
uasort($this->aSubForms, array(get_class($this), 'SortOnFormLabel'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->IsReadOnly())
|
if ($this->IsReadOnly()) {
|
||||||
{
|
|
||||||
$sDisplayValue = '';
|
$sDisplayValue = '';
|
||||||
$sHiddenValue = '';
|
$sHiddenValue = '';
|
||||||
foreach($this->aSubForms as $iKey => $aFormData)
|
foreach ($this->aSubForms as $iKey => $aFormData) {
|
||||||
{
|
|
||||||
if ($iKey == $this->defaultValue) // Default value is actually the index
|
if ($iKey == $this->defaultValue) // Default value is actually the index
|
||||||
{
|
{
|
||||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
||||||
@@ -1674,12 +1670,9 @@ class DesignerFormSelectorField extends DesignerFormField
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$sHtml = "<span $sCSSClasses>".$sDisplayValue.$sHiddenValue."</span>";
|
$sHtml = "<span $sCSSClasses>".$sDisplayValue.$sHiddenValue."</span>";
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||||
foreach($this->aSubForms as $iKey => $aFormData)
|
foreach ($this->aSubForms as $iKey => $aFormData) {
|
||||||
{
|
|
||||||
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
|
||||||
$sValue = htmlentities($aFormData['value'], ENT_QUOTES, 'UTF-8');
|
$sValue = htmlentities($aFormData['value'], ENT_QUOTES, 'UTF-8');
|
||||||
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
|
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
|
||||||
@@ -1687,22 +1680,19 @@ class DesignerFormSelectorField extends DesignerFormField
|
|||||||
}
|
}
|
||||||
$sHtml .= "</select>";
|
$sHtml .= "</select>";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($sRenderMode == 'property')
|
if ($sRenderMode == 'property') {
|
||||||
{
|
$sHtml .= '</td><td class="prop_icon prop_apply ibo-prop--apply ibo-button ibo-is-alternative"><span data-tooltip-content="'.Dict::Format('UI:DashboardEdit:Apply').'"><i class="fas fa-check"></i></span></td><td class="prop_icon prop_cancel ibo-prop--cancel ibo-button ibo-is-alternative"><span data-tooltip-content="'.Dict::Format('UI:DashboardEdit:Revert').'"><i class="fas fa-times"></i></span></td></tr>';
|
||||||
$sHtml .= '</td><td class="prop_icon prop_apply"><span title="Apply" class="ui-icon ui-icon-circle-check"/></td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td></tr>';
|
|
||||||
}
|
}
|
||||||
foreach($this->aSubForms as $sKey => $aFormData)
|
foreach ($this->aSubForms as $sKey => $aFormData) {
|
||||||
{
|
|
||||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||||
$sStyle = (($sKey == $this->defaultValue) && $this->oForm->IsDisplayed()) ? '' : 'style="display:none"';
|
$sStyle = (($sKey == $this->defaultValue) && $this->oForm->IsDisplayed()) ? '' : 'style="display:none"';
|
||||||
$oSubForm = $aFormData['form'];
|
$oSubForm = $aFormData['form'];
|
||||||
$oSubForm->SetParentForm($this->oForm);
|
$oSubForm->SetParentForm($this->oForm);
|
||||||
$oSubForm->CopySubmitParams($this->oForm);
|
$oSubForm->CopySubmitParams($this->oForm);
|
||||||
$oSubForm->SetPrefix($this->oForm->GetPrefix().$sKey.'_');
|
$oSubForm->SetPrefix($this->oForm->GetPrefix().$sKey.'_');
|
||||||
|
|
||||||
if ($sRenderMode == 'property')
|
if ($sRenderMode == 'property') {
|
||||||
{
|
|
||||||
// Note: Managing the visibility of nested subforms had several implications
|
// Note: Managing the visibility of nested subforms had several implications
|
||||||
// 1) Attributes are displayed in a table and we have to group them in as many tbodys as necessary to hide/show the various options depending on the current selection
|
// 1) Attributes are displayed in a table and we have to group them in as many tbodys as necessary to hide/show the various options depending on the current selection
|
||||||
// 2) It is not possible to nest tbody tags. Therefore, it is not possible to manage the visibility the same way as it is done for the dialog mode (using nested divs).
|
// 2) It is not possible to nest tbody tags. Therefore, it is not possible to manage the visibility the same way as it is done for the dialog mode (using nested divs).
|
||||||
|
|||||||
@@ -1094,9 +1094,13 @@ class LoginWebPage extends NiceWebPage
|
|||||||
if (isset($_SESSION['auth_user']))
|
if (isset($_SESSION['auth_user']))
|
||||||
{
|
{
|
||||||
$sAuthUser = $_SESSION['auth_user'];
|
$sAuthUser = $_SESSION['auth_user'];
|
||||||
|
$sIssue = $_SESSION['pwd_issue'] ?? null;
|
||||||
|
unset($_SESSION['pwd_issue']);
|
||||||
|
$bFailedLogin = ($sIssue != null); // Force the "failed login" flag to display the "issue" message
|
||||||
|
|
||||||
UserRights::Login($sAuthUser); // Set the user's language
|
UserRights::Login($sAuthUser); // Set the user's language
|
||||||
$oPage = self::NewLoginWebPage();
|
$oPage = self::NewLoginWebPage();
|
||||||
$oPage->DisplayChangePwdForm();
|
$oPage->DisplayChangePwdForm($bFailedLogin, $sIssue);
|
||||||
$oPage->output();
|
$oPage->output();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1128,22 +1128,11 @@ class OQLMenuNode extends MenuNode
|
|||||||
//$sIcon = MetaModel::GetClassIcon($oSearch->GetClass(), false);
|
//$sIcon = MetaModel::GetClassIcon($oSearch->GetClass(), false);
|
||||||
|
|
||||||
if ($bSearchPane) {
|
if ($bSearchPane) {
|
||||||
$aParams = array_merge(array('open' => $bSearchOpen, 'table_id' => $sUsageId), $aExtraParams);
|
$aParams = array_merge(['open' => $bSearchOpen, 'table_id' => $sUsageId, 'submit_on_load' => true], $aExtraParams);
|
||||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||||
$oBlock->Display($oPage, 0);
|
$oBlock->Display($oPage, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
//$oPage->add("<p class=\"page-header\">$sIcon ".utils::HtmlEntities(Dict::S($sTitle))."</p>");
|
|
||||||
$oPage->add("<div class='sf_results_area' data-target='search_results'>");
|
|
||||||
$oTitle = TitleUIBlockFactory::MakeForPage($sTitle);
|
|
||||||
$oPage->AddUiBlock($oTitle);
|
|
||||||
|
|
||||||
$aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
|
|
||||||
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);
|
|
||||||
$oBlock->Display($oPage, $sUsageId);
|
|
||||||
|
|
||||||
$oPage->add("</div>");
|
|
||||||
|
|
||||||
if ($bEnableBreadcrumb && ($oPage instanceof iTopWebPage)) {
|
if ($bEnableBreadcrumb && ($oPage instanceof iTopWebPage)) {
|
||||||
// Breadcrumb
|
// Breadcrumb
|
||||||
//$iCount = $oBlock->GetDisplayedCount();
|
//$iCount = $oBlock->GetDisplayedCount();
|
||||||
@@ -1209,7 +1198,8 @@ class SearchMenuNode extends MenuNode
|
|||||||
$oPage->SetBreadCrumbEntry("menu-".$this->sMenuId, $this->GetTitle(), '', '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
|
$oPage->SetBreadCrumbEntry("menu-".$this->sMenuId, $this->GetTitle(), '', '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
|
||||||
|
|
||||||
$oSearch = new DBObjectSearch($this->sClass);
|
$oSearch = new DBObjectSearch($this->sClass);
|
||||||
$aParams = array_merge(array('table_id' => 'Menu_'.utils::GetSafeId($this->GetMenuId())), $aExtraParams);
|
$sUsageId = 'Menu_'.utils::GetSafeId($this->GetMenuId());
|
||||||
|
$aParams = array_merge(array('table_id' =>$sUsageId), $aExtraParams);
|
||||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||||
$oBlock->Display($oPage, 0);
|
$oBlock->Display($oPage, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ class ThemeHandler
|
|||||||
{
|
{
|
||||||
const IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg'];
|
const IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg'];
|
||||||
|
|
||||||
|
/** @var \CompileCSSService */
|
||||||
private static $oCompileCSSService;
|
private static $oCompileCSSService;
|
||||||
|
|
||||||
public static function GetAppRootWithSlashes()
|
public static function GetAppRootWithSlashes()
|
||||||
@@ -315,11 +316,6 @@ class ThemeHandler
|
|||||||
// Loading files to import and stylesheet to compile, also getting most recent modification time on overall files
|
// Loading files to import and stylesheet to compile, also getting most recent modification time on overall files
|
||||||
$sTmpThemeScssContent = '';
|
$sTmpThemeScssContent = '';
|
||||||
$oFindStylesheetObject = new FindStylesheetObject();
|
$oFindStylesheetObject = new FindStylesheetObject();
|
||||||
if (isset($aThemeParameters['variable_imports'])) {
|
|
||||||
foreach ($aThemeParameters['variable_imports'] as $sImport) {
|
|
||||||
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($aThemeParameters['utility_imports'])) {
|
if (isset($aThemeParameters['utility_imports'])) {
|
||||||
foreach ($aThemeParameters['utility_imports'] as $sImport) {
|
foreach ($aThemeParameters['utility_imports'] as $sImport) {
|
||||||
@@ -337,6 +333,12 @@ class ThemeHandler
|
|||||||
$sTmpThemeScssContent .= '@import "'.$sStylesheet.'";'."\n";
|
$sTmpThemeScssContent .= '@import "'.$sStylesheet.'";'."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($aThemeParameters['variable_imports'])) {
|
||||||
|
foreach ($aThemeParameters['variable_imports'] as $sImport) {
|
||||||
|
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$iStyleLastModified = $oFindStylesheetObject->GetLastModified();
|
$iStyleLastModified = $oFindStylesheetObject->GetLastModified();
|
||||||
|
|
||||||
$aIncludedImages=static::GetIncludedImages($aThemeParametersWithVersion, $oFindStylesheetObject->GetAllStylesheetPaths(), $sThemeId);
|
$aIncludedImages=static::GetIncludedImages($aThemeParametersWithVersion, $oFindStylesheetObject->GetAllStylesheetPaths(), $sThemeId);
|
||||||
|
|||||||
@@ -82,6 +82,12 @@ class UIExtKeyWidget
|
|||||||
$aArgs = [], $bSearchMode = false, &$sInputType = ''
|
$aArgs = [], $bSearchMode = false, &$sInputType = ''
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
// we will only use key & name, so let's reduce fields loaded !
|
||||||
|
$aAttToLoad = [
|
||||||
|
$sClass => [], // nothing, id and friendlyname are automatically added by the API
|
||||||
|
];
|
||||||
|
$oAllowedValues->OptimizeColumnLoad($aAttToLoad);
|
||||||
|
|
||||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||||
$sTargetClass = $oAttDef->GetTargetClass();
|
$sTargetClass = $oAttDef->GetTargetClass();
|
||||||
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
|
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
|
||||||
@@ -246,7 +252,7 @@ class UIExtKeyWidget
|
|||||||
}
|
}
|
||||||
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_DECORATED;
|
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_DECORATED;
|
||||||
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\" tabindex=\"0\"></select>";
|
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\" tabindex=\"0\"></select>";
|
||||||
$sJsonOptions = json_encode($aOptions);
|
$sJsonOptions = str_replace('\\', '\\\\', json_encode($aOptions));
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||||
@@ -976,7 +982,7 @@ JS
|
|||||||
public function DisplayHierarchy(WebPage $oPage, $sFilter, $currValue, $oObj)
|
public function DisplayHierarchy(WebPage $oPage, $sFilter, $currValue, $oObj)
|
||||||
{
|
{
|
||||||
$sDialogTitle = addslashes(Dict::Format('UI:HierarchyOf_Class', MetaModel::GetName($this->sTargetClass)));
|
$sDialogTitle = addslashes(Dict::Format('UI:HierarchyOf_Class', MetaModel::GetName($this->sTargetClass)));
|
||||||
$oPage->add('<div id="dlg_tree_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div style="overflow:auto;background:#fff;margin-bottom:5px;" id="tree_'.$this->iId.'">');
|
$oPage->add('<div id="dlg_tree_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div style="margin-bottom:5px;" id="tree_'.$this->iId.'">');
|
||||||
$oPage->add('<table style="width:100%"><tr><td>');
|
$oPage->add('<table style="width:100%"><tr><td>');
|
||||||
if (is_null($sFilter))
|
if (is_null($sFilter))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -538,7 +538,7 @@ class utils
|
|||||||
*/
|
*/
|
||||||
public static function ReadMultipleSelection($oFullSetFilter)
|
public static function ReadMultipleSelection($oFullSetFilter)
|
||||||
{
|
{
|
||||||
$aSelectedObj = utils::ReadParam('selectObject[]', array());
|
$aSelectedObj = utils::ReadParam('selectObject', array());
|
||||||
$sSelectionMode = utils::ReadParam('selectionMode', '');
|
$sSelectionMode = utils::ReadParam('selectionMode', '');
|
||||||
if ($sSelectionMode != '') {
|
if ($sSelectionMode != '') {
|
||||||
// Paginated selection
|
// Paginated selection
|
||||||
@@ -2638,9 +2638,21 @@ class utils
|
|||||||
if(!empty($aMentionsAllowedClasses)) {
|
if(!empty($aMentionsAllowedClasses)) {
|
||||||
$aDefaultConf['mentions'] = [];
|
$aDefaultConf['mentions'] = [];
|
||||||
|
|
||||||
foreach($aMentionsAllowedClasses as $sMentionChar => $sMentionClass) {
|
foreach($aMentionsAllowedClasses as $sMentionMarker => $sMentionScope) {
|
||||||
|
// Retrieve mention class
|
||||||
|
// - First test if the conf is a simple Datamodel class
|
||||||
|
if (MetaModel::IsValidClass($sMentionScope)) {
|
||||||
|
$sMentionClass = $sMentionScope;
|
||||||
|
}
|
||||||
|
// - Otherwise it must be a valid OQL
|
||||||
|
else {
|
||||||
|
$oTmpSearch = DBSearch::FromOQL($sMentionScope);
|
||||||
|
$sMentionClass = $oTmpSearch->GetClass();
|
||||||
|
unset($oTmpSearch);
|
||||||
|
}
|
||||||
|
|
||||||
// Note: Endpoints are defaults only and should be overloaded by other GUIs such as the end-users portal
|
// 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&target_class='.$sMentionClass.'&needle={encodedQuery}';
|
$sMentionEndpoint = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=cke_mentions&marker='.$sMentionMarker.'&needle={encodedQuery}';
|
||||||
$sMentionItemUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class='.$sMentionClass.'&id={id}';
|
$sMentionItemUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class='.$sMentionClass.'&id={id}';
|
||||||
|
|
||||||
$sMentionItemPictureTemplate = (empty(MetaModel::GetImageAttributeCode($sMentionClass))) ? '' : <<<HTML
|
$sMentionItemPictureTemplate = (empty(MetaModel::GetImageAttributeCode($sMentionClass))) ? '' : <<<HTML
|
||||||
@@ -2650,12 +2662,12 @@ HTML;
|
|||||||
<li class="ibo-vendors-ckeditor--autocomplete-item" data-id="{id}">{$sMentionItemPictureTemplate}<span class="ibo-vendors-ckeditor--autocomplete-item-title">{friendlyname}</span></li>
|
<li class="ibo-vendors-ckeditor--autocomplete-item" data-id="{id}">{$sMentionItemPictureTemplate}<span class="ibo-vendors-ckeditor--autocomplete-item-title">{friendlyname}</span></li>
|
||||||
HTML;
|
HTML;
|
||||||
$sMentionOutputTemplate = <<<HTML
|
$sMentionOutputTemplate = <<<HTML
|
||||||
<a href="$sMentionItemUrl" data-role="object-mention" data-object-class="{class}" data-object-id="{id}">{$sMentionChar}{friendlyname}</a>
|
<a href="$sMentionItemUrl" data-role="object-mention" data-object-class="{class}" data-object-id="{id}">{$sMentionMarker}{friendlyname}</a>
|
||||||
HTML;
|
HTML;
|
||||||
|
|
||||||
$aDefaultConf['mentions'][] = [
|
$aDefaultConf['mentions'][] = [
|
||||||
'feed' => $sMentionEndpoint,
|
'feed' => $sMentionEndpoint,
|
||||||
'marker' => $sMentionChar,
|
'marker' => $sMentionMarker,
|
||||||
'minChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'),
|
'minChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'),
|
||||||
'itemTemplate' => $sMentionItemTemplate,
|
'itemTemplate' => $sMentionItemTemplate,
|
||||||
'outputTemplate' => $sMentionOutputTemplate,
|
'outputTemplate' => $sMentionOutputTemplate,
|
||||||
@@ -2814,15 +2826,30 @@ HTML;
|
|||||||
* Check if iTop is in a development environment (VCS vs build number)
|
* Check if iTop is in a development environment (VCS vs build number)
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
*
|
||||||
|
* @since 2.6.0 method creation
|
||||||
|
* @since 3.0.0 add the `developer_mode.enabled` config parameter
|
||||||
|
*
|
||||||
|
* @use `developer_mode.enabled` config parameter
|
||||||
|
* @use ITOP_REVISION
|
||||||
*/
|
*/
|
||||||
public static function IsDevelopmentEnvironment()
|
public static function IsDevelopmentEnvironment()
|
||||||
{
|
{
|
||||||
if (! defined('ITOP_REVISION')) {
|
$oConfig = utils::GetConfig();
|
||||||
|
$bIsDevEnvInConfig = $oConfig->Get('developer_mode.enabled');
|
||||||
|
if ($bIsDevEnvInConfig === true) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if ($bIsDevEnvInConfig === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('ITOP_REVISION')) {
|
||||||
//defensive behaviour: by default we are not in dev environment
|
//defensive behaviour: by default we are not in dev environment
|
||||||
//can happen even in production (unattended install for example) or with exotic use of iTop
|
//can happen even in production (unattended install for example) or with exotic use of iTop
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ITOP_REVISION === 'svn';
|
return ITOP_REVISION === 'svn';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"ext-mysqli": "*",
|
"ext-mysqli": "*",
|
||||||
"ext-soap": "*",
|
"ext-soap": "*",
|
||||||
"combodo/tcpdf": "6.3.5",
|
"combodo/tcpdf": "6.3.5",
|
||||||
"nikic/php-parser": "^3.1",
|
"nikic/php-parser": "^4.12.0",
|
||||||
"pear/archive_tar": "1.4.13",
|
"pear/archive_tar": "1.4.13",
|
||||||
"pelago/emogrifier": "2.1.0",
|
"pelago/emogrifier": "2.1.0",
|
||||||
"scssphp/scssphp": "1.0.6",
|
"scssphp/scssphp": "1.0.6",
|
||||||
@@ -50,11 +50,7 @@
|
|||||||
"classmap": [
|
"classmap": [
|
||||||
"core",
|
"core",
|
||||||
"application",
|
"application",
|
||||||
"sources/application",
|
"sources"
|
||||||
"sources/Composer",
|
|
||||||
"sources/Controller",
|
|
||||||
"sources/Form",
|
|
||||||
"sources/Renderer"
|
|
||||||
],
|
],
|
||||||
"exclude-from-classmap": [
|
"exclude-from-classmap": [
|
||||||
"core/dbobjectsearch.class.php",
|
"core/dbobjectsearch.class.php",
|
||||||
|
|||||||
23
composer.lock
generated
23
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "62e394b1ef30b4e716e3e3e519de11dd",
|
"content-hash": "75f17b71005971207815906ec7e9cf67",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "combodo/tcpdf",
|
"name": "combodo/tcpdf",
|
||||||
@@ -68,24 +68,25 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/php-parser",
|
"name": "nikic/php-parser",
|
||||||
"version": "v3.1.5",
|
"version": "v4.12.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||||
"reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce"
|
"reference": "6608f01670c3cc5079e18c1dab1104e002579143"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/bb87e28e7d7b8d9a7fda231d37457c9210faf6ce",
|
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6608f01670c3cc5079e18c1dab1104e002579143",
|
||||||
"reference": "bb87e28e7d7b8d9a7fda231d37457c9210faf6ce",
|
"reference": "6608f01670c3cc5079e18c1dab1104e002579143",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-tokenizer": "*",
|
"ext-tokenizer": "*",
|
||||||
"php": ">=5.5"
|
"php": ">=7.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "~4.0|~5.0"
|
"ircmaxell/php-yacc": "^0.0.7",
|
||||||
|
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0"
|
||||||
},
|
},
|
||||||
"bin": [
|
"bin": [
|
||||||
"bin/php-parse"
|
"bin/php-parse"
|
||||||
@@ -93,7 +94,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.0-dev"
|
"dev-master": "4.9-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -115,7 +116,11 @@
|
|||||||
"parser",
|
"parser",
|
||||||
"php"
|
"php"
|
||||||
],
|
],
|
||||||
"time": "2018-02-28T20:30:58+00:00"
|
"support": {
|
||||||
|
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||||
|
"source": "https://github.com/nikic/PHP-Parser/tree/v4.12.0"
|
||||||
|
},
|
||||||
|
"time": "2021-07-21T10:44:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "paragonie/random_compat",
|
"name": "paragonie/random_compat",
|
||||||
|
|||||||
@@ -388,7 +388,12 @@ class AsyncSendEmail extends AsyncTask
|
|||||||
return "Bug - the email should be sent in synchronous mode";
|
return "Bug - the email should be sent in synchronous mode";
|
||||||
|
|
||||||
case EMAIL_SEND_ERROR:
|
case EMAIL_SEND_ERROR:
|
||||||
return "Failed: ".implode(', ', $aIssues);
|
if (is_array($aIssues)) {
|
||||||
|
$sMessage = "Sending eMail failed: ".implode(', ', $aIssues);
|
||||||
|
} else {
|
||||||
|
$sMessage = "Sending eMail failed.";
|
||||||
|
}
|
||||||
|
throw new Exception($sMessage);
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4159,13 +4159,13 @@ class AttributeText extends AttributeString
|
|||||||
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
|
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
|
||||||
$sValue = self::RenderWikiHtml($sValue);
|
$sValue = self::RenderWikiHtml($sValue);
|
||||||
|
|
||||||
return "<div $sStyle>".str_replace("\n", "<br>\n", $sValue).'</div>';
|
return "<div $sStyle>$sValue</div>";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sValue = self::RenderWikiHtml($sValue, true /* wiki only */);
|
$sValue = self::RenderWikiHtml($sValue, true /* wiki only */);
|
||||||
|
|
||||||
return "<div class=\"HTML\" $sStyle>".InlineImage::FixUrls($sValue).'</div>';
|
return "<div class=\"HTML ibo-is-html-content\" $sStyle>".InlineImage::FixUrls($sValue).'</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,4 @@
|
|||||||
<?php
|
<?php
|
||||||
// Copyright (C) 2010-2021 Combodo SARL
|
|
||||||
//
|
|
||||||
// This file is part of iTop.
|
|
||||||
//
|
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// iTop is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Affero General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persistent class (internal) cmdbChange
|
* Persistent class (internal) cmdbChange
|
||||||
*
|
*
|
||||||
@@ -24,6 +6,7 @@
|
|||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A change as requested/validated at once by user, may groups many atomic changes
|
* A change as requested/validated at once by user, may groups many atomic changes
|
||||||
@@ -53,7 +36,7 @@ class CMDBChange extends DBObject
|
|||||||
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("allowed_values"=>null, "sql"=>"user_id", "targetclass"=>"User", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("allowed_values"=>null, "sql"=>"user_id", "targetclass"=>"User", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum('interactive,csv-interactive,csv-import.php,webservice-soap,webservice-rest,synchro-data-source,email-processing,custom-extension'), "sql"=>"origin", "default_value"=>"interactive", "is_null_allowed"=>true, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum(implode(',', [CMDBChangeOrigin::INTERACTIVE, CMDBChangeOrigin::CSV_INTERACTIVE, CMDBChangeOrigin::CSV_IMPORT, CMDBChangeOrigin::WEBSERVICE_SOAP, CMDBChangeOrigin::WEBSERVICE_REST, CMDBChangeOrigin::SYNCHRO_DATA_SOURCE, CMDBChangeOrigin::EMAIL_PROCESSING, CMDBChangeOrigin::CUSTOM_EXTENSION])), "sql"=>"origin", "default_value"=>CMDBChangeOrigin::INTERACTIVE, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -606,7 +606,7 @@ class CMDBSource
|
|||||||
{
|
{
|
||||||
$sShortSQL = substr(preg_replace("/\s+/", " ", substr($sSql, 0, 180)), 0, 150);
|
$sShortSQL = substr(preg_replace("/\s+/", " ", substr($sSql, 0, 180)), 0, 150);
|
||||||
if (substr_compare($sShortSQL, "SELECT", 0, strlen("SELECT")) !== 0) {
|
if (substr_compare($sShortSQL, "SELECT", 0, strlen("SELECT")) !== 0) {
|
||||||
IssueLog::Trace("$sShortSQL", 'cmdbsource');
|
IssueLog::Trace("$sShortSQL", LogChannels::CMDB_SOURCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
$oKPI = new ExecutionKPI();
|
$oKPI = new ExecutionKPI();
|
||||||
@@ -695,12 +695,11 @@ class CMDBSource
|
|||||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||||
$bHasExistingTransactions = self::IsInsideTransaction();
|
$bHasExistingTransactions = self::IsInsideTransaction();
|
||||||
if (!$bHasExistingTransactions)
|
if (!$bHasExistingTransactions) {
|
||||||
{
|
IssueLog::Trace("START TRANSACTION $sCaller", LogChannels::CMDB_SOURCE);
|
||||||
IssueLog::Trace("START TRANSACTION $sCaller", 'cmdbsource');
|
|
||||||
self::DBQuery('START TRANSACTION');
|
self::DBQuery('START TRANSACTION');
|
||||||
} else {
|
} else {
|
||||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") START TRANSACTION $sCaller", 'cmdbsource');
|
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") START TRANSACTION $sCaller", LogChannels::CMDB_SOURCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::AddTransactionLevel();
|
self::AddTransactionLevel();
|
||||||
@@ -720,21 +719,20 @@ class CMDBSource
|
|||||||
{
|
{
|
||||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||||
if (!self::IsInsideTransaction())
|
if (!self::IsInsideTransaction()) {
|
||||||
{
|
|
||||||
// should not happen !
|
// should not happen !
|
||||||
IssueLog::Error("No Transaction COMMIT $sCaller", 'cmdbsource');
|
IssueLog::Error("No Transaction COMMIT $sCaller", LogChannels::CMDB_SOURCE);
|
||||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
||||||
}
|
}
|
||||||
|
|
||||||
self::RemoveLastTransactionLevel();
|
self::RemoveLastTransactionLevel();
|
||||||
|
|
||||||
if (self::IsInsideTransaction())
|
if (self::IsInsideTransaction()) {
|
||||||
{
|
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") COMMIT $sCaller", LogChannels::CMDB_SOURCE);
|
||||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") COMMIT $sCaller", 'cmdbsource');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IssueLog::Trace("COMMIT $sCaller", 'cmdbsource');
|
IssueLog::Trace("COMMIT $sCaller", LogChannels::CMDB_SOURCE);
|
||||||
self::DBQuery('COMMIT');
|
self::DBQuery('COMMIT');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -755,20 +753,19 @@ class CMDBSource
|
|||||||
{
|
{
|
||||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||||
if (!self::IsInsideTransaction())
|
if (!self::IsInsideTransaction()) {
|
||||||
{
|
|
||||||
// should not happen !
|
// should not happen !
|
||||||
IssueLog::Error("No Transaction ROLLBACK $sCaller", 'cmdbsource');
|
IssueLog::Error("No Transaction ROLLBACK $sCaller", LogChannels::CMDB_SOURCE);
|
||||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
||||||
}
|
}
|
||||||
self::RemoveLastTransactionLevel();
|
self::RemoveLastTransactionLevel();
|
||||||
if (self::IsInsideTransaction())
|
if (self::IsInsideTransaction()) {
|
||||||
{
|
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") ROLLBACK $sCaller", LogChannels::CMDB_SOURCE);
|
||||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") ROLLBACK $sCaller", 'cmdbsource');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IssueLog::Trace("ROLLBACK $sCaller", 'cmdbsource');
|
IssueLog::Trace("ROLLBACK $sCaller", LogChannels::CMDB_SOURCE);
|
||||||
self::DBQuery('ROLLBACK');
|
self::DBQuery('ROLLBACK');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -904,11 +901,12 @@ class CMDBSource
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sSql
|
* @param string $sSql
|
||||||
|
* @param int $iMode
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
* @throws \MySQLException if query cannot be processed
|
* @throws \MySQLException if query cannot be processed
|
||||||
*/
|
*/
|
||||||
public static function QueryToArray($sSql)
|
public static function QueryToArray($sSql, $iMode = MYSQLI_BOTH)
|
||||||
{
|
{
|
||||||
$aData = array();
|
$aData = array();
|
||||||
$oKPI = new ExecutionKPI();
|
$oKPI = new ExecutionKPI();
|
||||||
@@ -927,7 +925,7 @@ class CMDBSource
|
|||||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||||
}
|
}
|
||||||
|
|
||||||
while ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
|
while ($aRow = $oResult->fetch_array($iMode))
|
||||||
{
|
{
|
||||||
$aData[] = $aRow;
|
$aData[] = $aRow;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1237,6 +1237,30 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
],
|
],
|
||||||
|
'activity_panel.prefilter_state_changes_on_logs' => [
|
||||||
|
'type' => 'bool',
|
||||||
|
'description' => 'Whether the "State changes" filter should be set by default on all log tabs.',
|
||||||
|
'default' => false,
|
||||||
|
'value' => false,
|
||||||
|
'source_of_value' => '',
|
||||||
|
'show_in_conf_sample' => false,
|
||||||
|
],
|
||||||
|
'activity_panel.prefilter_edits_on_logs' => [
|
||||||
|
'type' => 'bool',
|
||||||
|
'description' => 'Whether the "Edits" filter should be set by default on all log tabs.',
|
||||||
|
'default' => false,
|
||||||
|
'value' => false,
|
||||||
|
'source_of_value' => '',
|
||||||
|
'show_in_conf_sample' => false,
|
||||||
|
],
|
||||||
|
'activity_panel.hide_avatars' => [
|
||||||
|
'type' => 'array',
|
||||||
|
'description' => 'GUIs IDs ("backoffice", "itop-portal" for the standard end-users portal, ...) in which the user avatars should be hidden and replaced if possible by their initials (eg. array("backoffice", "itop-portal", "another-portal-id"))',
|
||||||
|
'default' => [],
|
||||||
|
'value' => [],
|
||||||
|
'source_of_value' => '',
|
||||||
|
'show_in_conf_sample' => false,
|
||||||
|
],
|
||||||
'activity_panel.show_author_name_below_entries' => [
|
'activity_panel.show_author_name_below_entries' => [
|
||||||
'type' => 'bool',
|
'type' => 'bool',
|
||||||
'description' => 'Whether or not to show the author friendlyname next to the date on the last entry.',
|
'description' => 'Whether or not to show the author friendlyname next to the date on the last entry.',
|
||||||
@@ -1263,9 +1287,9 @@ class Config
|
|||||||
],
|
],
|
||||||
'mentions.allowed_classes' => [
|
'mentions.allowed_classes' => [
|
||||||
'type' => 'array',
|
'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 (eg. "@" => "Person")',
|
'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\'")',
|
||||||
'default' => [
|
'default' => [
|
||||||
'@' => 'Person',
|
'@' => 'SELECT Person WHERE status = \'active\'',
|
||||||
],
|
],
|
||||||
'value' => false,
|
'value' => false,
|
||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
@@ -1399,6 +1423,14 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => true,
|
'show_in_conf_sample' => true,
|
||||||
],
|
],
|
||||||
|
'developer_mode.enabled' => [
|
||||||
|
'type' => 'bool',
|
||||||
|
'description' => 'If true then unlocks dev env functionalities, see \utils::IsDevelopmentEnvironment',
|
||||||
|
'default' => null,
|
||||||
|
'value' => null,
|
||||||
|
'source_of_value' => '',
|
||||||
|
'show_in_conf_sample' => false,
|
||||||
|
],
|
||||||
'theme.enable_precompilation' => [
|
'theme.enable_precompilation' => [
|
||||||
'type' => 'bool',
|
'type' => 'bool',
|
||||||
'description' => 'If false, theme compilation will not use any precompiled file setup optimization.)',
|
'description' => 'If false, theme compilation will not use any precompiled file setup optimization.)',
|
||||||
|
|||||||
@@ -1277,13 +1277,20 @@ abstract class DBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
// If the object if not issued from a query but constructed programmatically
|
// If the object if not issued from a query but constructed programmatically
|
||||||
// the label may be empty. In this case run a query to get the object's friendly name
|
// the label may be empty. In this case run a query to get the object's friendly name
|
||||||
$oTmpObj = MetaModel::GetObject($sObjClass, $sObjKey, false);
|
$sObjOql = 'SELECT '.$sObjClass.' WHERE id='.$sObjKey;
|
||||||
if (is_object($oTmpObj))
|
$oObjFilter = DBSearch::FromOQL($sObjOql);
|
||||||
{
|
$oSet = new DBObjectSet($oObjFilter);
|
||||||
|
|
||||||
|
// we will only use id and friendlyname, so let's remove other fields !
|
||||||
|
$aAttToLoad = [
|
||||||
|
$sObjClass => [],
|
||||||
|
];
|
||||||
|
$oSet->OptimizeColumnLoad($aAttToLoad);
|
||||||
|
|
||||||
|
$oTmpObj = $oSet->Fetch();
|
||||||
|
if (is_object($oTmpObj)) {
|
||||||
$sHtmlLabel = $oTmpObj->GetName();
|
$sHtmlLabel = $oTmpObj->GetName();
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
// May happen in case the target object is not in the list of allowed values for this attribute
|
// May happen in case the target object is not in the list of allowed values for this attribute
|
||||||
$sHtmlLabel = "<em>$sObjClass::$sObjKey</em>";
|
$sHtmlLabel = "<em>$sObjClass::$sObjKey</em>";
|
||||||
}
|
}
|
||||||
@@ -1859,9 +1866,7 @@ abstract class DBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
/** @var \AttributeExternalKey $oAtt */
|
/** @var \AttributeExternalKey $oAtt */
|
||||||
$sTargetClass = $oAtt->GetTargetClass();
|
$sTargetClass = $oAtt->GetTargetClass();
|
||||||
$oTargetObj = MetaModel::GetObject($sTargetClass, $toCheck, false /*must be found*/, true /*allow all data*/);
|
if (false === MetaModel::IsObjectInDB($sTargetClass, $toCheck)) {
|
||||||
if (is_null($oTargetObj))
|
|
||||||
{
|
|
||||||
return "Target object not found ($sTargetClass::$toCheck)";
|
return "Target object not found ($sTargetClass::$toCheck)";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,22 +63,23 @@ else
|
|||||||
|
|
||||||
abstract class DBSearch
|
abstract class DBSearch
|
||||||
{
|
{
|
||||||
/** @internal */
|
/** @internal */
|
||||||
const JOIN_POINTING_TO = 0;
|
const JOIN_POINTING_TO = 0;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
const JOIN_REFERENCED_BY = 1;
|
const JOIN_REFERENCED_BY = 1;
|
||||||
|
|
||||||
protected $m_bNoContextParameters = false;
|
protected $m_bNoContextParameters = false;
|
||||||
|
/** @var array For {@see iQueryModifier} impl */
|
||||||
protected $m_aModifierProperties = array();
|
protected $m_aModifierProperties = array();
|
||||||
protected $m_bArchiveMode = false;
|
protected $m_bArchiveMode = false;
|
||||||
protected $m_bShowObsoleteData = true;
|
protected $m_bShowObsoleteData = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DBSearch constructor.
|
* DBSearch constructor.
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @see DBSearch::FromOQL()
|
* @see DBSearch::FromOQL()
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->Init();
|
$this->Init();
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ class DeletionPlan
|
|||||||
|
|
||||||
public function SetDeletionIssues($oObject, $aIssues, $bSecurityIssue)
|
public function SetDeletionIssues($oObject, $aIssues, $bSecurityIssue)
|
||||||
{
|
{
|
||||||
if (count($aIssues) > 0)
|
if (count($aIssues ?? []) > 0)
|
||||||
{
|
{
|
||||||
$sClass = get_class($oObject);
|
$sClass = get_class($oObject);
|
||||||
$iId = $oObject->GetKey();
|
$iId = $oObject->GetKey();
|
||||||
|
|||||||
@@ -1448,7 +1448,7 @@ class DisplayableGraph extends SimpleGraph
|
|||||||
$oP->add("<div class=\"not-printable\">\n");
|
$oP->add("<div class=\"not-printable\">\n");
|
||||||
$oUiSearchBlock = new Panel($sSftShort, [],Panel::ENUM_COLOR_CYAN, 'ds_flash');
|
$oUiSearchBlock = new Panel($sSftShort, [],Panel::ENUM_COLOR_CYAN, 'ds_flash');
|
||||||
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
|
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
|
||||||
|
$oUiSearchBlock->SetIsCollapsible(true);
|
||||||
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
|
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
|
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use PhpParser\Node\Expr\Assign;
|
use PhpParser\Node\Expr\Assign;
|
||||||
|
use PhpParser\Node\Expr\Variable;
|
||||||
|
use PhpParser\Parser;
|
||||||
use PhpParser\ParserFactory;
|
use PhpParser\ParserFactory;
|
||||||
use PhpParser\PrettyPrinter\Standard;
|
use PhpParser\PrettyPrinter\Standard;
|
||||||
|
|
||||||
@@ -80,38 +82,49 @@ class iTopConfigParser
|
|||||||
* @param \PhpParser\Parser $oParser
|
* @param \PhpParser\Parser $oParser
|
||||||
* @param $sConfig
|
* @param $sConfig
|
||||||
*
|
*
|
||||||
* @return \Combodo\iTop\Config\Validator\ConfigNodesVisitor
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function BrowseFile(\PhpParser\Parser $oParser, $sConfig)
|
private function BrowseFile(Parser $oParser, $sConfig)
|
||||||
{
|
{
|
||||||
$prettyPrinter = new Standard();
|
$prettyPrinter = new Standard();
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
$aNodes = $oParser->parse($sConfig);
|
$aNodes = $oParser->parse($sConfig);
|
||||||
}
|
}
|
||||||
catch (\Error $e)
|
catch (\Error $e) {
|
||||||
{
|
|
||||||
$sMessage = Dict::Format('config-parse-error', $e->getMessage(), $e->getLine());
|
$sMessage = Dict::Format('config-parse-error', $e->getMessage(), $e->getLine());
|
||||||
$this->oException = new \Exception($sMessage, 0, $e);
|
$this->oException = new \Exception($sMessage, 0, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($aNodes as $oAssignation)
|
foreach ($aNodes as $sKey => $oNode) {
|
||||||
{
|
// With PhpParser 3 we had an Assign node at root
|
||||||
if (! $oAssignation instanceof Assign)
|
// In PhpParser 4 the root node is now an Expression
|
||||||
{
|
|
||||||
|
if (false === ($oNode instanceof \PhpParser\Node\Stmt\Expression)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/** @var \PhpParser\Node\Stmt\Expression $oNode */
|
||||||
|
|
||||||
|
if (false === ($oNode->expr instanceof Assign)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/** @var Assign $oAssignation */
|
||||||
|
$oAssignation = $oNode->expr;
|
||||||
|
|
||||||
|
if (false === ($oAssignation->var instanceof Variable)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (false === ($oAssignation->expr instanceof PhpParser\Node\Expr\Array_)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sCurrentRootVar = $oAssignation->var->name;
|
$sCurrentRootVar = $oAssignation->var->name;
|
||||||
if (!array_key_exists($sCurrentRootVar, $this->aVarsMap))
|
if (!array_key_exists($sCurrentRootVar, $this->aVarsMap)) {
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$aCurrentRootVarMap =& $this->aVarsMap[$sCurrentRootVar];
|
$aCurrentRootVarMap =& $this->aVarsMap[$sCurrentRootVar];
|
||||||
|
|
||||||
foreach ($oAssignation->expr->items as $oItem)
|
foreach ($oAssignation->expr->items as $oItem) {
|
||||||
{
|
|
||||||
$sValue = $prettyPrinter->prettyPrintExpr($oItem->value);
|
$sValue = $prettyPrinter->prettyPrintExpr($oItem->value);
|
||||||
$aCurrentRootVarMap[$oItem->key->value] = $sValue;
|
$aCurrentRootVarMap[$oItem->key->value] = $sValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,7 +577,7 @@ JS
|
|||||||
oEditor.on( 'instanceReady', function() {
|
oEditor.on( 'instanceReady', function() {
|
||||||
if(!CKEDITOR.env.iOS && $('#'+oEditor.id+'_toolbox .ibo-vendors-ckeditor--toolbar-fullscreen-button').length == 0)
|
if(!CKEDITOR.env.iOS && $('#'+oEditor.id+'_toolbox .ibo-vendors-ckeditor--toolbar-fullscreen-button').length == 0)
|
||||||
{
|
{
|
||||||
$('#'+oEditor.id+'_toolbox').append('<span class="ibo-vendors-ckeditor--toolbar-fullscreen-button" data-role="ibo-vendors-ckeditor--toolbar-fullscreen-button" title="$sToggleFullScreen" style="background-image:url(\\'$sAbsoluteUrlAppRoot/images/full-screen.png\\')"> </span>');
|
$('#'+oEditor.id+'_toolbox').append('<span class="ibo-vendors-ckeditor--toolbar-fullscreen-button editor-fullscreen-button" data-role="ibo-vendors-ckeditor--toolbar-fullscreen-button" title="$sToggleFullScreen"> </span>');
|
||||||
$('#'+oEditor.id+'_toolbox .ibo-vendors-ckeditor--toolbar-fullscreen-button').on('click', function() {
|
$('#'+oEditor.id+'_toolbox .ibo-vendors-ckeditor--toolbar-fullscreen-button').on('click', function() {
|
||||||
oEditor.execCommand('maximize');
|
oEditor.execCommand('maximize');
|
||||||
if ($(this).closest('.cke_maximized').length != 0)
|
if ($(this).closest('.cke_maximized').length != 0)
|
||||||
|
|||||||
@@ -545,6 +545,7 @@ class LogChannels
|
|||||||
public const DEADLOCK = 'DeadLock';
|
public const DEADLOCK = 'DeadLock';
|
||||||
public const INLINE_IMAGE = 'InlineImage';
|
public const INLINE_IMAGE = 'InlineImage';
|
||||||
public const PORTAL = 'portal';
|
public const PORTAL = 'portal';
|
||||||
|
public const CMDB_SOURCE = 'cmdbsource';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -676,11 +677,22 @@ abstract class LogAPI
|
|||||||
* @param string $sChannel
|
* @param string $sChannel
|
||||||
*
|
*
|
||||||
* @return string one of the LEVEL_* const value : the one configured it if exists, otherwise default log level for this channel
|
* @return string one of the LEVEL_* const value : the one configured it if exists, otherwise default log level for this channel
|
||||||
* Config can be done globally : `'log_level_min' => LogAPI::LEVEL_TRACE,`
|
* Config can be set :
|
||||||
* Or per channel : `'log_level_min' => ['InlineImage' => LogAPI::LEVEL_TRACE, 'UserRequest' => LogAPI::LEVEL_TRACE],`
|
* * globally : `'log_level_min' => LogAPI::LEVEL_TRACE,`
|
||||||
|
* * per channel :
|
||||||
|
* ```
|
||||||
|
* 'log_level_min' => [
|
||||||
|
* '' => LogAPI::LEVEL_ERROR, // default log level for channels not listed below
|
||||||
|
* 'InlineImage' => LogAPI::LEVEL_TRACE,
|
||||||
|
* 'UserRequest' => LogAPI::LEVEL_TRACE
|
||||||
|
* ],
|
||||||
|
* ```
|
||||||
*
|
*
|
||||||
* @uses \LogAPI::GetConfig()
|
* @uses \LogAPI::GetConfig()
|
||||||
|
* @uses `log_level_min` config parameter
|
||||||
* @uses \LogAPI::GetLevelDefault
|
* @uses \LogAPI::GetLevelDefault
|
||||||
|
*
|
||||||
|
* @link https://www.itophub.io/wiki/page?id=3_0_0%3Aadmin%3Alog iTop log reference
|
||||||
*/
|
*/
|
||||||
protected static function GetMinLogLevel($sChannel)
|
protected static function GetMinLogLevel($sChannel)
|
||||||
{
|
{
|
||||||
@@ -704,7 +716,12 @@ abstract class LogAPI
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT])) {
|
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT])) {
|
||||||
return $sLogLevelMin[$sChannel];
|
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.
|
||||||
|
if (isset($sLogLevelMin[''])) {
|
||||||
|
return $sLogLevelMin[''];
|
||||||
}
|
}
|
||||||
|
|
||||||
return static::GetLevelDefault();
|
return static::GetLevelDefault();
|
||||||
@@ -824,6 +841,7 @@ class DeadLockLog extends LogAPI
|
|||||||
class DeprecatedCallsLog extends LogAPI
|
class DeprecatedCallsLog extends LogAPI
|
||||||
{
|
{
|
||||||
public const ENUM_CHANNEL_PHP_METHOD = 'deprecated-php-method';
|
public const ENUM_CHANNEL_PHP_METHOD = 'deprecated-php-method';
|
||||||
|
public const ENUM_CHANNEL_PHP_LIBMETHOD = 'deprecated-php-libmethod';
|
||||||
public const ENUM_CHANNEL_FILE = 'deprecated-file';
|
public const ENUM_CHANNEL_FILE = 'deprecated-file';
|
||||||
public const CHANNEL_DEFAULT = self::ENUM_CHANNEL_PHP_METHOD;
|
public const CHANNEL_DEFAULT = self::ENUM_CHANNEL_PHP_METHOD;
|
||||||
|
|
||||||
@@ -832,12 +850,79 @@ class DeprecatedCallsLog extends LogAPI
|
|||||||
/** @var \FileLog we want our own instance ! */
|
/** @var \FileLog we want our own instance ! */
|
||||||
protected static $m_oFileLog = null;
|
protected static $m_oFileLog = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $sTargetFile
|
||||||
|
*
|
||||||
|
*@uses \set_error_handler() to catch deprecated notices
|
||||||
|
*
|
||||||
|
* @since 3.0.0 N°3002 logs deprecated notices in called code
|
||||||
|
*/
|
||||||
public static function Enable($sTargetFile = null): void
|
public static function Enable($sTargetFile = null): void
|
||||||
{
|
{
|
||||||
if (empty($sTargetFile)) {
|
if (empty($sTargetFile)) {
|
||||||
$sTargetFile = APPROOT.'log/deprecated-calls.log';
|
$sTargetFile = APPROOT.'log/deprecated-calls.log';
|
||||||
}
|
}
|
||||||
parent::Enable($sTargetFile);
|
parent::Enable($sTargetFile);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$bIsLogLevelEnabled = static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD);
|
||||||
|
}
|
||||||
|
catch (ConfigException $e) {
|
||||||
|
$bIsLogLevelEnabled = false;
|
||||||
|
}
|
||||||
|
if ($bIsLogLevelEnabled) {
|
||||||
|
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will catch a message for all E_DEPRECATED and E_USER_DEPRECATED errors.
|
||||||
|
* This handler is set in DeprecatedCallsLog::Enable
|
||||||
|
*
|
||||||
|
* @param int $errno
|
||||||
|
* @param string $errstr
|
||||||
|
* @param string $errfile
|
||||||
|
* @param int $errline
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
* @since 3.0.0 N°3002
|
||||||
|
* @noinspection SpellCheckingInspection
|
||||||
|
*/
|
||||||
|
public static function DeprecatedNoticesErrorHandler(int $errno, string $errstr, string $errfile, int $errline): bool
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
(\E_USER_DEPRECATED !== $errno)
|
||||||
|
&& (\E_DEPRECATED !== $errno)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
|
||||||
|
$iStackDeprecatedMethodLevel = 2; // level 0 = current method, level 1 = @trigger_error, level 2 = method containing the `trigger_error` call
|
||||||
|
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
|
||||||
|
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
|
||||||
|
if (($sDeprecatedObject === __CLASS__) && ($sDeprecatedMethod === 'Log')) {
|
||||||
|
// We are generating a trigger_error ourselves, we don't want to trace them !
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
|
||||||
|
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
|
||||||
|
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
|
||||||
|
|
||||||
|
$iStackCallerMethodLevel = $iStackDeprecatedMethodLevel + 1; // level 3 = caller of the deprecated method
|
||||||
|
if (array_key_exists($iStackCallerMethodLevel, $aStack)) {
|
||||||
|
$sCallerObject = $aStack[3]['class'];
|
||||||
|
$sCallerMethod = $aStack[3]['function'];
|
||||||
|
$sMessage .= " ({$sCallerObject}::{$sCallerMethod})";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($errstr)) {
|
||||||
|
$sMessage .= ' : '.$errstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_LIBMETHOD);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function GetLevelDefault(): string
|
protected static function GetLevelDefault(): string
|
||||||
@@ -897,16 +982,17 @@ class DeprecatedCallsLog extends LogAPI
|
|||||||
}
|
}
|
||||||
|
|
||||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
||||||
|
$iStackDeprecatedMethodLevel = 1; // level 0 = current method, level 1 = method containing the `NotifyDeprecatedPhpMethod` call
|
||||||
$sDeprecatedObject = $aStack[1]['class'];
|
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
|
||||||
$sDeprecatedMethod = $aStack[1]['function'];
|
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
|
||||||
$sCallerFile = $aStack[1]['file'];
|
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
|
||||||
$sCallerLine = $aStack[1]['line'];
|
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
|
||||||
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
|
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
|
||||||
|
|
||||||
if (array_key_exists(2, $aStack)) {
|
$iStackCallerMethodLevel = $iStackDeprecatedMethodLevel + 1; // level 2 = caller of the deprecated method
|
||||||
$sCallerObject = $aStack[2]['class'];
|
if (array_key_exists($iStackCallerMethodLevel, $aStack)) {
|
||||||
$sCallerMethod = $aStack[2]['function'];
|
$sCallerObject = $aStack[$iStackCallerMethodLevel]['class'];
|
||||||
|
$sCallerMethod = $aStack[$iStackCallerMethodLevel]['function'];
|
||||||
$sMessage .= " ({$sCallerObject}::{$sCallerMethod})";
|
$sMessage .= " ({$sCallerObject}::{$sCallerMethod})";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -914,12 +1000,12 @@ class DeprecatedCallsLog extends LogAPI
|
|||||||
$sMessage .= ' : '.$sAdditionalMessage;
|
$sMessage .= ' : '.$sAdditionalMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
static::Warning($sMessage, static::ENUM_CHANNEL_PHP_METHOD);
|
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_METHOD);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array()): void
|
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array()): void
|
||||||
{
|
{
|
||||||
if (utils::IsDevelopmentEnvironment()) {
|
if (true === utils::IsDevelopmentEnvironment()) {
|
||||||
trigger_error($sMessage, E_USER_DEPRECATED);
|
trigger_error($sMessage, E_USER_DEPRECATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1116,7 +1116,6 @@ abstract class MetaModel
|
|||||||
*/
|
*/
|
||||||
final public static function GetFilterCodeOrigin($sClass, $sAttCode)
|
final public static function GetFilterCodeOrigin($sClass, $sAttCode)
|
||||||
{
|
{
|
||||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
|
||||||
self::_check_subclass($sClass);
|
self::_check_subclass($sClass);
|
||||||
|
|
||||||
return self::$m_aFilterOrigins[$sClass][$sAttCode];
|
return self::$m_aFilterOrigins[$sClass][$sAttCode];
|
||||||
@@ -6957,7 +6956,7 @@ abstract class MetaModel
|
|||||||
* @param int $iKey id value of the object to retrieve
|
* @param int $iKey id value of the object to retrieve
|
||||||
* @param bool $bMustBeFound see throws ArchivedObjectException
|
* @param bool $bMustBeFound see throws ArchivedObjectException
|
||||||
* @param bool $bAllowAllData if true then user rights will be bypassed - use with care!
|
* @param bool $bAllowAllData if true then user rights will be bypassed - use with care!
|
||||||
* @param null $aModifierProperties
|
* @param array $aModifierProperties properties for {@see iQueryModifier} impl
|
||||||
*
|
*
|
||||||
* @return \DBObject null if : (the object is not found) or (archive mode disabled and object is archived and
|
* @return \DBObject null if : (the object is not found) or (archive mode disabled and object is archived and
|
||||||
* $bMustBeFound=false)
|
* $bMustBeFound=false)
|
||||||
@@ -6977,12 +6976,9 @@ abstract class MetaModel
|
|||||||
|
|
||||||
if (!utils::IsArchiveMode() && $oObject->IsArchived())
|
if (!utils::IsArchiveMode() && $oObject->IsArchived())
|
||||||
{
|
{
|
||||||
if ($bMustBeFound)
|
if ($bMustBeFound) {
|
||||||
{
|
|
||||||
throw new ArchivedObjectException("The object $sClass::$iKey is archived");
|
throw new ArchivedObjectException("The object $sClass::$iKey is archived");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6990,6 +6986,35 @@ abstract class MetaModel
|
|||||||
return $oObject;
|
return $oObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $sClass
|
||||||
|
* @param int $iKey
|
||||||
|
*
|
||||||
|
* @return bool True if the object of $sClass and $iKey exists in the DB -no matter the current user restrictions-, false otherwise meaning:
|
||||||
|
* - It could be in memory for now and is not persisted yet
|
||||||
|
* - It is neither in memory nor DB
|
||||||
|
*
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \MySQLException
|
||||||
|
* @throws \MySQLQueryHasNoResultException
|
||||||
|
* @since 3.0.0 N°4173
|
||||||
|
*/
|
||||||
|
public static function IsObjectInDB(string $sClass, int $iKey): bool
|
||||||
|
{
|
||||||
|
// Note: We take the root class to ensure that there is a corresponding table in the DB
|
||||||
|
// as some intermediate classes can have no table in the DB.
|
||||||
|
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||||
|
|
||||||
|
$sTable = MetaModel::DBGetTable($sRootClass);
|
||||||
|
$sKeyCol = MetaModel::DBGetKey($sRootClass);
|
||||||
|
$sEscapedKey = CMDBSource::Quote($iKey);
|
||||||
|
|
||||||
|
$sQuery = "SELECT count(*) FROM `{$sTable}` WHERE `{$sKeyCol}` = {$sEscapedKey}";
|
||||||
|
$iCount = (int) CMDBSource::QueryToScalar($sQuery);
|
||||||
|
|
||||||
|
return $iCount === 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for the specified class and id. If the object is archived it will be returned anyway (this is for pre-2.4
|
* Search for the specified class and id. If the object is archived it will be returned anyway (this is for pre-2.4
|
||||||
* module compatibility, see N.1108)
|
* module compatibility, see N.1108)
|
||||||
@@ -7244,7 +7269,6 @@ abstract class MetaModel
|
|||||||
*/
|
*/
|
||||||
public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues)
|
public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues)
|
||||||
{
|
{
|
||||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
|
||||||
// $aValues is an array of $sAttCode => $value
|
// $aValues is an array of $sAttCode => $value
|
||||||
$sSQL = $oFilter->MakeUpdateQuery($aValues);
|
$sSQL = $oFilter->MakeUpdateQuery($aValues);
|
||||||
if (!self::DBIsReadOnly()) {
|
if (!self::DBIsReadOnly()) {
|
||||||
|
|||||||
@@ -504,21 +504,38 @@ class ormCaseLog {
|
|||||||
$sHtml .= '</td></tr></table>';
|
$sHtml .= '</td></tr></table>';
|
||||||
return $sHtml;
|
return $sHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new entry to the log or merge the given text into the currently modified entry
|
* Add a new entry to the log or merge the given text into the currently modified entry
|
||||||
* and updates the internal index
|
* and updates the internal index
|
||||||
* @param $sText string The text of the new entry
|
*
|
||||||
|
* @param string $sText The text of the new entry
|
||||||
|
* @param string $sOnBehalfOf Display this name instead of current user name
|
||||||
|
* @param null|int $iOnBehalfOfId Use this UserId to author this Entry. If $sOnBehalfOf equals '', it'll be replaced by this User friendlyname
|
||||||
|
*
|
||||||
|
* @throws \ArchivedObjectException
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \OQLException
|
||||||
|
*
|
||||||
|
* @since 3.0.0 New $iOnBehalfOfId parameter
|
||||||
|
* @since 3.0.0 May throw \ArchivedObjectException exception
|
||||||
*/
|
*/
|
||||||
public function AddLogEntry($sText, $sOnBehalfOf = '')
|
public function AddLogEntry(string $sText, $sOnBehalfOf = '', $iOnBehalfOfId = null)
|
||||||
{
|
{
|
||||||
$sText = HTMLSanitizer::Sanitize($sText);
|
$sText = HTMLSanitizer::Sanitize($sText);
|
||||||
$sDate = date(AttributeDateTime::GetInternalFormat());
|
$sDate = date(AttributeDateTime::GetInternalFormat());
|
||||||
if ($sOnBehalfOf == '')
|
if ($sOnBehalfOf == '' && $iOnBehalfOfId === null) {
|
||||||
{
|
|
||||||
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
||||||
$iUserId = UserRights::GetUserId();
|
$iUserId = UserRights::GetUserId();
|
||||||
}
|
}
|
||||||
|
elseif ($iOnBehalfOfId !== null) {
|
||||||
|
$iUserId = $iOnBehalfOfId;
|
||||||
|
/* @var User $oUser */
|
||||||
|
$oUser = MetaModel::GetObject('User', $iUserId, false, true);
|
||||||
|
if ($oUser !== null && $sOnBehalfOf === '') {
|
||||||
|
$sOnBehalfOf = $oUser->GetFriendlyName();
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$iUserId = null;
|
$iUserId = null;
|
||||||
|
|||||||
@@ -388,15 +388,18 @@ class SQLObjectQuery extends SQLQuery
|
|||||||
{
|
{
|
||||||
if (count($this->__aSelectedIdFields) > 0)
|
if (count($this->__aSelectedIdFields) > 0)
|
||||||
{
|
{
|
||||||
$aCountFields = array();
|
$aCountFields = [];
|
||||||
foreach ($this->__aSelectedIdFields as $sFieldExpr)
|
$aCountI = [];
|
||||||
{
|
$i = 0;
|
||||||
$aCountFields[] = "COALESCE($sFieldExpr, 0)"; // Null values are excluded from the count
|
foreach ($this->__aSelectedIdFields as $sFieldExpr) {
|
||||||
|
$aCountFields[] = "COALESCE($sFieldExpr, 0) AS idCount$i"; // Null values are excluded from the count
|
||||||
|
$aCountI[] = 'idCount'.$i++;
|
||||||
}
|
}
|
||||||
$sCountFields = implode(', ', $aCountFields);
|
$sCountFields = implode(', ', $aCountFields);
|
||||||
|
$sCountI = implode('+ ', $aCountI);
|
||||||
// Count can be limited for performance reason, in this case the total amount is not important,
|
// Count can be limited for performance reason, in this case the total amount is not important,
|
||||||
// we only need to know if the number of entries is greater than a certain amount.
|
// we only need to know if the number of entries is greater than a certain amount.
|
||||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep DISTINCT $sCountFields $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _alderaan_";
|
$sSQL = "SELECT COUNT(*) AS COUNT FROM (SELECT$sLineSep DISTINCT $sCountFields $sLineSep FROM $sFrom$sLineSep WHERE $sWhere $sLimit) AS _alderaan_ WHERE $sCountI>0";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -333,77 +333,135 @@ abstract class User extends cmdbAbstractObject
|
|||||||
{
|
{
|
||||||
parent::DoCheckToWrite();
|
parent::DoCheckToWrite();
|
||||||
|
|
||||||
// Note: This MUST be factorized later: declare unique keys (set of columns) in the data model
|
$oAddon = UserRights::GetModuleInstance();
|
||||||
$aChanges = $this->ListChanges();
|
$aChanges = $this->ListChanges();
|
||||||
if (array_key_exists('login', $aChanges))
|
if (array_key_exists('login', $aChanges)) {
|
||||||
{
|
// Check login uniqueness
|
||||||
if (strcasecmp($this->Get('login'), $this->GetOriginal('login')) !== 0)
|
if (strcasecmp($this->Get('login'), $this->GetOriginal('login')) !== 0) {
|
||||||
{
|
|
||||||
$sNewLogin = $aChanges['login'];
|
$sNewLogin = $aChanges['login'];
|
||||||
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT User WHERE login = :newlogin");
|
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT User WHERE login = :newlogin");
|
||||||
if (!$this->IsNew())
|
if (!$this->IsNew()) {
|
||||||
{
|
|
||||||
$oSearch->AddCondition('id', $this->GetKey(), '!=');
|
$oSearch->AddCondition('id', $this->GetKey(), '!=');
|
||||||
}
|
}
|
||||||
$oSet = new DBObjectSet($oSearch, array(), array('newlogin' => $sNewLogin));
|
$oSet = new DBObjectSet($oSearch, array(), array('newlogin' => $sNewLogin));
|
||||||
if ($oSet->Count() > 0)
|
if ($oSet->Count() > 0) {
|
||||||
{
|
|
||||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:LoginMustBeUnique', $sNewLogin);
|
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:LoginMustBeUnique', $sNewLogin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check that this user has at least one profile assigned when profiles have changed
|
|
||||||
if (array_key_exists('profile_list', $aChanges))
|
// A User cannot disable himself
|
||||||
{
|
if ($this->IsCurrentUser()) {
|
||||||
$oSet = $this->Get('profile_list');
|
if (isset($aChanges['status']) && ($this->Get('status') == 'disabled')) {
|
||||||
if ($oSet->Count() == 0)
|
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:StatusChangeIsNotAllowed');
|
||||||
{
|
|
||||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneProfileIsNeeded');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that this user has at least one profile assigned when profiles have changed
|
||||||
|
if (array_key_exists('profile_list', $aChanges)) {
|
||||||
|
/** @var \DBObjectSet $oSet */
|
||||||
|
$oSet = $this->Get('profile_list');
|
||||||
|
if ($oSet->Count() == 0) {
|
||||||
|
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneProfileIsNeeded');
|
||||||
|
}
|
||||||
|
|
||||||
|
// A user cannot add to themself a profile denying the access to the backoffice
|
||||||
|
$aForbiddenProfiles = PortalDispatcherData::GetData('backoffice')['deny'];
|
||||||
|
if ($this->IsCurrentUser()) {
|
||||||
|
$oSet->Rewind();
|
||||||
|
$aProfiles = [];
|
||||||
|
while ($oUserProfile = $oSet->Fetch()) {
|
||||||
|
$sProfile = $oUserProfile->Get('profile');
|
||||||
|
if (in_array($sProfile, $aForbiddenProfiles)) {
|
||||||
|
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:ProfileNotAllowed', $sProfile);
|
||||||
|
}
|
||||||
|
$aProfiles[$oUserProfile->Get('profileid')] = $sProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array(ADMIN_PROFILE_NAME, $aProfiles)) {
|
||||||
|
// Check if the user is yet allowed to modify Users
|
||||||
|
if (method_exists($oAddon, 'ResetCache')) {
|
||||||
|
$aCurrentProfiles = $_SESSION['profile_list'] ?? null;
|
||||||
|
// Set the current profiles into a session variable (not yet in the database)
|
||||||
|
$_SESSION['profile_list'] = $aProfiles;
|
||||||
|
|
||||||
|
$oAddon->ResetCache();
|
||||||
|
if (!$oAddon->IsActionAllowed($this, 'User', UR_ACTION_MODIFY, null)) {
|
||||||
|
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:CurrentProfilesHaveInsufficientRights');
|
||||||
|
}
|
||||||
|
$oAddon->ResetCache();
|
||||||
|
|
||||||
|
if (is_null($aCurrentProfiles)) {
|
||||||
|
unset($_SESSION['profile_list']);
|
||||||
|
} else {
|
||||||
|
$_SESSION['profile_list'] = $aCurrentProfiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Only administrators can manage administrators
|
// Only administrators can manage administrators
|
||||||
if (UserRights::IsAdministrator($this) && !UserRights::IsAdministrator())
|
if (UserRights::IsAdministrator($this) && !UserRights::IsAdministrator()) {
|
||||||
{
|
|
||||||
$this->m_aCheckIssues[] = Dict::S('UI:Login:Error:AccessRestricted');
|
$this->m_aCheckIssues[] = Dict::S('UI:Login:Error:AccessRestricted');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!UserRights::IsAdministrator())
|
// A contact is mandatory (an administrator can bypass it but not for himself)
|
||||||
{
|
if ((!UserRights::IsAdministrator() || $this->IsCurrentUser())
|
||||||
$oUser = UserRights::GetUserObject();
|
&& !$this->IsNew()
|
||||||
$oAddon = UserRights::GetModuleInstance();
|
&& isset($aChanges['contactid'])
|
||||||
if (!is_null($oUser) && method_exists($oAddon, 'GetUserOrgs'))
|
&& empty($this->Get('contactid'))) {
|
||||||
{
|
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:PersonIsMandatory');
|
||||||
if ((empty($this->GetOriginal('contactid')) && !($this->IsNew())) || empty($this->Get('contactid')))
|
}
|
||||||
{
|
|
||||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:PersonIsMandatory');
|
// Allowed orgs must contains the user org (if any)
|
||||||
|
if (!empty($this->Get('org_id')) && !UserRights::IsAdministrator($this)) {
|
||||||
|
// Get the user org and all its parent orgs
|
||||||
|
$aUserOrgs = [$this->Get('org_id')];
|
||||||
|
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
|
||||||
|
if ($sHierarchicalKeyCode !== false) {
|
||||||
|
$sOrgQuery = 'SELECT Org FROM Organization AS Org JOIN Organization AS Root ON Org.'.$sHierarchicalKeyCode.' ABOVE Root.id WHERE Root.id = :id';
|
||||||
|
$oOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sOrgQuery), [], ['id' => $this->Get('org_id')]);
|
||||||
|
while ($aRow = $oOrgSet->FetchAssoc()) {
|
||||||
|
$oOrg = $aRow['Org'];
|
||||||
|
$aUserOrgs[] = $oOrg->GetKey();
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
// Check the allowed orgs list
|
||||||
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
|
$oSet = $this->get('allowed_org_list');
|
||||||
if (count($aOrgs) > 0)
|
if ($oSet->Count() > 0) {
|
||||||
{
|
$bFound = false;
|
||||||
// Check that the modified User belongs to one of our organization
|
while ($oOrg = $oSet->Fetch()) {
|
||||||
if (!in_array($this->GetOriginal('org_id'), $aOrgs) && !in_array($this->Get('org_id'), $aOrgs))
|
if (in_array($oOrg->Get('allowed_org_id'), $aUserOrgs)) {
|
||||||
{
|
$bFound = true;
|
||||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:UserOrganizationNotAllowed');
|
break;
|
||||||
}
|
}
|
||||||
// Check users with restricted organizations when allowed organizations have changed
|
}
|
||||||
if ($this->IsNew() || array_key_exists('allowed_org_list', $aChanges))
|
if (!$bFound) {
|
||||||
{
|
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AllowedOrgsMustContainUserOrg');
|
||||||
$oSet = $this->get('allowed_org_list');
|
}
|
||||||
if ($oSet->Count() == 0)
|
}
|
||||||
{
|
}
|
||||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneOrganizationIsNeeded');
|
|
||||||
}
|
if (!UserRights::IsAdministrator()) {
|
||||||
else
|
$oUser = UserRights::GetUserObject();
|
||||||
{
|
if (!is_null($oUser) && method_exists($oAddon, 'GetUserOrgs')) {
|
||||||
$aModifiedLinks = $oSet->ListModifiedLinks();
|
$aOrgs = $oAddon->GetUserOrgs($oUser, ''); // Modifier allowed orgs
|
||||||
foreach ($aModifiedLinks as $oLink)
|
if (count($aOrgs) > 0) {
|
||||||
{
|
// Check that the modified User belongs to one of our organization
|
||||||
if (!in_array($oLink->Get('allowed_org_id'), $aOrgs))
|
if (!in_array($this->GetOriginal('org_id'), $aOrgs) && !in_array($this->Get('org_id'), $aOrgs)) {
|
||||||
{
|
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:UserOrganizationNotAllowed');
|
||||||
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:OrganizationNotAllowed');
|
}
|
||||||
}
|
// Check users with restricted organizations when allowed organizations have changed
|
||||||
|
if ($this->IsNew() || array_key_exists('allowed_org_list', $aChanges)) {
|
||||||
|
$oSet = $this->get('allowed_org_list');
|
||||||
|
if ($oSet->Count() == 0) {
|
||||||
|
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:AtLeastOneOrganizationIsNeeded');
|
||||||
|
} else {
|
||||||
|
$aModifiedLinks = $oSet->ListModifiedLinks();
|
||||||
|
foreach ($aModifiedLinks as $oLink) {
|
||||||
|
if (!in_array($oLink->Get('allowed_org_id'), $aOrgs)) {
|
||||||
|
$this->m_aCheckIssues[] = Dict::S('Class:User/Error:OrganizationNotAllowed');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,14 +471,26 @@ abstract class User extends cmdbAbstractObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
public function DoCheckToDelete(&$oDeletionPlan)
|
||||||
|
{
|
||||||
|
parent::DoCheckToDelete($oDeletionPlan);
|
||||||
|
|
||||||
|
// A user cannot suppress himself
|
||||||
|
if ($this->IsCurrentUser()) {
|
||||||
|
$this->m_bSecurityIssue = true;
|
||||||
|
$this->m_aDeleteIssues[] = Dict::S('UI:Delete:NotAllowedToDelete');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function GetGrantAsHtml($sClass, $iAction)
|
function GetGrantAsHtml($sClass, $iAction)
|
||||||
{
|
{
|
||||||
if (UserRights::IsActionAllowed($sClass, $iAction, null, $this))
|
if (UserRights::IsActionAllowed($sClass, $iAction, null, $this)) {
|
||||||
{
|
|
||||||
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -528,6 +598,19 @@ abstract class User extends cmdbAbstractObject
|
|||||||
}
|
}
|
||||||
parent::DBDeleteSingleObject();
|
parent::DBDeleteSingleObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
* @throws \OQLException
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
protected function IsCurrentUser(): bool
|
||||||
|
{
|
||||||
|
if (is_null(UserRights::GetUserId())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return UserRights::GetUserId() == $this->GetKey();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1011,6 +1094,30 @@ class UserRights
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Person $oPerson Person we try to match against Users contact (also Person objects)
|
||||||
|
* @param bool $bMustBeUnique If true, return null when 2+ Users matching this Person were found. Otherwise return the first one
|
||||||
|
*
|
||||||
|
* @return \DBObject|null
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \MissingQueryArgument
|
||||||
|
* @throws \MySQLException
|
||||||
|
* @throws \MySQLHasGoneAwayException
|
||||||
|
* @since 3.0.0
|
||||||
|
*/
|
||||||
|
public static function GetUserFromPerson(Person $oPerson, bool $bMustBeUnique = true): ?DBObject
|
||||||
|
{
|
||||||
|
$sUserSearch = 'SELECT User WHERE contactid = :id';
|
||||||
|
$oUserSearch = DBObjectSearch::FromOQL($sUserSearch);
|
||||||
|
$oUserSearch->AllowAllData();
|
||||||
|
$oUserSet = new DBObjectSet($oUserSearch, array(), array('id' => $oPerson->GetKey()));
|
||||||
|
if($oUserSet->Count() > 0 && !($oUserSet->Count() > 1 && $bMustBeUnique)){
|
||||||
|
return $oUserSet->Fetch();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
@@ -1110,13 +1217,15 @@ class UserRights
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // If no contact, check if user has a placeholder in they preferences
|
}
|
||||||
else {
|
// If no contact & empty login, check if current user has a placeholder in they preferences
|
||||||
|
elseif ('' === $sLogin) {
|
||||||
$sPlaceholderPictureFilename = appUserPreferences::GetPref($sUserPicturePlaceholderPrefKey, null, static::GetUserId($sLogin));
|
$sPlaceholderPictureFilename = appUserPreferences::GetPref($sUserPicturePlaceholderPrefKey, null, static::GetUserId($sLogin));
|
||||||
if (!empty($sPlaceholderPictureFilename)) {
|
if (!empty($sPlaceholderPictureFilename)) {
|
||||||
$sPictureUrl = utils::GetAbsoluteUrlAppRoot().$sUserPicturesFolder.$sPlaceholderPictureFilename;
|
$sPictureUrl = utils::GetAbsoluteUrlAppRoot().$sUserPicturesFolder.$sPlaceholderPictureFilename;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Else, no contact and no login, then it's for an unknown origin (system, extension, ...)
|
||||||
|
|
||||||
// Update cache
|
// Update cache
|
||||||
static::$m_aCacheContactPictureAbsUrl[$sLogin] = $sPictureUrl;
|
static::$m_aCacheContactPictureAbsUrl[$sLogin] = $sPictureUrl;
|
||||||
|
|||||||
@@ -4,3 +4,4 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
@import "block-csv";
|
@import "block-csv";
|
||||||
|
@import "block-list";
|
||||||
|
|||||||
@@ -8,10 +8,4 @@ $ibo-block-csv--textarea--margin-top: 10px !default;
|
|||||||
min-height: $ibo-block-csv--textarea--min-height;
|
min-height: $ibo-block-csv--textarea--min-height;
|
||||||
margin-top: $ibo-block-csv--textarea--margin-top;
|
margin-top: $ibo-block-csv--textarea--margin-top;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.ibo-block-csv--download-link{
|
|
||||||
@extend .ibo-button;
|
|
||||||
@extend .ibo-is-alternative;
|
|
||||||
@extend .ibo-is-primary;
|
|
||||||
}
|
}
|
||||||
14
css/backoffice/application/display-block/_block-list.scss
Normal file
14
css/backoffice/application/display-block/_block-list.scss
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/*!
|
||||||
|
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
$ibo-block-list--create-icon--margin-right: 0.5rem !default;
|
||||||
|
|
||||||
|
.ibo-block-list--empty-text, .ibo-block-list--create-action{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-block-list--create-icon {
|
||||||
|
margin-right: $ibo-block-list--create-icon--margin-right;
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
$ibo-scrollbar--scrollbar-width: 8px !default;
|
$ibo-scrollbar--scrollbar-width: 8px !default;
|
||||||
|
$ibo-scrollbar--scrollbar-height: $ibo-scrollbar--scrollbar-width !default; /* For horizontal scrollbars */
|
||||||
$ibo-scrollbar--scrollbar-track-background-color: $ibo-color-transparent !default;
|
$ibo-scrollbar--scrollbar-track-background-color: $ibo-color-transparent !default;
|
||||||
$ibo-scrollbar--scrollbar-thumb-background-color: $ibo-color-grey-300 !default;
|
$ibo-scrollbar--scrollbar-thumb-background-color: $ibo-color-grey-300 !default;
|
||||||
$ibo-scrollbar--scrollbar-thumb-border: none !default;
|
$ibo-scrollbar--scrollbar-thumb-border: none !default;
|
||||||
@@ -34,6 +35,7 @@ $ibo-content-block--border: 1px solid $ibo-color-grey-400 !default;
|
|||||||
/* CSS variables */
|
/* CSS variables */
|
||||||
:root{
|
:root{
|
||||||
--ibo-scrollbar--scrollbar-width: #{$ibo-scrollbar--scrollbar-width};
|
--ibo-scrollbar--scrollbar-width: #{$ibo-scrollbar--scrollbar-width};
|
||||||
|
--ibo-scrollbar--scrollbar-height: #{$ibo-scrollbar--scrollbar-height};
|
||||||
--ibo-scrollbar--scrollbar-track-background-color: #{$ibo-scrollbar--scrollbar-track-background-color};
|
--ibo-scrollbar--scrollbar-track-background-color: #{$ibo-scrollbar--scrollbar-track-background-color};
|
||||||
--ibo-scrollbar--scrollbar-thumb-background-color: #{$ibo-scrollbar--scrollbar-thumb-background-color};
|
--ibo-scrollbar--scrollbar-thumb-background-color: #{$ibo-scrollbar--scrollbar-thumb-background-color};
|
||||||
--ibo-scrollbar--scrollbar-thumb-border: #{$ibo-scrollbar--scrollbar-thumb-border};
|
--ibo-scrollbar--scrollbar-thumb-border: #{$ibo-scrollbar--scrollbar-thumb-border};
|
||||||
@@ -60,6 +62,7 @@ $ibo-content-block--border: 1px solid $ibo-color-grey-400 !default;
|
|||||||
/* - For Chrome/Edge/Safari */
|
/* - For Chrome/Edge/Safari */
|
||||||
&::-webkit-scrollbar {
|
&::-webkit-scrollbar {
|
||||||
width: var(--ibo-scrollbar--scrollbar-width);
|
width: var(--ibo-scrollbar--scrollbar-width);
|
||||||
|
height: var(--ibo-scrollbar--scrollbar-height);
|
||||||
}
|
}
|
||||||
&::-webkit-scrollbar-track {
|
&::-webkit-scrollbar-track {
|
||||||
background-color: var(--ibo-scrollbar--scrollbar-track-background-color);
|
background-color: var(--ibo-scrollbar--scrollbar-track-background-color);
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
.ibo-blocklist--empty-text, .ibo-blocklist--create-new{
|
.ibo-block-list--medallion{
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.ibo-blocklist--medallion{
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
> .ibo-medallion-icon--image{
|
> .ibo-medallion-icon--image{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
$ibo-panel-within-main-content--margin-bottom: 200px !default;
|
||||||
|
|
||||||
$ibo-panel-within-main-content--sticky-sentinel-top--top: -1 * $ibo-main-content--padding-top !default;
|
$ibo-panel-within-main-content--sticky-sentinel-top--top: -1 * $ibo-main-content--padding-top !default;
|
||||||
$ibo-panel-within-main-content--sticky-sentinel-top--height: $ibo-main-content--padding-top !default;
|
$ibo-panel-within-main-content--sticky-sentinel-top--height: $ibo-main-content--padding-top !default;
|
||||||
@@ -17,6 +18,8 @@ $ibo-panel-within-main-content--header--top--is-sticky: -1 * $ibo-main-content--
|
|||||||
* - Unlike in JS, there no easy way to find the closest descendant
|
* - Unlike in JS, there no easy way to find the closest descendant
|
||||||
*/
|
*/
|
||||||
.ibo-panel.ibo-has-sticky-header {
|
.ibo-panel.ibo-has-sticky-header {
|
||||||
|
margin-bottom: $ibo-panel-within-main-content--margin-bottom; /* Add a margin below the panel so the dropdown lists can open without problem (N°4039) */
|
||||||
|
|
||||||
/* Stickable header rules */
|
/* Stickable header rules */
|
||||||
> .ibo-sticky-sentinel-top {
|
> .ibo-sticky-sentinel-top {
|
||||||
top: $ibo-panel-within-main-content--sticky-sentinel-top--top;
|
top: $ibo-panel-within-main-content--sticky-sentinel-top--top;
|
||||||
|
|||||||
@@ -7,13 +7,13 @@
|
|||||||
@import "button";
|
@import "button";
|
||||||
@import "button-group";
|
@import "button-group";
|
||||||
@import "breadcrumbs";
|
@import "breadcrumbs";
|
||||||
@import "collapsible-section";
|
|
||||||
@import "quick-create";
|
@import "quick-create";
|
||||||
@import "global-search";
|
@import "global-search";
|
||||||
@import "popover-menu/popover-menu";
|
@import "popover-menu/popover-menu";
|
||||||
@import "popover-menu/popover-menu-item";
|
@import "popover-menu/popover-menu-item";
|
||||||
@import "newsroom-menu";
|
@import "newsroom-menu";
|
||||||
@import "panel";
|
@import "panel";
|
||||||
|
@import "collapsible-section";
|
||||||
@import "modal";
|
@import "modal";
|
||||||
@import "dashlet/all";
|
@import "dashlet/all";
|
||||||
@import "input/all";
|
@import "input/all";
|
||||||
|
|||||||
@@ -17,21 +17,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* SCSS variables */
|
/* SCSS variables */
|
||||||
$ibo-collapsible-section--margin-top: 3rem;
|
$ibo-collapsible-section--margin-top: 3rem !default;
|
||||||
$ibo-collapsible-section--title--color: $ibo-color-grey-900 !default;
|
$ibo-collapsible-section--title--color: $ibo-panel--title--color !default;
|
||||||
$ibo-collapsible-section--body--background-color: $ibo-color-white-100 !default;
|
$ibo-collapsible-section--body--background-color: $ibo-panel--body--background-color !default;
|
||||||
|
|
||||||
$ibo-collapsible-section--highlight--height: 8px !default;
|
$ibo-collapsible-section--highlight--height: 8px !default;
|
||||||
|
|
||||||
$ibo-collapsible-section--maximize-minimize-button--right: 5px !default;
|
$ibo-collapsible-section--maximize-minimize-button--color: $ibo-panel--collapsible-toggler--color !default;
|
||||||
|
$ibo-collapsible-section--maximize-minimize-button--right: $ibo-panel--collapsible-toggler--margin-right !default;
|
||||||
|
|
||||||
|
|
||||||
$ibo-collapsible-section--body--padding-bottom: 16px !default;
|
$ibo-collapsible-section--body--padding-bottom: 16px !default;
|
||||||
$ibo-collapsible-section--body--padding-top: $ibo-collapsible-section--body--padding-bottom + $ibo-collapsible-section--highlight--height !default;
|
$ibo-collapsible-section--body--padding-top: $ibo-collapsible-section--body--padding-bottom + $ibo-collapsible-section--highlight--height !default;
|
||||||
$ibo-collapsible-section--body--padding-x: 16px !default;
|
$ibo-collapsible-section--body--padding-x: 16px !default;
|
||||||
$ibo-collapsible-section--body--border-radius: $ibo-border-radius-500 !default;
|
$ibo-collapsible-section--body--border-radius: $ibo-panel--body--border-radius !default;
|
||||||
$ibo-collapsible-section--body--border-size: 1px !default;
|
$ibo-collapsible-section--body--border-size: 1px !default;
|
||||||
$ibo-collapsible-section--body--border-color: $ibo-color-grey-400 !default;
|
$ibo-collapsible-section--body--border-color: $ibo-panel--base-border-color !default;
|
||||||
|
|
||||||
|
|
||||||
/* Rules */
|
/* Rules */
|
||||||
@@ -78,6 +78,7 @@ $ibo-collapsible-section--body--border-color: $ibo-color-grey-400 !default;
|
|||||||
|
|
||||||
.ibo-collapsible-section--action-button {
|
.ibo-collapsible-section--action-button {
|
||||||
&.ibo-collapsible-section--maximize-button, &.ibo-collapsible-section--minimize-button {
|
&.ibo-collapsible-section--maximize-button, &.ibo-collapsible-section--minimize-button {
|
||||||
|
color: $ibo-collapsible-section--maximize-minimize-button--color;
|
||||||
margin-right: $ibo-collapsible-section--maximize-minimize-button--right;
|
margin-right: $ibo-collapsible-section--maximize-minimize-button--right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,12 +35,12 @@ $ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
|||||||
|
|
||||||
/* Avoid value to overflow from its container with very long strings (typically URLs) */
|
/* 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 */
|
/* 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(.ibo-input-file-select--container) {
|
&: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 */
|
/* We need the rule to apply for the class and all its descendants */
|
||||||
.ibo-field--value {
|
.ibo-field--value {
|
||||||
* {
|
* {
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
white-space: pre-wrap;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,3 +205,7 @@ $ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
|||||||
.multi_values {
|
.multi_values {
|
||||||
background-color: #c33;
|
background-color: #c33;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form_field ~ .form_field {
|
||||||
|
margin-top: $ibo-field--sibling-spacing;
|
||||||
|
}
|
||||||
@@ -2,3 +2,7 @@
|
|||||||
* copyright Copyright (C) 2010-2021 Combodo SARL
|
* copyright Copyright (C) 2010-2021 Combodo SARL
|
||||||
* license http://opensource.org/licenses/AGPL-3.0
|
* license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
.ibo-prop-header {
|
||||||
|
@extend %ibo-font-size-150;
|
||||||
|
padding-bottom: 14px;
|
||||||
|
}
|
||||||
@@ -22,7 +22,7 @@ $ibo-global-search--head--background-color: $ibo-color-white-100 !default;
|
|||||||
$ibo-global-search--icon-padding-x: 16px !default;
|
$ibo-global-search--icon-padding-x: 16px !default;
|
||||||
$ibo-global-search--icon-padding-y: 0 !default;
|
$ibo-global-search--icon-padding-y: 0 !default;
|
||||||
|
|
||||||
$ibo-global-search--input--padding: 0 default;
|
$ibo-global-search--input--padding: 0 !default;
|
||||||
$ibo-global-search--input--padding-x--is-opened: 8px !default;
|
$ibo-global-search--input--padding-x--is-opened: 8px !default;
|
||||||
$ibo-global-search--input--padding-y--is-opened: 8px !default;
|
$ibo-global-search--input--padding-y--is-opened: 8px !default;
|
||||||
$ibo-global-search--input--width: 0 !default;
|
$ibo-global-search--input--width: 0 !default;
|
||||||
@@ -102,6 +102,7 @@ $ibo-global-search--compartment--placeholder-hint--text-color: $ibo-color-grey-7
|
|||||||
padding: $ibo-global-search--input--padding;
|
padding: $ibo-global-search--input--padding;
|
||||||
width: $ibo-global-search--input--width;
|
width: $ibo-global-search--input--width;
|
||||||
color: $ibo-global-search--input--text-color;
|
color: $ibo-global-search--input--text-color;
|
||||||
|
background-color: transparent;
|
||||||
@extend %ibo-font-ral-nor-300;
|
@extend %ibo-font-ral-nor-300;
|
||||||
|
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -237,26 +237,28 @@ $ibo-panel-colors: (
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ibo-panel--collapsible-toggler--opened {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ibo-panel--collapsible-toggler--closed {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Collapsible rules */
|
/* Collapsible rules */
|
||||||
.ibo-panel:not(.ibo-is-opened) {
|
.ibo-panel {
|
||||||
.ibo-panel--collapsible-toggler--closed {
|
.ibo-panel--collapsible-toggler--opened {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ibo-panel--collapsible-toggler--opened {
|
.ibo-panel--collapsible-toggler--closed {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
&:not(.ibo-is-opened) {
|
||||||
|
.ibo-panel--collapsible-toggler--closed {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.ibo-panel--body {
|
.ibo-panel--collapsible-toggler--opened {
|
||||||
display: none;
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ibo-panel--body {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,25 +4,43 @@ $ibo-prop--cancel--padding-left: 7px !default;
|
|||||||
$ibo-prop--apply-cancel--span--height: 28px !default;
|
$ibo-prop--apply-cancel--span--height: 28px !default;
|
||||||
$ibo-prop--apply-cancel--span--width: 32px !default;
|
$ibo-prop--apply-cancel--span--width: 32px !default;
|
||||||
|
|
||||||
|
$ibo-prop--apply-cancel--height: $ibo-prop--apply-cancel--span--height !default;
|
||||||
|
$ibo-prop--apply--width: calc(#{$ibo-prop--apply-cancel--span--width} + #{$ibo-prop--apply--padding-left}) !default;
|
||||||
|
$ibo-prop--cancel--width: calc(#{$ibo-prop--apply-cancel--span--width} + #{$ibo-prop--cancel--padding-left}) !default;
|
||||||
|
|
||||||
|
$ibo-prop--apply--error--color: $ibo-color-grey-800 !default;
|
||||||
|
|
||||||
.ibo-prop--apply{
|
.ibo-prop--apply{
|
||||||
|
width: $ibo-prop--apply--width;
|
||||||
padding-left: $ibo-prop--apply--padding-left;
|
padding-left: $ibo-prop--apply--padding-left;
|
||||||
> span{
|
> span{
|
||||||
@extend .ibo-is-green;
|
@extend .ibo-is-green;
|
||||||
}
|
}
|
||||||
|
&.ui-state-error{
|
||||||
|
&:after {
|
||||||
|
color: $ibo-prop--apply--error--color;
|
||||||
|
content: '\f071';
|
||||||
|
vertical-align: bottom;
|
||||||
|
@extend %fa-solid-base;
|
||||||
|
}
|
||||||
|
> span{
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ibo-prop--cancel{
|
.ibo-prop--cancel{
|
||||||
padding-left: $ibo-prop--cancel--padding-left;
|
width: $ibo-prop--cancel--width;
|
||||||
|
padding-left: $ibo-prop--cancel--padding-left;
|
||||||
> span{
|
> span{
|
||||||
@extend .ibo-is-red;
|
@extend .ibo-is-red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ibo-prop--apply, .ibo-prop--cancel{
|
.ibo-prop--apply, .ibo-prop--cancel{
|
||||||
> span{
|
height: $ibo-prop--apply-cancel--height;
|
||||||
|
> span{
|
||||||
display: block;
|
display: block;
|
||||||
height: $ibo-prop--apply-cancel--span--height;
|
height: $ibo-prop--apply-cancel--span--height;
|
||||||
width: $ibo-prop--apply-cancel--span--width;
|
width: $ibo-prop--apply-cancel--span--width;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@extend .ibo-button;
|
|
||||||
@extend .ibo-is-alternative;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,19 @@ $ibo-search-form-panel--body--padding-top: 18px !default;
|
|||||||
$ibo-search-form-panel--body--padding-bottom: 10px !default;
|
$ibo-search-form-panel--body--padding-bottom: 10px !default;
|
||||||
$ibo-search-form-panel--body--padding-x: 14px !default;
|
$ibo-search-form-panel--body--padding-x: 14px !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;
|
||||||
|
$ibo-search-form-panel--criteria--locked--background-color: $ibo-color-grey-300 !default;
|
||||||
|
|
||||||
|
$ibo-search-form-panel--more-criteria--color: $ibo-color-blue-grey-800 !default;
|
||||||
|
$ibo-search-form-panel--more-criteria--background-color: $ibo-color-white-100 !default;
|
||||||
|
$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--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--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--datatable-toolbar--background-color--is-sticking: $ibo-panel--body--background-color !default;
|
$ibo-search-results-area--datatable-toolbar--background-color--is-sticking: $ibo-panel--body--background-color !default;
|
||||||
@@ -140,7 +153,6 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
|||||||
.sf_criterion_area {
|
.sf_criterion_area {
|
||||||
/*display: none;*/
|
/*display: none;*/
|
||||||
padding: 8px 8px 3px 8px; /* padding-bottom must equals to padding-top - .search_form_criteria:margin-bottom */
|
padding: 8px 8px 3px 8px; /* padding-bottom must equals to padding-top - .search_form_criteria:margin-bottom */
|
||||||
background-color: $ibo-color-white-100;
|
|
||||||
|
|
||||||
.sf_criterion_row {
|
.sf_criterion_row {
|
||||||
|
|
||||||
@@ -172,7 +184,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
|||||||
display: inline;
|
display: inline;
|
||||||
.sfc_fg_button,
|
.sfc_fg_button,
|
||||||
.sfc_header {
|
.sfc_header {
|
||||||
border: 1px solid #E1E7EC; /* Must be equal to .search_form_criteria:margin-bottom + this:padding-bottom */
|
border: 1px solid $ibo-search-form-panel--criteria--border-color; /* Must be equal to .search_form_criteria:margin-bottom + this:padding-bottom */
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,7 +213,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
|||||||
> * {
|
> * {
|
||||||
padding: 7px 8px;
|
padding: 7px 8px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
border: solid 1px $ibo-color-grey-300;
|
border: solid 1px $ibo-search-form-panel--more-criteria--border-color;
|
||||||
border-radius: $ibo-border-radius-300;
|
border-radius: $ibo-border-radius-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +241,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
|||||||
.search_form_criteria {
|
.search_form_criteria {
|
||||||
/* Non editable criteria */
|
/* Non editable criteria */
|
||||||
&.locked {
|
&.locked {
|
||||||
background-color: $ibo-color-grey-200;
|
background-color: $ibo-search-form-panel--criteria--locked--background-color;
|
||||||
|
|
||||||
.sfc_title {
|
.sfc_title {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
@@ -278,8 +290,8 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
|||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
background-color: $ibo-color-white-200;
|
background-color: $ibo-search-form-panel--criteria--background-color;
|
||||||
color: $ibo-color-grey-900;
|
color: $ibo-search-form-panel--criteria--color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Top left corner icons */
|
/* Top left corner icons */
|
||||||
@@ -646,8 +658,8 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
|||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
background-color: $ibo-color-white-100;
|
background-color: $ibo-search-form-panel--more-criteria--background-color;
|
||||||
color: $ibo-color-blue-grey-800;
|
color: $ibo-search-form-panel--more-criteria--color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sfm_toggler {
|
.sfm_toggler {
|
||||||
@@ -702,8 +714,8 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
background-color: $ibo-color-white-100;
|
background-color: $ibo-search-form-panel--misc-button--background-color;
|
||||||
color: $ibo-color-primary-600;
|
color: $ibo-search-form-panel--misc-button--icon--color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -835,9 +847,6 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
|||||||
|
|
||||||
button {
|
button {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
@extend .ibo-button;
|
|
||||||
@extend .ibo-is-primary;
|
|
||||||
@extend .ibo-is-regular;
|
|
||||||
> span {
|
> span {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
@@ -864,16 +873,6 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
|
|||||||
padding-top: $ibo-panel--spacing-top ;
|
padding-top: $ibo-panel--spacing-top ;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sfc_fg_button{
|
|
||||||
@extend .ibo-button;
|
|
||||||
@extend .ibo-is-neutral;
|
|
||||||
&.sfc_fg_search, &.sfc_fg_apply, &.sfc_fg_cancel {
|
|
||||||
@extend .ibo-is-regular;
|
|
||||||
}
|
|
||||||
&.sfc_fg_more {
|
|
||||||
@extend .ibo-is-alternative;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.sfm_tg_title{
|
.sfm_tg_title{
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ $ibo-dashlet--elements-spacing-y: 24px !default;
|
|||||||
|
|
||||||
/* Rules */
|
/* Rules */
|
||||||
.ibo-dashlet {
|
.ibo-dashlet {
|
||||||
|
position: relative;
|
||||||
width: calc(#{$ibo-dashlet--width} - #{$ibo-dashlet--elements-spacing-x});
|
width: calc(#{$ibo-dashlet--width} - #{$ibo-dashlet--elements-spacing-x});
|
||||||
margin: calc(#{$ibo-dashlet--elements-spacing-y} / 2) calc(#{$ibo-dashlet--elements-spacing-x} / 2);
|
margin: calc(#{$ibo-dashlet--elements-spacing-y} / 2) calc(#{$ibo-dashlet--elements-spacing-x} / 2);
|
||||||
|
|
||||||
@@ -27,4 +28,13 @@ $ibo-dashlet--elements-spacing-y: 24px !default;
|
|||||||
}
|
}
|
||||||
.ibo-details{
|
.ibo-details{
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
.ibo-dashlet-blocker{
|
||||||
|
position: absolute;
|
||||||
|
z-index: 9; /* To be above calendar links & all, but below .close-box (9) */
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
@@ -25,9 +25,7 @@ $ibo-input-datetime--ui-tpicker-slider--padding-right: 18px !default;
|
|||||||
color: $ibo-input-datetime--action-button--color;
|
color: $ibo-input-datetime--action-button--color;
|
||||||
}
|
}
|
||||||
.ui-datepicker-current, .ui-datepicker-close{
|
.ui-datepicker-current, .ui-datepicker-close{
|
||||||
@extend .ibo-button;
|
@extend .ibo-button, .ibo-is-regular, .ibo-is-secondary;
|
||||||
@extend .ibo-is-regular;
|
|
||||||
@extend .ibo-is-secondary;
|
|
||||||
}
|
}
|
||||||
.ui_tpicker_hour_slider, .ui_tpicker_minute_slider, .ui_tpicker_second_slider{
|
.ui_tpicker_hour_slider, .ui_tpicker_minute_slider, .ui_tpicker_second_slider{
|
||||||
@extend .ibo-input-wrapper;
|
@extend .ibo-input-wrapper;
|
||||||
|
|||||||
@@ -35,10 +35,13 @@ $ibo-input-select--action-button--padding-left: 6px !default;
|
|||||||
$ibo-input-select--action-button--padding-right: 2px !default;
|
$ibo-input-select--action-button--padding-right: 2px !default;
|
||||||
|
|
||||||
.ibo-input-select {
|
.ibo-input-select {
|
||||||
appearance: none;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
min-width: $ibo-input-select--value--min-midth;
|
min-width: $ibo-input-select--value--min-midth;
|
||||||
|
|
||||||
|
&:not(.ibo-input-select-autocomplete):not(.ibo-input-selectize) {
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
&.ibo-input-selectize {
|
&.ibo-input-selectize {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
@@ -56,6 +59,10 @@ $ibo-input-select--action-button--padding-right: 2px !default;
|
|||||||
padding-left: $ibo-input--padding-x;
|
padding-left: $ibo-input--padding-x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&[size]{
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ibo-input-select-autocomplete{
|
.ibo-input-select-autocomplete{
|
||||||
min-width: $ibo-input-select-autocomplete--value--min-midth !important;
|
min-width: $ibo-input-select-autocomplete--value--min-midth !important;
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ $ibo-navigation-menu--menu-group-title--text-color--is-active: $ibo-color-blue-g
|
|||||||
$ibo-navigation-menu--drawer--width: 312px !default;
|
$ibo-navigation-menu--drawer--width: 312px !default;
|
||||||
$ibo-navigation-menu--drawer--padding-x: 20px !default;
|
$ibo-navigation-menu--drawer--padding-x: 20px !default;
|
||||||
$ibo-navigation-menu--drawer--padding-y: 32px !default;
|
$ibo-navigation-menu--drawer--padding-y: 32px !default;
|
||||||
$ibo-navigation-menu--drawer--background-color: $ibo-color-grey-100 !default;
|
$ibo-navigation-menu--drawer--background-color: $ibo-navigation-menu--menu-group--background-color--is-active !default;
|
||||||
$ibo-navigation-menu--drawer--border-right: 1px solid $ibo-color-grey-300 !default;
|
$ibo-navigation-menu--drawer--border-right: 1px solid $ibo-color-grey-300 !default;
|
||||||
|
|
||||||
/* TODO: Refactor this into the standard field input */
|
/* TODO: Refactor this into the standard field input */
|
||||||
|
|||||||
@@ -56,12 +56,17 @@ $ibo-activity-entry--sub-information--margin-top: 4px !default;
|
|||||||
$ibo-activity-entry--sub-information--margin-bottom: $ibo-activity-entry--sub-information--margin-top !default;
|
$ibo-activity-entry--sub-information--margin-bottom: $ibo-activity-entry--sub-information--margin-top !default;
|
||||||
$ibo-activity-entry--sub-information--text-color: $ibo-color-grey-700 !default;
|
$ibo-activity-entry--sub-information--text-color: $ibo-color-grey-700 !default;
|
||||||
|
|
||||||
$ibo-activity-entry--author-name--sibling-spacing: 0.2rem !default;
|
$ibo-activity-entry--sub-information--sibling-spacing: 0.5rem !default;
|
||||||
|
$ibo-activity-entry--sub-information-sibling-separator--size: 4px !default;
|
||||||
|
$ibo-activity-entry--sub-information-sibling-separator--border-radius: 100% !default;
|
||||||
|
$ibo-activity-entry--sub-information-sibling-separator--background-color: $ibo-color-grey-600 !default;
|
||||||
|
|
||||||
$ibo-activity-panel--load-more-entries--size: 32px !default;
|
$ibo-activity-panel--load-entries-button--size: 32px !default;
|
||||||
$ibo-activity-panel--load-more-entries--border-radius: $ibo-border-radius-full !default;
|
$ibo-activity-panel--load-entries-button--border-radius: $ibo-border-radius-full !default;
|
||||||
$ibo-activity-panel--load-more-entries--background-color: $ibo-activity-entry--main-information--background-color !default;
|
$ibo-activity-panel--load-entries-button--background-color: $ibo-activity-entry--main-information--background-color !default;
|
||||||
$ibo-activity-panel--load-more-entries--border: $ibo-content-block--border !default;
|
$ibo-activity-panel--load-entries-button--border: $ibo-content-block--border !default;
|
||||||
|
|
||||||
|
$ibo-activity-panel--load-all-entries--is-hover--margin-left: ($ibo-activity-panel--load-entries-button--size + 10px) * 2 !default; /* 2x is necessary here as the elements are centered */
|
||||||
|
|
||||||
/* Entry group */
|
/* Entry group */
|
||||||
.ibo-activity-panel--entry-group{
|
.ibo-activity-panel--entry-group{
|
||||||
@@ -198,7 +203,7 @@ $ibo-activity-panel--load-more-entries--border: $ibo-content-block--border !defa
|
|||||||
|
|
||||||
/* Avoid pre (code snippets) to overflow outside the entry */
|
/* Avoid pre (code snippets) to overflow outside the entry */
|
||||||
pre {
|
pre {
|
||||||
white-space: pre-wrap;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Avoid table to overflow outside the entry (see N°2127) */
|
/* Avoid table to overflow outside the entry (see N°2127) */
|
||||||
@@ -228,26 +233,52 @@ $ibo-activity-panel--load-more-entries--border: $ibo-content-block--border !defa
|
|||||||
color: $ibo-activity-entry--sub-information--text-color;
|
color: $ibo-activity-entry--sub-information--text-color;
|
||||||
|
|
||||||
@extend %ibo-font-size-50;
|
@extend %ibo-font-size-50;
|
||||||
}
|
|
||||||
|
|
||||||
.ibo-activity-entry--author-name {
|
> *:not(:last-child):after {
|
||||||
&:after {
|
content: " ";
|
||||||
content: "-";
|
display: inline-block;
|
||||||
margin-left: $ibo-activity-entry--author-name--sibling-spacing;
|
vertical-align: middle;
|
||||||
margin-right: $ibo-activity-entry--author-name--sibling-spacing;
|
margin-left: $ibo-activity-entry--sub-information--sibling-spacing;
|
||||||
|
margin-right: $ibo-activity-entry--sub-information--sibling-spacing;
|
||||||
|
width: $ibo-activity-entry--sub-information-sibling-separator--size;
|
||||||
|
height: $ibo-activity-entry--sub-information-sibling-separator--size;
|
||||||
|
border-radius: $ibo-activity-entry--sub-information-sibling-separator--border-radius;
|
||||||
|
background-color: $ibo-activity-entry--sub-information-sibling-separator--background-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ibo-activity-panel--load-more-entries-container {
|
.ibo-activity-panel--load-more-entries-container {
|
||||||
|
position: relative;
|
||||||
@extend %ibo-fully-centered-content;
|
@extend %ibo-fully-centered-content;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.ibo-activity-panel--load-all-entries {
|
||||||
|
margin-left: $ibo-activity-panel--load-all-entries--is-hover--margin-left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&:not(:hover) {
|
||||||
|
.ibo-activity-panel--load-all-entries {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ibo-activity-panel--load-more-entries {
|
.ibo-activity-panel--load-entries-button {
|
||||||
width: $ibo-activity-panel--load-more-entries--size;
|
width: $ibo-activity-panel--load-entries-button--size;
|
||||||
height: $ibo-activity-panel--load-more-entries--size;
|
height: $ibo-activity-panel--load-entries-button--size;
|
||||||
border-radius: $ibo-activity-panel--load-more-entries--border-radius;
|
border-radius: $ibo-activity-panel--load-entries-button--border-radius;
|
||||||
background-color: $ibo-activity-panel--load-more-entries--background-color;
|
background-color: $ibo-activity-panel--load-entries-button--background-color;
|
||||||
border: $ibo-activity-panel--load-more-entries--border;
|
border: $ibo-activity-panel--load-entries-button--border;
|
||||||
@extend %ibo-fully-centered-content;
|
@extend %ibo-fully-centered-content;
|
||||||
@extend %ibo-hyperlink-inherited-colors;
|
@extend %ibo-hyperlink-inherited-colors;
|
||||||
|
}
|
||||||
|
.ibo-activity-panel--load-more-entries {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.ibo-activity-panel--load-all-entries {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 0; /* Must be below the other button as it will reveal later */
|
||||||
|
top: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
transition: all 0.1s ease-in-out;
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,9 @@ $ibo-activity-panel--togglers--elements-spacing: 0.75rem !default;
|
|||||||
|
|
||||||
/* - Tabs togglers*/
|
/* - Tabs togglers*/
|
||||||
$ibo-activity-panel--tabs-togglers--padding-x: $ibo-activity-panel--padding-x * 3 !default; /* We need to increase this so the size toggler which will be set in abs. pos. can overlap it nicely */
|
$ibo-activity-panel--tabs-togglers--padding-x: $ibo-activity-panel--padding-x * 3 !default; /* We need to increase this so the size toggler which will be set in abs. pos. can overlap it nicely */
|
||||||
|
$ibo-activity-panel--tab-toolbar-action--color: $ibo-color-grey-900 !default;
|
||||||
|
|
||||||
|
$ibo-activity-panel--tab-toolbar-info--color: $ibo-activity-panel--tab-toolbar-action--color !default;
|
||||||
/* - Tab toggler */
|
/* - Tab toggler */
|
||||||
$ibo-activity-panel--tab-toggler--caselog-highlight-colors: $ibo-caselog-highlight-colors !default;
|
$ibo-activity-panel--tab-toggler--caselog-highlight-colors: $ibo-caselog-highlight-colors !default;
|
||||||
$ibo-activity-panel--tab-toggler--is-active--background-color: $ibo-color-grey-200 !default;
|
$ibo-activity-panel--tab-toggler--is-active--background-color: $ibo-color-grey-200 !default;
|
||||||
@@ -278,7 +280,8 @@ $ibo-activity-panel--open-icon--margin-left: 0.75rem !default;
|
|||||||
}
|
}
|
||||||
.ibo-activity-panel--tab-toolbar-right-actions {
|
.ibo-activity-panel--tab-toolbar-right-actions {
|
||||||
.ibo-activity-panel--tab-toolbar-info {
|
.ibo-activity-panel--tab-toolbar-info {
|
||||||
> .ibo-activity-panel--tab-toolbar-info-icon {
|
color: $ibo-activity-panel--tab-toolbar-info--color;
|
||||||
|
> .ibo-activity-panel--tab-toolbar-info-icon {
|
||||||
margin-left: $ibo-activity-panel--tab-toolbar-info-icon--margin-left;
|
margin-left: $ibo-activity-panel--tab-toolbar-info-icon--margin-left;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +292,7 @@ $ibo-activity-panel--open-icon--margin-left: 0.75rem !default;
|
|||||||
}
|
}
|
||||||
.ibo-activity-panel--tab-toolbar-action{
|
.ibo-activity-panel--tab-toolbar-action{
|
||||||
position: relative;
|
position: relative;
|
||||||
|
color: $ibo-activity-panel--tab-toolbar-action--color;
|
||||||
@extend %ibo-fully-centered-content;
|
@extend %ibo-fully-centered-content;
|
||||||
}
|
}
|
||||||
.ibo-activity-panel--filter{
|
.ibo-activity-panel--filter{
|
||||||
|
|||||||
@@ -44,15 +44,17 @@ $ibo-dashboard-editor--delete-dashlet-icon--z-index: 21 !default;
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
table{
|
table{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
td{
|
|
||||||
margin-bottom: 14px;
|
td, th {
|
||||||
.ibo-field{
|
margin-bottom: 14px;
|
||||||
@extend %ibo-font-size-100;
|
|
||||||
}
|
.ibo-field {
|
||||||
}
|
@extend %ibo-font-size-100;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ibo-dashboard-editor--properties-title{
|
.ibo-dashboard-editor--properties-title{
|
||||||
padding-bottom: $ibo-dashboard-editor--properties-title--padding-bottom;
|
padding-bottom: $ibo-dashboard-editor--properties-title--padding-bottom;
|
||||||
@@ -98,7 +100,9 @@ $ibo-dashboard-editor--delete-dashlet-icon--z-index: 21 !default;
|
|||||||
right: $ibo-dashboard-editor--delete-dashlet-icon--right;
|
right: $ibo-dashboard-editor--delete-dashlet-icon--right;
|
||||||
padding: $ibo-dashboard-editor--delete-dashlet-icon--padding;
|
padding: $ibo-dashboard-editor--delete-dashlet-icon--padding;
|
||||||
z-index: $ibo-dashboard-editor--delete-dashlet-icon--z-index;
|
z-index: $ibo-dashboard-editor--delete-dashlet-icon--z-index;
|
||||||
@extend .ibo-button;
|
}
|
||||||
@extend .ibo-is-alternative;
|
.ibo-dashboard-editor .itop-dashboard{
|
||||||
@extend .ibo-is-danger;
|
a{
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +80,10 @@ $ibo-display-graph--search-box--criterion--content--checkbox--margin-right: 10px
|
|||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
}
|
}
|
||||||
.ibo-display-graph--search-box {
|
.ibo-display-graph--search-box {
|
||||||
|
.sf_criterion_area{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
.sf_criterion_row {
|
.sf_criterion_row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -103,6 +106,6 @@ $ibo-display-graph--search-box--criterion--content--checkbox--margin-right: 10px
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ReloadMovieBtn {
|
#ReloadMovieBtn {
|
||||||
float: right;
|
align-self: flex-end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,6 +142,19 @@ body.ibo-has-fullscreen-descendant {
|
|||||||
@extend %ibo-font-code-150;
|
@extend %ibo-font-code-150;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A single class to handle WYSIWYG generated content, where only HTML tags are available
|
||||||
|
* See https://bulma.io/documentation/elements/content/
|
||||||
|
*/
|
||||||
|
.ibo-is-html-content {
|
||||||
|
@extend .content;
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/***********************************************************************/
|
/***********************************************************************/
|
||||||
/* Sticky headers */
|
/* Sticky headers */
|
||||||
/* */
|
/* */
|
||||||
|
|||||||
1
css/backoffice/vendors/_all.scss
vendored
1
css/backoffice/vendors/_all.scss
vendored
@@ -19,6 +19,7 @@
|
|||||||
@import "bulma-variables-overload";
|
@import "bulma-variables-overload";
|
||||||
@import "../../../node_modules/bulma-scss/bulma";
|
@import "../../../node_modules/bulma-scss/bulma";
|
||||||
@import "ckeditor";
|
@import "ckeditor";
|
||||||
|
@import "tippy";
|
||||||
@import "jqueryui";
|
@import "jqueryui";
|
||||||
@import "jquery-multiselect";
|
@import "jquery-multiselect";
|
||||||
@import "datatables";
|
@import "datatables";
|
||||||
|
|||||||
@@ -23,4 +23,15 @@ $body-font-size: $ibo-font-size-100 !default;
|
|||||||
$body-weight: $ibo-font-weight-500 !default;
|
$body-weight: $ibo-font-weight-500 !default;
|
||||||
|
|
||||||
$body-overflow-x: hidden !default;
|
$body-overflow-x: hidden !default;
|
||||||
$body-overflow-y: auto !default;
|
$body-overflow-y: auto !default;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* customize Bulma content variables
|
||||||
|
* See https://bulma.io/documentation/elements/content/
|
||||||
|
*/
|
||||||
|
$content-table-cell-border: 1px solid black;
|
||||||
|
$content-table-cell-border-width: 1px;
|
||||||
|
$content-table-head-cell-border-width: 1px;
|
||||||
|
$content-table-foot-cell-border-width: 1px;
|
||||||
|
$content-table-foot-cell-color: black;
|
||||||
|
|||||||
9
css/backoffice/vendors/_ckeditor.scss
vendored
9
css/backoffice/vendors/_ckeditor.scss
vendored
@@ -17,7 +17,7 @@ $ibo-vendors-ckeditor--autocomplete-panel--background-color: $ibo-color-white-10
|
|||||||
|
|
||||||
$ibo-vendors-ckeditor--autocomplete-item-image--size: 25px !default;
|
$ibo-vendors-ckeditor--autocomplete-item-image--size: 25px !default;
|
||||||
$ibo-vendors-ckeditor--autocomplete-item-image--margin-right: 0.5rem !default;
|
$ibo-vendors-ckeditor--autocomplete-item-image--margin-right: 0.5rem !default;
|
||||||
$ibo-vendors-ckeditor--autocomplete-item-image--background-color: $ibo-color-grey-200 !default;
|
$ibo-vendors-ckeditor--autocomplete-item-image--background-color: $ibo-color-blue-100 !default;
|
||||||
$ibo-vendors-ckeditor--autocomplete-item-image--border: 1px solid $ibo-color-grey-600 !default;
|
$ibo-vendors-ckeditor--autocomplete-item-image--border: 1px solid $ibo-color-grey-600 !default;
|
||||||
|
|
||||||
$ibo-vendors-ckeditor--autocomplete-item-title--text-color: #3A3A3A !default;
|
$ibo-vendors-ckeditor--autocomplete-item-title--text-color: #3A3A3A !default;
|
||||||
@@ -37,6 +37,7 @@ $ibo-vendors-ckeditor--autocomplete-item-title--text-color: #3A3A3A !default;
|
|||||||
background-position: center center !important;
|
background-position: center center !important;
|
||||||
background-repeat: no-repeat !important;
|
background-repeat: no-repeat !important;
|
||||||
background-size: 100% !important;
|
background-size: 100% !important;
|
||||||
|
background-image: url('../../../../images/full-screen.png') !important;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: $ibo-vendors-ckeditor--toolbar-fullscreen-button--background-color--on-hover;
|
background-color: $ibo-vendors-ckeditor--toolbar-fullscreen-button--background-color--on-hover;
|
||||||
@@ -48,7 +49,7 @@ $ibo-vendors-ckeditor--autocomplete-item-title--text-color: #3A3A3A !default;
|
|||||||
padding: $ibo-vendors-highlightjs--padding !important;
|
padding: $ibo-vendors-highlightjs--padding !important;
|
||||||
box-shadow: 0 0px 3px 2px inset rgba(0, 0, 0, 0.4);
|
box-shadow: 0 0px 3px 2px inset rgba(0, 0, 0, 0.4);
|
||||||
border-radius: $ibo-vendors-highlightjs--border-radius;
|
border-radius: $ibo-vendors-highlightjs--border-radius;
|
||||||
white-space: pre-wrap;
|
white-space: pre-line;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mentions in caselogs */
|
/* Mentions in caselogs */
|
||||||
@@ -67,6 +68,9 @@ ul.cke_autocomplete_panel{
|
|||||||
.ibo-vendors-ckeditor--autocomplete-item-image{
|
.ibo-vendors-ckeditor--autocomplete-item-image{
|
||||||
width: $ibo-vendors-ckeditor--autocomplete-item-image--size;
|
width: $ibo-vendors-ckeditor--autocomplete-item-image--size;
|
||||||
height: $ibo-vendors-ckeditor--autocomplete-item-image--size;
|
height: $ibo-vendors-ckeditor--autocomplete-item-image--size;
|
||||||
|
/* min-xxx are here to avoid medallion to be horizontally compressed when the title is to long */
|
||||||
|
min-width: $ibo-vendors-ckeditor--autocomplete-item-image--size;
|
||||||
|
min-height: $ibo-vendors-ckeditor--autocomplete-item-image--size;
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-size: 100%;
|
background-size: 100%;
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
@@ -77,6 +81,7 @@ ul.cke_autocomplete_panel{
|
|||||||
@extend %ibo-fully-centered-content;
|
@extend %ibo-fully-centered-content;
|
||||||
}
|
}
|
||||||
.ibo-vendors-ckeditor--autocomplete-item-title{
|
.ibo-vendors-ckeditor--autocomplete-item-title{
|
||||||
|
white-space: nowrap; /* Here we don't want to truncate the text as in an autocomplete we might have similar values and we need the user to see the entire text to be able to differenciate them */
|
||||||
color: $ibo-vendors-ckeditor--autocomplete-item-title--text-color;
|
color: $ibo-vendors-ckeditor--autocomplete-item-title--text-color;
|
||||||
@extend %ibo-font-weight-700;
|
@extend %ibo-font-weight-700;
|
||||||
}
|
}
|
||||||
|
|||||||
9
css/backoffice/vendors/_jqueryui.scss
vendored
9
css/backoffice/vendors/_jqueryui.scss
vendored
@@ -167,9 +167,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
|
|||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
.ui-button {
|
.ui-button {
|
||||||
@extend .ibo-button;
|
@extend .ibo-button, .ibo-is-regular, .ibo-is-secondary;
|
||||||
@extend .ibo-is-regular;
|
|
||||||
@extend .ibo-is-secondary;
|
|
||||||
> .ui-icon {
|
> .ui-icon {
|
||||||
background-image: none;
|
background-image: none;
|
||||||
float: unset;
|
float: unset;
|
||||||
@@ -301,7 +299,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
|
|||||||
background-color: $ibo-vendors-jqueryui--ui-datepicker--background-color;
|
background-color: $ibo-vendors-jqueryui--ui-datepicker--background-color;
|
||||||
border-radius: $ibo-vendors-jqueryui--ui-datepicker--border-radius;
|
border-radius: $ibo-vendors-jqueryui--ui-datepicker--border-radius;
|
||||||
box-shadow: $ibo-vendors-jqueryui--ui-datepicker--box-shadow;
|
box-shadow: $ibo-vendors-jqueryui--ui-datepicker--box-shadow;
|
||||||
z-index: 21 !important;
|
z-index: 32 !important;
|
||||||
padding: 0px 8px 5px 8px;
|
padding: 0px 8px 5px 8px;
|
||||||
|
|
||||||
.ui-datepicker-header {
|
.ui-datepicker-header {
|
||||||
@@ -569,6 +567,9 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
|
|||||||
left: 0;
|
left: 0;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
.ui-menu-item{
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.ui-autocomplete-input{
|
.ui-autocomplete-input{
|
||||||
@extend .ibo-input;
|
@extend .ibo-input;
|
||||||
|
|||||||
9
css/backoffice/vendors/_tippy.scss
vendored
Normal file
9
css/backoffice/vendors/_tippy.scss
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/*!
|
||||||
|
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||||
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Allow plain text (opposite of HTML) multi-lines tooltips to be displayed correctly, otherwise it will all be on a single line.
|
||||||
|
.tippy-content {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
@CHARSET "UTF-8";
|
@CHARSET "UTF-8";
|
||||||
|
|
||||||
|
*, ::before, ::after {
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
html,body {
|
html,body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
@@ -107,10 +111,9 @@ a:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#login-form-content .divider hr {
|
#login-form-content .divider hr {
|
||||||
margin-left:auto;
|
margin: 0.5rem auto;
|
||||||
margin-right:auto;
|
|
||||||
width:40%;
|
width:40%;
|
||||||
color: #E1E7EC;
|
background-color: #E1E7EC;
|
||||||
}
|
}
|
||||||
|
|
||||||
#login-form-content .divider hr.left {
|
#login-form-content .divider hr.left {
|
||||||
|
|||||||
756
css/setup.css
756
css/setup.css
File diff suppressed because one or more lines are too long
@@ -36,6 +36,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
|||||||
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '',
|
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'abgelaufen',
|
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'abgelaufen',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '',
|
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '',
|
||||||
|
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'einmaliges Passwort',
|
||||||
|
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
|
||||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Letzte Passworterneuerung',
|
'Class:UserLocal/Attribute:password_renewed_date' => 'Letzte Passworterneuerung',
|
||||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Letztes Änderungsdatum',
|
'Class:UserLocal/Attribute:password_renewed_date+' => 'Letztes Änderungsdatum',
|
||||||
|
|
||||||
|
|||||||
@@ -49,10 +49,13 @@ Dict::Add('EN US', 'English', 'English', array(
|
|||||||
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '',
|
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expired',
|
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expired',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '',
|
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '',
|
||||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal',
|
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'One-time Password',
|
||||||
|
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.',
|
||||||
|
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on',
|
||||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed',
|
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed',
|
||||||
|
|
||||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.',
|
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.',
|
||||||
|
|
||||||
'UserLocal:password:expiration' => 'The fields below require an extension'
|
'UserLocal:password:expiration' => 'The fields below require an extension',
|
||||||
|
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User',
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -27,16 +27,19 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
|||||||
|
|
||||||
'Class:UserLocal/Attribute:expiration' => 'Validité du mot de passe',
|
'Class:UserLocal/Attribute:expiration' => 'Validité du mot de passe',
|
||||||
'Class:UserLocal/Attribute:expiration+' => 'Statut du mot de passe (nécessite une extension pour avoir un effet)',
|
'Class:UserLocal/Attribute:expiration+' => 'Statut du mot de passe (nécessite une extension pour avoir un effet)',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'à durée limitée',
|
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Durée limitée',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '',
|
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'à validité permanente',
|
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Permanente',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '',
|
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'à changer',
|
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'A changer à la prochaine connexion',
|
||||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '',
|
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '',
|
||||||
|
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'Usage unique',
|
||||||
|
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
|
||||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Mot de passe changé le',
|
'Class:UserLocal/Attribute:password_renewed_date' => 'Mot de passe changé le',
|
||||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'Dernière date à laquelle le mot de passe a été changé',
|
'Class:UserLocal/Attribute:password_renewed_date+' => 'Dernière date à laquelle le mot de passe a été changé',
|
||||||
|
|
||||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 8 caractères, avec minuscule, majuscule, nombre et caractère spécial.',
|
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 8 caractères, avec minuscule, majuscule, nombre et caractère spécial.',
|
||||||
|
|
||||||
'UserLocal:password:expiration' => 'Les champs ci-dessous nécessitent une extension'
|
'UserLocal:password:expiration' => 'Les champs ci-dessous nécessitent une extension',
|
||||||
|
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Impossible de mettre "Usage unique" comme validité du mot de passe pour son propre utilisateur.',
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ class UserLocal extends UserInternal
|
|||||||
const EXPIRE_CAN = 'can_expire';
|
const EXPIRE_CAN = 'can_expire';
|
||||||
const EXPIRE_NEVER = 'never_expire';
|
const EXPIRE_NEVER = 'never_expire';
|
||||||
const EXPIRE_FORCE = 'force_expire';
|
const EXPIRE_FORCE = 'force_expire';
|
||||||
|
const EXPIRE_ONE_TIME_PWD = 'otp_expire';
|
||||||
|
|
||||||
/** @var UserLocalPasswordValidity|null */
|
/** @var UserLocalPasswordValidity|null */
|
||||||
protected $m_oPasswordValidity = null;
|
protected $m_oPasswordValidity = null;
|
||||||
|
|
||||||
@@ -90,8 +91,8 @@ class UserLocal extends UserInternal
|
|||||||
|
|
||||||
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("password", array("allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("password", array("allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
|
|
||||||
$sExpireEnum = implode(',', array(self::EXPIRE_CAN, self::EXPIRE_NEVER, self::EXPIRE_FORCE));
|
$sExpireEnum = implode(',', array(self::EXPIRE_CAN, self::EXPIRE_NEVER, self::EXPIRE_FORCE, self::EXPIRE_ONE_TIME_PWD));
|
||||||
MetaModel::Init_AddAttribute(new AttributeEnum("expiration", array("allowed_values"=>new ValueSetEnum($sExpireEnum), "sql"=>"expiration", "default_value"=>'never_expire', "is_null_allowed"=>false, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeEnum("expiration", array("allowed_values"=>new ValueSetEnum($sExpireEnum), "sql"=>"expiration", "default_value"=>self::EXPIRE_NEVER, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||||
MetaModel::Init_AddAttribute(new AttributeDate("password_renewed_date", array("allowed_values"=>null, "sql"=>"password_renewed_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
MetaModel::Init_AddAttribute(new AttributeDate("password_renewed_date", array("allowed_values"=>null, "sql"=>"password_renewed_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||||
|
|
||||||
// Display lists
|
// Display lists
|
||||||
@@ -136,6 +137,10 @@ class UserLocal extends UserInternal
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if($this->Get('expiration') == self::EXPIRE_ONE_TIME_PWD)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +189,8 @@ class UserLocal extends UserInternal
|
|||||||
protected function OnInsert()
|
protected function OnInsert()
|
||||||
{
|
{
|
||||||
parent::OnInsert();
|
parent::OnInsert();
|
||||||
|
$sToday = date(\AttributeDate::GetInternalFormat());
|
||||||
|
$this->Set('password_renewed_date', $sToday);
|
||||||
|
|
||||||
$this->OnWrite();
|
$this->OnWrite();
|
||||||
}
|
}
|
||||||
@@ -202,6 +209,15 @@ class UserLocal extends UserInternal
|
|||||||
|
|
||||||
$sNow = date(\AttributeDate::GetInternalFormat());
|
$sNow = date(\AttributeDate::GetInternalFormat());
|
||||||
$this->Set('password_renewed_date', $sNow);
|
$this->Set('password_renewed_date', $sNow);
|
||||||
|
|
||||||
|
// Reset the "force" expiration flag when the user updates her/his own password!
|
||||||
|
if ($this->IsCurrentUser())
|
||||||
|
{
|
||||||
|
if (($this->Get('expiration') == self::EXPIRE_FORCE))
|
||||||
|
{
|
||||||
|
$this->Set('expiration', self::EXPIRE_CAN);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsPasswordValid()
|
public function IsPasswordValid()
|
||||||
@@ -278,7 +294,13 @@ class UserLocal extends UserInternal
|
|||||||
{
|
{
|
||||||
$this->m_aCheckIssues[] = $this->m_oPasswordValidity->getPasswordValidityMessage();
|
$this->m_aCheckIssues[] = $this->m_oPasswordValidity->getPasswordValidityMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A User cannot force a one-time password on herself/himself
|
||||||
|
if ($this->IsCurrentUser()) {
|
||||||
|
if (array_key_exists('expiration', $this->ListChanges()) && ($this->Get('expiration') == self::EXPIRE_ONE_TIME_PWD)) {
|
||||||
|
$this->m_aCheckIssues[] = Dict::S('Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed');
|
||||||
|
}
|
||||||
|
}
|
||||||
parent::DoCheckToWrite();
|
parent::DoCheckToWrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
|||||||
'DBTools:Class' => 'Class~~',
|
'DBTools:Class' => 'Class~~',
|
||||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||||
|
'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:Error' => 'Error~~',
|
'DBTools:Error' => 'Error~~',
|
||||||
'DBTools:Count' => 'Count~~',
|
'DBTools:Count' => 'Count~~',
|
||||||
'DBTools:SQLquery' => 'SQL query~~',
|
'DBTools:SQLquery' => 'SQL query~~',
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
|||||||
'DBTools:Class' => 'Class~~',
|
'DBTools:Class' => 'Class~~',
|
||||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||||
|
'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:Error' => 'Error~~',
|
'DBTools:Error' => 'Error~~',
|
||||||
'DBTools:Count' => 'Count~~',
|
'DBTools:Count' => 'Count~~',
|
||||||
'DBTools:SQLquery' => 'SQL query~~',
|
'DBTools:SQLquery' => 'SQL query~~',
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
|||||||
'DBTools:Class' => 'Klasse',
|
'DBTools:Class' => 'Klasse',
|
||||||
'DBTools:Title' => 'Datenbankpflege-Tools',
|
'DBTools:Title' => 'Datenbankpflege-Tools',
|
||||||
'DBTools:ErrorsFound' => 'Fehler gefunden',
|
'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:Error' => 'Fehler',
|
'DBTools:Error' => 'Fehler',
|
||||||
'DBTools:Count' => 'Anzahl',
|
'DBTools:Count' => 'Anzahl',
|
||||||
'DBTools:SQLquery' => 'SQL Query',
|
'DBTools:SQLquery' => 'SQL Query',
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
|||||||
'DBTools:Class' => 'Clase',
|
'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: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~~',
|
||||||
'DBTools:Error' => 'Error',
|
'DBTools:Error' => 'Error',
|
||||||
'DBTools:Count' => 'Cantidad',
|
'DBTools:Count' => 'Cantidad',
|
||||||
'DBTools:SQLquery' => 'Consulta SQL',
|
'DBTools:SQLquery' => 'Consulta SQL',
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
|||||||
'DBTools:Class' => 'Class~~',
|
'DBTools:Class' => 'Class~~',
|
||||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||||
|
'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:Error' => 'Error~~',
|
'DBTools:Error' => 'Error~~',
|
||||||
'DBTools:Count' => 'Count~~',
|
'DBTools:Count' => 'Count~~',
|
||||||
'DBTools:SQLquery' => 'SQL query~~',
|
'DBTools:SQLquery' => 'SQL query~~',
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
|||||||
'DBTools:Class' => 'Class~~',
|
'DBTools:Class' => 'Class~~',
|
||||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||||
|
'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:Error' => 'Error~~',
|
'DBTools:Error' => 'Error~~',
|
||||||
'DBTools:Count' => 'Count~~',
|
'DBTools:Count' => 'Count~~',
|
||||||
'DBTools:SQLquery' => 'SQL query~~',
|
'DBTools:SQLquery' => 'SQL query~~',
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
|
|||||||
'DBTools:Class' => 'Class~~',
|
'DBTools:Class' => 'Class~~',
|
||||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||||
|
'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:Error' => 'Error~~',
|
'DBTools:Error' => 'Error~~',
|
||||||
'DBTools:Count' => 'Count~~',
|
'DBTools:Count' => 'Count~~',
|
||||||
'DBTools:SQLquery' => 'SQL query~~',
|
'DBTools:SQLquery' => 'SQL query~~',
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
|||||||
'DBTools:Class' => 'Klasse',
|
'DBTools:Class' => 'Klasse',
|
||||||
'DBTools:Title' => 'Onderhoudstools voor de database~~',
|
'DBTools:Title' => 'Onderhoudstools voor de database~~',
|
||||||
'DBTools:ErrorsFound' => 'Fouten gevonden',
|
'DBTools:ErrorsFound' => 'Fouten gevonden',
|
||||||
|
'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:Error' => 'Fout',
|
'DBTools:Error' => 'Fout',
|
||||||
'DBTools:Count' => 'Aantal',
|
'DBTools:Count' => 'Aantal',
|
||||||
'DBTools:SQLquery' => 'SQL-query',
|
'DBTools:SQLquery' => 'SQL-query',
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
|||||||
'DBTools:Class' => 'Classe',
|
'DBTools:Class' => 'Classe',
|
||||||
'DBTools:Title' => 'Manutenção da Base de Dados',
|
'DBTools:Title' => 'Manutenção da Base de Dados',
|
||||||
'DBTools:ErrorsFound' => 'Erros Encontrados',
|
'DBTools:ErrorsFound' => 'Erros 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~~',
|
||||||
'DBTools:Error' => 'Erros',
|
'DBTools:Error' => 'Erros',
|
||||||
'DBTools:Count' => 'Quantidade',
|
'DBTools:Count' => 'Quantidade',
|
||||||
'DBTools:SQLquery' => 'Query SQL',
|
'DBTools:SQLquery' => 'Query SQL',
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
|||||||
'DBTools:Class' => 'Класс',
|
'DBTools:Class' => 'Класс',
|
||||||
'DBTools:Title' => 'Инструменты обслуживания базы данных~~',
|
'DBTools:Title' => 'Инструменты обслуживания базы данных~~',
|
||||||
'DBTools:ErrorsFound' => 'Найденные ошибки',
|
'DBTools:ErrorsFound' => 'Найденные ошибки',
|
||||||
|
'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:Error' => 'Ошибка',
|
'DBTools:Error' => 'Ошибка',
|
||||||
'DBTools:Count' => 'Количество',
|
'DBTools:Count' => 'Количество',
|
||||||
'DBTools:SQLquery' => 'SQL-запрос',
|
'DBTools:SQLquery' => 'SQL-запрос',
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
|
|||||||
'DBTools:Class' => 'Class~~',
|
'DBTools:Class' => 'Class~~',
|
||||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||||
|
'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:Error' => 'Error~~',
|
'DBTools:Error' => 'Error~~',
|
||||||
'DBTools:Count' => 'Count~~',
|
'DBTools:Count' => 'Count~~',
|
||||||
'DBTools:SQLquery' => 'SQL query~~',
|
'DBTools:SQLquery' => 'SQL query~~',
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
|||||||
'DBTools:Class' => 'Class~~',
|
'DBTools:Class' => 'Class~~',
|
||||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||||
|
'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:Error' => 'Error~~',
|
'DBTools:Error' => 'Error~~',
|
||||||
'DBTools:Count' => 'Count~~',
|
'DBTools:Count' => 'Count~~',
|
||||||
'DBTools:SQLquery' => 'SQL query~~',
|
'DBTools:SQLquery' => 'SQL query~~',
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
|||||||
'DBTools:Class' => 'Class~~',
|
'DBTools:Class' => 'Class~~',
|
||||||
'DBTools:Title' => '数据库维护工具',
|
'DBTools:Title' => '数据库维护工具',
|
||||||
'DBTools:ErrorsFound' => '发现错误',
|
'DBTools:ErrorsFound' => '发现错误',
|
||||||
|
'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:Error' => '错误',
|
'DBTools:Error' => '错误',
|
||||||
'DBTools:Count' => '个数',
|
'DBTools:Count' => '个数',
|
||||||
'DBTools:SQLquery' => 'SQL 查询',
|
'DBTools:SQLquery' => 'SQL 查询',
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
|||||||
'Attachments:PreviewNotAvailable' => '该附件类型不支持预览.',
|
'Attachments:PreviewNotAvailable' => '该附件类型不支持预览.',
|
||||||
'Attachments:Error:FileTooLarge' => '上传的文件过大. %1$s',
|
'Attachments:Error:FileTooLarge' => '上传的文件过大. %1$s',
|
||||||
'Attachments:Error:UploadedFileEmpty' => '收到的文件为空,无法添加. 可能是因为您发送的是空文件,或者咨询 iTop 管理员服务器磁盘是否已满. ',
|
'Attachments:Error:UploadedFileEmpty' => '收到的文件为空,无法添加. 可能是因为您发送的是空文件,或者咨询 iTop 管理员服务器磁盘是否已满. ',
|
||||||
|
|
||||||
|
|
||||||
'Attachments:Render:Icons' => '显示为图标',
|
'Attachments:Render:Icons' => '显示为图标',
|
||||||
'Attachments:Render:Table' => '显示为列表',
|
'Attachments:Render:Table' => '显示为列表',
|
||||||
'UI:Attachments:DropYourFileHint' => '将文件拖放到此区域的任意位置',
|
'UI:Attachments:DropYourFileHint' => '将文件拖放到此区域的任意位置',
|
||||||
|
|||||||
@@ -170,10 +170,6 @@ JS
|
|||||||
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');
|
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');
|
||||||
|
|
||||||
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
|
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
|
||||||
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
|
|
||||||
IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'");
|
|
||||||
$oRestoreMutex->Lock();
|
|
||||||
IssueLog::Info('Backup Restore - LOCK acquired, executing...');
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
set_time_limit(0);
|
set_time_limit(0);
|
||||||
@@ -203,7 +199,6 @@ JS
|
|||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
unlink($tokenRealPath);
|
unlink($tokenRealPath);
|
||||||
$oRestoreMutex->Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$oPage->output();
|
$oPage->output();
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ if (!defined('APPROOT'))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
require_once(APPROOT.'application/application.inc.php');
|
require_once(APPROOT.'application/application.inc.php');
|
||||||
require_once(APPROOT.'application/webpage.class.inc.php');
|
|
||||||
require_once(APPROOT.'application/csvpage.class.inc.php');
|
|
||||||
require_once(APPROOT.'application/clipage.class.inc.php');
|
|
||||||
require_once(APPROOT.'application/ajaxwebpage.class.inc.php');
|
require_once(APPROOT.'application/ajaxwebpage.class.inc.php');
|
||||||
|
|
||||||
require_once(APPROOT.'core/log.class.inc.php');
|
require_once(APPROOT.'core/log.class.inc.php');
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ class DBRestore extends DBBackup
|
|||||||
{
|
{
|
||||||
parent::__construct($oConfig);
|
parent::__construct($oConfig);
|
||||||
|
|
||||||
$this->sDBUser = $oConfig->Get('db_user');
|
$this->sDBUser = $this->oConfig->Get('db_user');
|
||||||
$this->sDBPwd = $oConfig->Get('db_pwd');
|
$this->sDBPwd = $this->oConfig->Get('db_pwd');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function LogInfo($sMsg)
|
protected function LogInfo($sMsg)
|
||||||
@@ -127,79 +127,88 @@ class DBRestore extends DBBackup
|
|||||||
*/
|
*/
|
||||||
public function RestoreFromCompressedBackup($sFile, $sEnvironment = 'production')
|
public function RestoreFromCompressedBackup($sFile, $sEnvironment = 'production')
|
||||||
{
|
{
|
||||||
$this->LogInfo("Starting restore of ".basename($sFile));
|
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
|
||||||
|
IssueLog::Info("Backup Restore - Acquiring the LOCK 'restore.$sEnvironment'");
|
||||||
|
$oRestoreMutex->Lock();
|
||||||
|
|
||||||
$sNormalizedFile = strtolower(basename($sFile));
|
try {
|
||||||
if (substr($sNormalizedFile, -4) == '.zip')
|
IssueLog::Info('Backup Restore - LOCK acquired, executing...');
|
||||||
{
|
$bReadonlyBefore = SetupUtils::EnterReadOnlyMode(MetaModel::GetConfig());
|
||||||
$this->LogInfo('zip file detected');
|
|
||||||
$oArchive = new ZipArchiveEx();
|
|
||||||
$oArchive->open($sFile);
|
|
||||||
}
|
|
||||||
elseif (substr($sNormalizedFile, -7) == '.tar.gz')
|
|
||||||
{
|
|
||||||
$this->LogInfo('tar.gz file detected');
|
|
||||||
$oArchive = new TarGzArchive($sFile);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BackupException('Unsupported format for a backup file: '.$sFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the database
|
try {
|
||||||
//
|
//safe zone for db backup => cron is stopped/ itop in readonly
|
||||||
$sDataDir = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
|
$this->LogInfo("Starting restore of ".basename($sFile));
|
||||||
|
|
||||||
SetupUtils::builddir($sDataDir); // Here is the directory
|
|
||||||
$oArchive->extractTo($sDataDir);
|
|
||||||
|
|
||||||
$sDataFile = $sDataDir.'/itop-dump.sql';
|
$sNormalizedFile = strtolower(basename($sFile));
|
||||||
$this->LoadDatabase($sDataFile);
|
if (substr($sNormalizedFile, -4) == '.zip') {
|
||||||
|
$this->LogInfo('zip file detected');
|
||||||
|
$oArchive = new ZipArchiveEx();
|
||||||
|
$oArchive->open($sFile);
|
||||||
|
} elseif (substr($sNormalizedFile, -7) == '.tar.gz') {
|
||||||
|
$this->LogInfo('tar.gz file detected');
|
||||||
|
$oArchive = new TarGzArchive($sFile);
|
||||||
|
} else {
|
||||||
|
throw new BackupException('Unsupported format for a backup file: '.$sFile);
|
||||||
|
}
|
||||||
|
|
||||||
// Update the code
|
// Load the database
|
||||||
//
|
//
|
||||||
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
|
$sDataDir = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax());
|
||||||
|
|
||||||
if (is_file($sDataDir.'/delta.xml'))
|
SetupUtils::builddir($sDataDir); // Here is the directory
|
||||||
{
|
$oArchive->extractTo($sDataDir);
|
||||||
// Extract and rename delta.xml => <env>.delta.xml;
|
|
||||||
rename($sDataDir.'/delta.xml', $sDeltaFile);
|
$sDataFile = $sDataDir.'/itop-dump.sql';
|
||||||
}
|
$this->LoadDatabase($sDataFile);
|
||||||
else
|
|
||||||
{
|
// Update the code
|
||||||
@unlink($sDeltaFile);
|
//
|
||||||
}
|
$sDeltaFile = APPROOT.'data/'.$sEnvironment.'.delta.xml';
|
||||||
if (is_dir(APPROOT.'data/production-modules/'))
|
|
||||||
{
|
if (is_file($sDataDir.'/delta.xml')) {
|
||||||
try
|
// Extract and rename delta.xml => <env>.delta.xml;
|
||||||
{
|
rename($sDataDir.'/delta.xml', $sDeltaFile);
|
||||||
SetupUtils::rrmdir(APPROOT.'data/production-modules/');
|
} else {
|
||||||
}
|
@unlink($sDeltaFile);
|
||||||
catch (Exception $e)
|
}
|
||||||
{
|
if (is_dir(APPROOT.'data/production-modules/')) {
|
||||||
throw new BackupException("Can't remove production-modules dir", 0, $e);
|
try {
|
||||||
|
SetupUtils::rrmdir(APPROOT.'data/production-modules/');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new BackupException("Can't remove production-modules dir", 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_dir($sDataDir.'/production-modules')) {
|
||||||
|
rename($sDataDir.'/production-modules', APPROOT.'data/production-modules/');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sConfigFile = APPROOT.'conf/'.$sEnvironment.'/config-itop.php';
|
||||||
|
@chmod($sConfigFile, 0770); // Allow overwriting the file
|
||||||
|
rename($sDataDir.'/config-itop.php', $sConfigFile);
|
||||||
|
@chmod($sConfigFile, 0440); // Read-only
|
||||||
|
|
||||||
|
try {
|
||||||
|
SetupUtils::rrmdir($sDataDir);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new BackupException("Can't remove data dir", 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oEnvironment = new RunTimeEnvironment($sEnvironment);
|
||||||
|
$oEnvironment->CompileFrom($sEnvironment);
|
||||||
|
} finally {
|
||||||
|
if (! $bReadonlyBefore) {
|
||||||
|
SetupUtils::ExitReadOnlyMode();
|
||||||
|
} else {
|
||||||
|
//we are in the scope of main process that needs to handle/keep readonly mode.
|
||||||
|
$this->LogInfo("Keep readonly mode after restore");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_dir($sDataDir.'/production-modules'))
|
finally
|
||||||
{
|
{
|
||||||
rename($sDataDir.'/production-modules', APPROOT.'data/production-modules/');
|
IssueLog::Info('Backup Restore - LOCK released.');
|
||||||
|
$oRestoreMutex->Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
$sConfigFile = APPROOT.'conf/'.$sEnvironment.'/config-itop.php';
|
|
||||||
@chmod($sConfigFile, 0770); // Allow overwriting the file
|
|
||||||
rename($sDataDir.'/config-itop.php', $sConfigFile);
|
|
||||||
@chmod($sConfigFile, 0440); // Read-only
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SetupUtils::rrmdir($sDataDir);
|
|
||||||
}
|
|
||||||
catch (Exception $e)
|
|
||||||
{
|
|
||||||
throw new BackupException("Can't remove data dir", 0, $e);
|
|
||||||
}
|
|
||||||
|
|
||||||
$oEnvironment = new RunTimeEnvironment($sEnvironment);
|
|
||||||
$oEnvironment->CompileFrom($sEnvironment);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
|||||||
'bkp-status-backups-auto' => 'Scheduled backups',
|
'bkp-status-backups-auto' => 'Scheduled backups',
|
||||||
'bkp-status-backups-manual' => 'Manual backups',
|
'bkp-status-backups-manual' => 'Manual backups',
|
||||||
'bkp-status-backups-none' => 'No backup yet',
|
'bkp-status-backups-none' => 'No backup yet',
|
||||||
'bkp-next-backup' => 'The next backup will occur on <b>%1$s</b> (%2$s) at %3$s',
|
'bkp-next-backup' => 'The next backup will occur on <b>%1$s</b> (%2$s) at %3$s.',
|
||||||
|
'bkp-next-backup-unknown' => 'The next backup is <b>not scheduled</b> yet.',
|
||||||
'bkp-button-backup-now' => 'Backup now!',
|
'bkp-button-backup-now' => 'Backup now!',
|
||||||
'bkp-button-restore-now' => 'Restore!',
|
'bkp-button-restore-now' => 'Restore!',
|
||||||
'bkp-confirm-backup' => 'Please confirm that you do request the backup to occur right now.',
|
'bkp-confirm-backup' => 'Please confirm that you do request the backup to occur right now.',
|
||||||
|
|||||||
203
datamodels/2.x/itop-backup/restore.php
Normal file
203
datamodels/2.x/itop-backup/restore.php
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2013-2021 Combodo SARL
|
||||||
|
*
|
||||||
|
* This file is part of iTop.
|
||||||
|
*
|
||||||
|
* iTop is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* iTop is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__));
|
||||||
|
if (!defined('APPROOT'))
|
||||||
|
{
|
||||||
|
if (file_exists(__DIR__.'/../../approot.inc.php'))
|
||||||
|
{
|
||||||
|
require_once __DIR__.'/../../approot.inc.php'; // When in env-xxxx folder
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
require_once __DIR__.'/../../../approot.inc.php'; // When in datamodels/x.x folder
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require_once(APPROOT.'application/application.inc.php');
|
||||||
|
require_once(APPROOT.'application/ajaxwebpage.class.inc.php');
|
||||||
|
require_once(APPROOT.'core/log.class.inc.php');
|
||||||
|
require_once(APPROOT.'application/startup.inc.php');
|
||||||
|
require_once(dirname(__FILE__).'/dbrestore.class.inc.php');
|
||||||
|
|
||||||
|
class MyDBRestore extends DBRestore
|
||||||
|
{
|
||||||
|
/** @var Page used to send log */
|
||||||
|
protected $oPage;
|
||||||
|
|
||||||
|
protected function LogInfo($sMsg)
|
||||||
|
{
|
||||||
|
$this->oPage->p($sMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function LogError($sMsg)
|
||||||
|
{
|
||||||
|
$this->oPage->p('Error: '.$sMsg);
|
||||||
|
ToolsLog::Error($sMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($oPage)
|
||||||
|
{
|
||||||
|
$this->oPage = $oPage;
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a parameter (possibly empty) was specified when calling this page
|
||||||
|
*/
|
||||||
|
function CheckParam($sParamName)
|
||||||
|
{
|
||||||
|
global $argv;
|
||||||
|
|
||||||
|
if (isset($_REQUEST[$sParamName])) return true; // HTTP parameter either GET or POST
|
||||||
|
if (!is_array($argv)) return false;
|
||||||
|
foreach($argv as $sArg)
|
||||||
|
{
|
||||||
|
if ($sArg == '--'.$sParamName) return true; // Empty command line parameter, long unix style
|
||||||
|
if ($sArg == $sParamName) return true; // Empty command line parameter, Windows style
|
||||||
|
if ($sArg == '-'.$sParamName) return true; // Empty command line parameter, short unix style
|
||||||
|
if (preg_match('/^--'.$sParamName.'=(.*)$/', $sArg, $aMatches)) return true; // Command parameter with a value
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Usage($oP)
|
||||||
|
{
|
||||||
|
$oP->p('Restore an iTop from a backup file');
|
||||||
|
$oP->p('Parameters:');
|
||||||
|
if (utils::IsModeCLI())
|
||||||
|
{
|
||||||
|
$oP->p('auth_user: login, must be administrator');
|
||||||
|
$oP->p('auth_pwd: ...');
|
||||||
|
}
|
||||||
|
$oP->p('backup_file [optional]: name of the file to store the backup into. Follows the PHP strftime format spec. The following placeholders are available: __HOST__, __DB__, __SUBNAME__');
|
||||||
|
$oP->p('mysql_bindir [optional]: specify the path for mysql executable');
|
||||||
|
|
||||||
|
if (utils::IsModeCLI())
|
||||||
|
{
|
||||||
|
$oP->p('Example: php -q restore.php --auth_user=admin --auth_pwd=myPassw0rd --backup_file=/tmp/backup.zip');
|
||||||
|
$oP->p('Known limitation: the current directory must be the directory of backup.php');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$oP->p('Example: .../restore.php?backup_file=/tmp/backup.zip');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ExitError($oP, $sMessage)
|
||||||
|
{
|
||||||
|
ToolsLog::Error($sMessage);
|
||||||
|
$oP->p($sMessage);
|
||||||
|
$oP->output();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ReadMandatoryParam($oP, $sParam)
|
||||||
|
{
|
||||||
|
$sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, 'raw_data');
|
||||||
|
if (is_null($sValue))
|
||||||
|
{
|
||||||
|
ExitError($oP, "ERROR: Missing argument '$sParam'");
|
||||||
|
}
|
||||||
|
return trim($sValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////
|
||||||
|
// Main program
|
||||||
|
|
||||||
|
set_time_limit(0);
|
||||||
|
|
||||||
|
if (utils::IsModeCLI())
|
||||||
|
{
|
||||||
|
$oP = new CLIPage("iTop - iTop Restore");
|
||||||
|
|
||||||
|
SetupUtils::CheckPhpAndExtensionsForCli($oP);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$oP = new WebPage("iTop - iTop Restore");
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
utils::UseParamFile();
|
||||||
|
}
|
||||||
|
catch(Exception $e)
|
||||||
|
{
|
||||||
|
ExitError($oP, $e->GetMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (utils::IsModeCLI())
|
||||||
|
{
|
||||||
|
$oP->p(date('Y-m-d H:i:s')." - running backup utility");
|
||||||
|
$sAuthUser = ReadMandatoryParam($oP, 'auth_user');
|
||||||
|
$sAuthPwd = ReadMandatoryParam($oP, 'auth_pwd');
|
||||||
|
$sBackupFile = ReadMandatoryParam($oP, 'backup_file');
|
||||||
|
$bDownloadBackup = false;
|
||||||
|
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd))
|
||||||
|
{
|
||||||
|
UserRights::Login($sAuthUser); // Login & set the user's language
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ExitError($oP, "Access restricted or wrong credentials ('$sAuthUser')");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_file($sBackupFile) && is_readable($sBackupFile)){
|
||||||
|
ExitError($oP, "Cannot access backup file ('$sBackupFile')");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
require_once(APPROOT.'application/loginwebpage.class.inc.php');
|
||||||
|
LoginWebPage::DoLogin(); // Check user rights and prompt if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!UserRights::IsAdministrator())
|
||||||
|
{
|
||||||
|
ExitError($oP, "Access restricted to administors");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CheckParam('?') || CheckParam('h') || CheckParam('help'))
|
||||||
|
{
|
||||||
|
Usage($oP);
|
||||||
|
$oP->output();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Interpret strftime specifications (like %Y) and database placeholders
|
||||||
|
$oRestore = new MyDBRestore($oP);
|
||||||
|
$oRestore->SetMySQLBinDir(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''));
|
||||||
|
|
||||||
|
$res = false;
|
||||||
|
if (MetaModel::GetConfig()->Get('demo_mode'))
|
||||||
|
{
|
||||||
|
$oP->p("Sorry, iTop is in demonstration mode: the feature is disabled");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
|
||||||
|
$oRestore->RestoreFromCompressedBackup($sBackupFile, $sEnvironment);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oP->output();
|
||||||
@@ -337,18 +337,29 @@ try {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do backup now
|
// Next occurrence
|
||||||
//
|
//
|
||||||
$oBackupExec = new BackupExec();
|
/** @var \BackgroundTask $oTask */
|
||||||
$oNext = $oBackupExec->GetNextOccurrence();
|
$oTask = MetaModel::GetObjectByName(BackgroundTask::class, BackupExec::class, false);
|
||||||
$sNextOccurrence = Dict::Format('bkp-next-backup', $aWeekDayToString[$oNext->Format('N')], $oNext->Format('Y-m-d'),
|
if ($oTask)
|
||||||
$oNext->Format('H:i'));
|
{
|
||||||
|
$oTimezone = new DateTimeZone(MetaModel::GetConfig()->Get('timezone'));
|
||||||
|
$oNext = new DateTime($oTask->Get('next_run_date'), $oTimezone);
|
||||||
|
$sNextOccurrence = Dict::Format('bkp-next-backup', $aWeekDayToString[$oNext->Format('N')], $oNext->Format('Y-m-d'),
|
||||||
|
$oNext->Format('H:i'));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sNextOccurrence = Dict::S('bkp-next-backup-unknown');
|
||||||
|
}
|
||||||
$oFieldsetBackupNow->AddSubBlock(
|
$oFieldsetBackupNow->AddSubBlock(
|
||||||
AlertUIBlockFactory::MakeForInformation('', $sNextOccurrence)
|
AlertUIBlockFactory::MakeForInformation('', $sNextOccurrence)
|
||||||
->SetIsClosable(false)
|
->SetIsClosable(false)
|
||||||
->SetIsCollapsible(false)
|
->SetIsCollapsible(false)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Do backup now
|
||||||
|
//
|
||||||
$oLaunchBackupButton = ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('bkp-button-backup-now'));
|
$oLaunchBackupButton = ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('bkp-button-backup-now'));
|
||||||
$oLaunchBackupButton->SetOnClickJsCode('LaunchBackupNow();');
|
$oLaunchBackupButton->SetOnClickJsCode('LaunchBackupNow();');
|
||||||
$oFieldsetBackupNow->AddSubBlock($oLaunchBackupButton);
|
$oFieldsetBackupNow->AddSubBlock($oLaunchBackupButton);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class ConfigNodesVisitor extends NodeVisitorAbstract
|
|||||||
Node\Name::class,
|
Node\Name::class,
|
||||||
|
|
||||||
Node\Const_::class,
|
Node\Const_::class,
|
||||||
|
Node\Identifier::class,
|
||||||
|
|
||||||
Node\Expr\Array_::class,
|
Node\Expr\Array_::class,
|
||||||
Node\Expr\ArrayDimFetch::class,
|
Node\Expr\ArrayDimFetch::class,
|
||||||
@@ -44,6 +45,7 @@ class ConfigNodesVisitor extends NodeVisitorAbstract
|
|||||||
Node\Expr\PreDec::class,
|
Node\Expr\PreDec::class,
|
||||||
Node\Expr\PreInc::class,
|
Node\Expr\PreInc::class,
|
||||||
Node\Expr\Print_::class,
|
Node\Expr\Print_::class,
|
||||||
|
Node\Stmt\Expression::class,
|
||||||
Node\Expr\Ternary::class,
|
Node\Expr\Ternary::class,
|
||||||
Node\Expr\UnaryMinus::class,
|
Node\Expr\UnaryMinus::class,
|
||||||
Node\Expr\UnaryPlus::class,
|
Node\Expr\UnaryPlus::class,
|
||||||
|
|||||||
@@ -115,3 +115,5 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
|||||||
'Class:ModuleInstallation/Attribute:version' => 'Version',
|
'Class:ModuleInstallation/Attribute:version' => 'Version',
|
||||||
'Class:ModuleInstallation/Attribute:comment' => 'Kommentar',
|
'Class:ModuleInstallation/Attribute:comment' => 'Kommentar',
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -83,3 +83,4 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
|||||||
'iTopHub:InstallationStatus:Version_NotInstalled' => 'Version %1$s <b>未</b> 安装.',
|
'iTopHub:InstallationStatus:Version_NotInstalled' => 'Version %1$s <b>未</b> 安装.',
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
|||||||
Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
|||||||
Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
|||||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Dieses Objekt schließen',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Dieses Objekt schließen',
|
||||||
'Portal:Form:Close:Warning' => 'Soll diese Eingabemaske verlassen werden? Eingegebene Daten werden nicht gespeichert.',
|
'Portal:Form:Close:Warning' => 'Soll diese Eingabemaske verlassen werden? Eingegebene Daten werden nicht gespeichert.',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
|||||||
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
|||||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
|||||||
Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
|
|||||||
Dict::Add('JA JP', 'Japanese', '日本語', array(
|
Dict::Add('JA JP', 'Japanese', '日本語', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
|||||||
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Ben je zeker dat je dit venster wil sluiten? Ingevoerde gegevens kunnen verloren gaan.',
|
'Portal:Form:Close:Warning' => 'Ben je zeker dat je dit venster wil sluiten? Ingevoerde gegevens kunnen verloren gaan.',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
|||||||
Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Você deseja abandonar esta página? Os dados digitados podem ser perdidos.',
|
'Portal:Form:Close:Warning' => 'Você deseja abandonar esta página? Os dados digitados podem ser perdidos.',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
|||||||
Dict::Add('RU RU', 'Russian', 'Русский', array(
|
Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Вы действительно хотите закрыть эту форму? Введённые данные могут быть утеряны.',
|
'Portal:Form:Close:Warning' => 'Вы действительно хотите закрыть эту форму? Введённые данные могут быть утеряны.',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
|
|||||||
Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
|
Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
|||||||
Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
|||||||
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||||
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
'Portal:Form:Caselog:Entry:Close:Tooltip' => 'Close this entry~~',
|
||||||
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
'Portal:Form:Close:Warning' => 'Do you want to leave this form ? Data entered may be lost~~',
|
||||||
|
'Portal:Error:ObjectCannotBeCreated' => 'Error: object cannot be created. Check associated objects and attachments before submitting again this form.~~',
|
||||||
|
'Portal:Error:ObjectCannotBeUpdated' => 'Error: object cannot be updated. Check associated objects and attachments before submitting again this form.~~',
|
||||||
));
|
));
|
||||||
|
|
||||||
// UserProfile brick
|
// UserProfile brick
|
||||||
|
|||||||
@@ -1630,6 +1630,21 @@ table .group-actions {
|
|||||||
.cke_toolbox_collapser, .cke_toolbox_collapser .cke_arrow {
|
.cke_toolbox_collapser, .cke_toolbox_collapser .cke_arrow {
|
||||||
cursor: pointer !important;
|
cursor: pointer !important;
|
||||||
}
|
}
|
||||||
|
.cke_toolbox_collapser.cke_toolbox_collapser_min ~ .editor-fullscreen-button {
|
||||||
|
display: block;
|
||||||
|
width: 12px;
|
||||||
|
height: 11px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
cursor: pointer;
|
||||||
|
/* !important so it overrides the .cke_reset_all style */
|
||||||
|
background-position: center center !important;
|
||||||
|
background-repeat: no-repeat !important;
|
||||||
|
background-size: 100% !important;
|
||||||
|
background-image: url('../../../../../images/full-screen.png') !important;
|
||||||
|
}
|
||||||
|
.cke_toolbox_collapser.cke_toolbox_collapser_min ~ .editor-fullscreen-button:hover {
|
||||||
|
background-color: #E5E5E5;
|
||||||
|
}
|
||||||
/* DataTables : Selection inputs */
|
/* DataTables : Selection inputs */
|
||||||
.dataTable.table th span.row_input, .dataTable.table td span.row_input {
|
.dataTable.table th span.row_input, .dataTable.table td span.row_input {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user