mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-22 01:58:47 +02:00
Compare commits
185 Commits
3.0.3
...
support/2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13239c2751 | ||
|
|
81b20ee583 | ||
|
|
38683c20b1 | ||
|
|
81791dd253 | ||
|
|
e77e0eec9f | ||
|
|
960133c0df | ||
|
|
5232694c04 | ||
|
|
874a5fd2ce | ||
|
|
063bb9680e | ||
|
|
8f8ac46f55 | ||
|
|
07b904ee1b | ||
|
|
affed69999 | ||
|
|
d5754fc568 | ||
|
|
44290db312 | ||
|
|
c49ceae75e | ||
|
|
8980f627e9 | ||
|
|
160bfd714b | ||
|
|
8d58372074 | ||
|
|
37fc1a5723 | ||
|
|
95aa444ee6 | ||
|
|
f5de808c7c | ||
|
|
e03033ce52 | ||
|
|
374b35f78a | ||
|
|
04bd8cc5ce | ||
|
|
8cb701bda3 | ||
|
|
1b29746806 | ||
|
|
fb9c317256 | ||
|
|
0904a21e3f | ||
|
|
82d11eeb47 | ||
|
|
142d6c8993 | ||
|
|
320922a13d | ||
|
|
f03d731b1d | ||
|
|
8be7628668 | ||
|
|
62caf16153 | ||
|
|
163a3afc0f | ||
|
|
f8b54be896 | ||
|
|
53dc452d61 | ||
|
|
ccaf2dc5b7 | ||
|
|
5d5df5ad1a | ||
|
|
32140b360f | ||
|
|
d85767a838 | ||
|
|
eeec57536b | ||
|
|
16ff6341d0 | ||
|
|
9dab8679d6 | ||
|
|
4c78488644 | ||
|
|
b65e931c4c | ||
|
|
6cb3519308 | ||
|
|
cfb9fae648 | ||
|
|
f4e791734f | ||
|
|
6653ab0668 | ||
|
|
7ab258ba03 | ||
|
|
b5af30a93f | ||
|
|
bbfa601ab1 | ||
|
|
172b1cb1ff | ||
|
|
ca356859a3 | ||
|
|
5efe294895 | ||
|
|
e0170ccc7e | ||
|
|
3b78885f38 | ||
|
|
ed562c9f73 | ||
|
|
85c576a986 | ||
|
|
5a34c76cc4 | ||
|
|
da99a250bf | ||
|
|
dbd5ba0377 | ||
|
|
5d6f293956 | ||
|
|
986c24d777 | ||
|
|
763112c179 | ||
|
|
a5efd981d8 | ||
|
|
2922b22478 | ||
|
|
2af05a437e | ||
|
|
a9f8dcc5e8 | ||
|
|
c325294e17 | ||
|
|
da490739be | ||
|
|
b867faa355 | ||
|
|
7453cc184f | ||
|
|
473cf004b6 | ||
|
|
f6fec506b1 | ||
|
|
5c12151c26 | ||
|
|
5d6c4939f6 | ||
|
|
1b3a2c8470 | ||
|
|
618d8e6468 | ||
|
|
01a955a16f | ||
|
|
87582a021b | ||
|
|
9830178a47 | ||
|
|
c140ebcb6b | ||
|
|
7a0a4e377b | ||
|
|
7fffbb60e9 | ||
|
|
2fd9523c16 | ||
|
|
a4f6f6e877 | ||
|
|
94c604a6af | ||
|
|
6995a3c641 | ||
|
|
9865bf0779 | ||
|
|
d5449cca42 | ||
|
|
5d38d22c50 | ||
|
|
99d69493d1 | ||
|
|
c9bb628c30 | ||
|
|
08e8d15d78 | ||
|
|
7b59df216b | ||
|
|
cb5eab812e | ||
|
|
c9b73a7fe2 | ||
|
|
3b2da39469 | ||
|
|
fc22d91232 | ||
|
|
b10bcb976d | ||
|
|
5a43448644 | ||
|
|
77409eed99 | ||
|
|
83a70daf68 | ||
|
|
a49a4e6c2b | ||
|
|
7419749ba6 | ||
|
|
037dfe1df6 | ||
|
|
0b26d45014 | ||
|
|
4fd8177165 | ||
|
|
a2cdf214f0 | ||
|
|
013173019f | ||
|
|
9469681a0c | ||
|
|
c72cb7e70e | ||
|
|
9df92665e0 | ||
|
|
8ecebee511 | ||
|
|
35cd965360 | ||
|
|
e5dd51f637 | ||
|
|
0a6c82dfe1 | ||
|
|
24c0f4950f | ||
|
|
d4dbbc59d4 | ||
|
|
dc0cd44c79 | ||
|
|
6c6131ce03 | ||
|
|
e76728b2bf | ||
|
|
e946fc65fc | ||
|
|
0d8ff7bbac | ||
|
|
bf768311c2 | ||
|
|
a8c689c6c0 | ||
|
|
1990ccb5d8 | ||
|
|
e107be56e4 | ||
|
|
ed6df77cbb | ||
|
|
1ad28312ec | ||
|
|
f002aa04cd | ||
|
|
b86d70623e | ||
|
|
fe3467309d | ||
|
|
851ab9c356 | ||
|
|
aef3c2e609 | ||
|
|
6d13397ba1 | ||
|
|
c0c8a13864 | ||
|
|
cd9beec313 | ||
|
|
8295eaed90 | ||
|
|
5475b9fbbe | ||
|
|
6f8e7c7002 | ||
|
|
bc7c1b4744 | ||
|
|
4d8246c4d8 | ||
|
|
5c61d725e1 | ||
|
|
2c4cad4dac | ||
|
|
da45651121 | ||
|
|
d388ce9a06 | ||
|
|
47e71d8838 | ||
|
|
2b5973ec67 | ||
|
|
78396d8e4a | ||
|
|
9afc22bd8f | ||
|
|
264a8cd70a | ||
|
|
aa1834170b | ||
|
|
f94d67ab35 | ||
|
|
3048c8c41f | ||
|
|
246e4a9f50 | ||
|
|
6d58adb6dd | ||
|
|
5a0b5364d6 | ||
|
|
76eed2eba0 | ||
|
|
1ec671ef61 | ||
|
|
72716b7ec8 | ||
|
|
4f999de844 | ||
|
|
ea49c0a87c | ||
|
|
6cc971849b | ||
|
|
2405810864 | ||
|
|
fff46d99fc | ||
|
|
3a891f707c | ||
|
|
8b6ea43ebe | ||
|
|
90cf7502e8 | ||
|
|
c596fa2967 | ||
|
|
a45177410e | ||
|
|
9e96ea2873 | ||
|
|
1172159745 | ||
|
|
fa038ded3d | ||
|
|
e7ea1b831c | ||
|
|
4aff65f98b | ||
|
|
3c94974d9d | ||
|
|
fbd72b2783 | ||
|
|
4e95ca3c7b | ||
|
|
1114ed9562 | ||
|
|
34368fe795 | ||
|
|
0f016d7511 | ||
|
|
0001e8ffc4 |
83
.github/pull_request_template.md
vendored
Normal file
83
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<!--
|
||||||
|
|
||||||
|
IMPORTANT: Please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
|
||||||
|
|
||||||
|
Any PRs not following the guidelines or with missing information will not be considered.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Base information
|
||||||
|
| Question | Answer
|
||||||
|
|---------------------------------------------------------------|--------
|
||||||
|
| Related to a SourceForge thead / Another PR / Combodo ticket? | <!-- Put the URL -->
|
||||||
|
| Type of change? | Bug fix / Enhancement / Translations
|
||||||
|
|
||||||
|
|
||||||
|
## Symptom (bug) / Objective (enhancement)
|
||||||
|
<!--
|
||||||
|
If it's a bug
|
||||||
|
- Explain the symptom in details
|
||||||
|
- If possible put error messages, logs or screenshots (you can paste image directly in this editor).
|
||||||
|
|
||||||
|
If it's an enhancement
|
||||||
|
- Describe what is blocking you, what is the objective with as much details as possible.
|
||||||
|
- Add screenshots if it's related to UI.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Reproduction procedure (bug)
|
||||||
|
<!--
|
||||||
|
Remove this section only if it's NOT a bug.
|
||||||
|
|
||||||
|
Otherwise, explain step by step how to reproduce the issue on a standard iTop Community.
|
||||||
|
|
||||||
|
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
|
||||||
|
-->
|
||||||
|
|
||||||
|
1. On iTop x.y.z <!-- Put complete iTop version (eg. 3.1.0-2) -->
|
||||||
|
2. With PHP x.y.z <!-- Put complete PHP version (eg. 8.1.24) -->
|
||||||
|
2. First go there
|
||||||
|
2. Then do that
|
||||||
|
3. ...
|
||||||
|
4. Finally, see that...
|
||||||
|
|
||||||
|
|
||||||
|
## Cause (bug)
|
||||||
|
<!--
|
||||||
|
Remove this section only if it's NOT a bug.
|
||||||
|
|
||||||
|
Otherwise, explain what is the cause of the issue (where in the code and why)
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Proposed solution (bug and enhancement)
|
||||||
|
<!--
|
||||||
|
Explain in details how you are proposing to solve this:
|
||||||
|
- What did you do in the code and why
|
||||||
|
- If you changed something in the UI, put before / after screenshots (you can paste image directly in this editor)
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
## Checklist before requesting a review
|
||||||
|
<!--
|
||||||
|
Don't remove these lines, check them once done.
|
||||||
|
-->
|
||||||
|
- [ ] I have performed a self-review of my code
|
||||||
|
- [ ] I have tested all changes I made on an iTop instance
|
||||||
|
- [ ] I have added a unit test, otherwise I have explained why I couldn't
|
||||||
|
- [ ] Is the PR clear and detailed enough so anyone can understand digging in the code?
|
||||||
|
|
||||||
|
## Checklist of things to do before PR is ready to merge
|
||||||
|
<!--
|
||||||
|
Things that needs to be done in the PR before it can be considered as ready to be merged
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- Changes requested in the review
|
||||||
|
- Unit test to add
|
||||||
|
- Dictionary entries to translate
|
||||||
|
- ...
|
||||||
|
-->
|
||||||
|
|
||||||
|
- [ ] ...
|
||||||
|
- [ ] ...
|
||||||
|
- [ ] ...
|
||||||
43
.github/workflows/action.yml
vendored
Normal file
43
.github/workflows/action.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: Add PRs to Combodo PRs Dashboard
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
add-to-project:
|
||||||
|
name: Add PR to Combodo Project
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check if author is a member of the organization
|
||||||
|
id: check-membership
|
||||||
|
run: |
|
||||||
|
ORG="Combodo"
|
||||||
|
AUTHOR=$(jq -r .pull_request.user.login "$GITHUB_EVENT_PATH")
|
||||||
|
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
|
||||||
|
"https://api.github.com/orgs/$ORG/members/$AUTHOR")
|
||||||
|
if [ "$RESPONSE" == "404" ]; then
|
||||||
|
echo "project_url=https://github.com/orgs/Combodo/projects/5" >> $GITHUB_ENV
|
||||||
|
echo "is_member=false" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "project_url=https://github.com/orgs/Combodo/projects/4" >> $GITHUB_ENV
|
||||||
|
echo "is_member=true" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Add internal tag if member
|
||||||
|
if: env.is_member == 'true'
|
||||||
|
run: |
|
||||||
|
curl -X POST -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/labels \
|
||||||
|
-d '{"labels":["internal"]}'
|
||||||
|
env:
|
||||||
|
is_member: ${{ env.is_member }}
|
||||||
|
|
||||||
|
- name: Add PR to the appropriate project
|
||||||
|
uses: actions/add-to-project@v1.0.2
|
||||||
|
with:
|
||||||
|
project-url: ${{ env.project_url }}
|
||||||
|
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -24,7 +24,9 @@ tests/*/vendor/*
|
|||||||
|
|
||||||
# iTop extensions
|
# iTop extensions
|
||||||
/extensions/**
|
/extensions/**
|
||||||
|
!/extensions/.htaccess
|
||||||
!/extensions/readme.txt
|
!/extensions/readme.txt
|
||||||
|
!/extensions/web.config
|
||||||
|
|
||||||
# all logs but listing prevention
|
# all logs but listing prevention
|
||||||
/log/**
|
/log/**
|
||||||
@@ -32,8 +34,10 @@ tests/*/vendor/*
|
|||||||
!/log/index.php
|
!/log/index.php
|
||||||
!/log/web.config
|
!/log/web.config
|
||||||
|
|
||||||
# PHPUnit cache file
|
# PHPUnit: Cache file, local XML working copies
|
||||||
/tests/php-unit-tests/.phpunit.result.cache
|
/tests/php-unit-tests/.phpunit.result.cache
|
||||||
|
/tests/php-unit-tests/phpunit.xml
|
||||||
|
/tests/php-unit-tests/postbuild_integration.xml
|
||||||
|
|
||||||
|
|
||||||
# Jetbrains
|
# Jetbrains
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ $iTopFolder = __DIR__."/../../../";
|
|||||||
require_once("$iTopFolder/approot.inc.php");
|
require_once("$iTopFolder/approot.inc.php");
|
||||||
require_once(APPROOT."/application/utils.inc.php");
|
require_once(APPROOT."/application/utils.inc.php");
|
||||||
|
|
||||||
if (php_sapi_name() !== 'cli')
|
if (PHP_SAPI !== 'cli')
|
||||||
{
|
{
|
||||||
throw new \Exception('This script can only run from CLI');
|
throw new \Exception('This script can only run from CLI');
|
||||||
}
|
}
|
||||||
@@ -48,4 +48,4 @@ if (!file_exists($sCssFile))
|
|||||||
{
|
{
|
||||||
fwrite(STDERR, "Failed to compile $sCssFile, exiting.");
|
fwrite(STDERR, "Failed to compile $sCssFile, exiting.");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ $iTopFolder = __DIR__ . "/../../" ;
|
|||||||
require_once ("$iTopFolder/approot.inc.php");
|
require_once ("$iTopFolder/approot.inc.php");
|
||||||
require_once (APPROOT."/setup/setuputils.class.inc.php");
|
require_once (APPROOT."/setup/setuputils.class.inc.php");
|
||||||
|
|
||||||
if (php_sapi_name() !== 'cli')
|
if (PHP_SAPI !== 'cli')
|
||||||
{
|
{
|
||||||
throw new \Exception('This script can only run from CLI');
|
throw new \Exception('This script can only run from CLI');
|
||||||
}
|
}
|
||||||
@@ -70,4 +70,4 @@ if (false === empty($aMissing)) {
|
|||||||
echo "Some new tests dirs exists !\n"
|
echo "Some new tests dirs exists !\n"
|
||||||
.' They must be declared either in the allowed or denied list in '.iTopComposer::class." (see N°2651).\n"
|
.' They must be declared either in the allowed or denied list in '.iTopComposer::class." (see N°2651).\n"
|
||||||
.' List of dirs:'."\n".var_export($aMissing, true);
|
.' List of dirs:'."\n".var_export($aMissing, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,17 +19,24 @@
|
|||||||
* The target license file path is in `$xmlFilePath`
|
* The target license file path is in `$xmlFilePath`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$iTopFolder = __DIR__ . "/../../" ;
|
$iTopFolder = __DIR__."/../../";
|
||||||
$xmlFilePath = $iTopFolder . "setup/licenses/community-licenses.xml";
|
$xmlFilePath = $iTopFolder."setup/licenses/community-licenses.xml";
|
||||||
|
|
||||||
function get_scope($product_node)
|
$jqExec = shell_exec("jq -V"); // a param is mandatory otherwise the script will freeze
|
||||||
{
|
if ((null === $jqExec) || (false === $jqExec)) {
|
||||||
|
echo "/!\ JQ is required but cannot be launched :( \n";
|
||||||
|
echo "Check this script PHPDoc block for instructions\n";
|
||||||
|
die(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function get_scope($product_node) {
|
||||||
$scope = $product_node->getAttribute("scope");
|
$scope = $product_node->getAttribute("scope");
|
||||||
|
|
||||||
if ($scope === "")
|
if ($scope === "") { //put iTop first
|
||||||
{ //put iTop first
|
|
||||||
return "aaaaaaaaa";
|
return "aaaaaaaaa";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $scope;
|
return $scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -425,6 +425,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
UR_ACTION_BULK_DELETE => 'bd',
|
UR_ACTION_BULK_DELETE => 'bd',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array $aUsersProfilesList Cache of users' profiles. Hash array of user ID => [profile ID => profile friendlyname, profile ID => profile friendlyname, ...]
|
||||||
|
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6887
|
||||||
|
*/
|
||||||
|
private $aUsersProfilesList = [];
|
||||||
|
|
||||||
// Installation: create the very first user
|
// Installation: create the very first user
|
||||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||||
{
|
{
|
||||||
@@ -654,8 +660,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||||
|
|
||||||
$bStatus = null;
|
$bStatus = null;
|
||||||
|
// Cache user's profiles
|
||||||
|
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
|
||||||
|
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
|
||||||
|
}
|
||||||
// Call the API of UserRights because it caches the list for us
|
// Call the API of UserRights because it caches the list for us
|
||||||
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
|
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
|
||||||
{
|
{
|
||||||
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
|
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
|
||||||
if (!is_null($bGrant))
|
if (!is_null($bGrant))
|
||||||
@@ -781,11 +791,16 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
// Note: this code is VERY close to the code of IsActionAllowed()
|
// Note: this code is VERY close to the code of IsActionAllowed()
|
||||||
$iUser = $oUser->GetKey();
|
$iUser = $oUser->GetKey();
|
||||||
|
|
||||||
|
// Cache user's profiles
|
||||||
|
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
|
||||||
|
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
|
||||||
|
}
|
||||||
|
|
||||||
// Note: The object set is ignored because it was interesting to optimize for huge data sets
|
// Note: The object set is ignored because it was interesting to optimize for huge data sets
|
||||||
// and acceptable to consider only the root class of the object set
|
// and acceptable to consider only the root class of the object set
|
||||||
$bStatus = null;
|
$bStatus = null;
|
||||||
// Call the API of UserRights because it caches the list for us
|
// Call the API of UserRights because it caches the list for us
|
||||||
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
|
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
|
||||||
{
|
{
|
||||||
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
|
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
|
||||||
if (!is_null($bGrant))
|
if (!is_null($bGrant))
|
||||||
@@ -814,8 +829,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find out which attribute is corresponding the the dimension 'owner org'
|
* @param string $sClass
|
||||||
* returns null if no such attribute has been found (no filtering should occur)
|
* @return string|null Find out which attribute is corresponding the dimension 'owner org'
|
||||||
|
* returns null if no such attribute has been found (no filtering should occur)
|
||||||
*/
|
*/
|
||||||
public static function GetOwnerOrganizationAttCode($sClass)
|
public static function GetOwnerOrganizationAttCode($sClass)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -604,10 +604,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
|||||||
/**
|
/**
|
||||||
* Read and cache organizations allowed to the given user
|
* Read and cache organizations allowed to the given user
|
||||||
*
|
*
|
||||||
* @param $oUser
|
* @param User $oUser
|
||||||
* @param $sClass (not used here but can be used in overloads)
|
* @param string $sClass (not used here but can be used in overloads)
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array keys of the User allowed org
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class ajax_page extends WebPage implements iTabbedPage
|
|||||||
$this->m_sReadyScript = "";
|
$this->m_sReadyScript = "";
|
||||||
//$this->add_header("Content-type: text/html; charset=utf-8");
|
//$this->add_header("Content-type: text/html; charset=utf-8");
|
||||||
$this->no_cache();
|
$this->no_cache();
|
||||||
$this->add_xframe_options();
|
$this->add_http_headers();
|
||||||
$this->m_oTabs = new TabManager();
|
$this->m_oTabs = new TabManager();
|
||||||
$this->sContentType = 'text/html';
|
$this->sContentType = 'text/html';
|
||||||
$this->sContentDisposition = 'inline';
|
$this->sContentDisposition = 'inline';
|
||||||
@@ -51,6 +51,16 @@ class ajax_page extends WebPage implements iTabbedPage
|
|||||||
utils::InitArchiveMode();
|
utils::InitArchiveMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disabling sending the header so that resource won't be blocked by CORB. See parent method documentation.
|
||||||
|
* @return void
|
||||||
|
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°4368 method creation
|
||||||
|
*/
|
||||||
|
public function add_xcontent_type_options()
|
||||||
|
{
|
||||||
|
// Nothing to do !
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
|
|||||||
@@ -1316,6 +1316,11 @@ interface iRestServiceProvider
|
|||||||
public function ExecOperation($sVersion, $sVerb, $aParams);
|
public function ExecOperation($sVersion, $sVerb, $aParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface iRestInputSanitizer
|
||||||
|
{
|
||||||
|
public function SanitizeJsonInput(string $sJsonInput): string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal REST response structure. Derive this structure to add response data and error codes.
|
* Minimal REST response structure. Derive this structure to add response data and error codes.
|
||||||
*
|
*
|
||||||
@@ -1405,6 +1410,14 @@ class RestResult
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public $message;
|
public $message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitize the content of this result to hide sensitive information
|
||||||
|
*/
|
||||||
|
public function SanitizeContent()
|
||||||
|
{
|
||||||
|
// The default implementation does nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1621,6 +1634,8 @@ class RestUtils
|
|||||||
*
|
*
|
||||||
* @return DBObject The object found
|
* @return DBObject The object found
|
||||||
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
||||||
|
*
|
||||||
|
* @see DBObject::CheckChangedExtKeysValues() generic method to check that we can access the linked object isn't used in that use case because values can be literal, OQL, friendlyname
|
||||||
*/
|
*/
|
||||||
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
|
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
|
||||||
{
|
{
|
||||||
@@ -1707,8 +1722,16 @@ class RestUtils
|
|||||||
elseif (is_string($key))
|
elseif (is_string($key))
|
||||||
{
|
{
|
||||||
// OQL
|
// OQL
|
||||||
$oSearch = DBObjectSearch::FromOQL($key);
|
try {
|
||||||
}
|
$oSearch = DBObjectSearch::FromOQL($key);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
throw new CoreOqlException('Query failed to execute', [
|
||||||
|
'query' => $key,
|
||||||
|
'exception_class' => get_class($e),
|
||||||
|
'exception_message' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception("Wrong format for key");
|
throw new Exception("Wrong format for key");
|
||||||
@@ -1857,4 +1880,28 @@ class RestUtils
|
|||||||
interface iModuleExtension
|
interface iModuleExtension
|
||||||
{
|
{
|
||||||
public function __construct();
|
public function __construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KPI logging extensibility point
|
||||||
|
*
|
||||||
|
* KPI Logger extension
|
||||||
|
*/
|
||||||
|
interface iKPILoggerExtension
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Init the statistics collected
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function InitStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new KPI to the stats
|
||||||
|
*
|
||||||
|
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKPILogData
|
||||||
|
*
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function LogOperation($oKPILogData);
|
||||||
}
|
}
|
||||||
@@ -3218,13 +3218,13 @@ EOF
|
|||||||
if ($oAttDef->GetEditClass() == 'Document')
|
if ($oAttDef->GetEditClass() == 'Document')
|
||||||
{
|
{
|
||||||
$oDocument = $this->Get($sAttCode);
|
$oDocument = $this->Get($sAttCode);
|
||||||
if (!$oDocument->IsEmpty())
|
if (is_object($oDocument) && !$oDocument->IsEmpty())
|
||||||
{
|
{
|
||||||
$sDisplayValue = $this->GetAsHTML($sAttCode);
|
$sDisplayValue = $this->GetAsHTML($sAttCode);
|
||||||
$sDisplayValue .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',
|
$sDisplayValue .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',
|
||||||
$oDocument->GetDisplayLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
$oDocument->GetDisplayLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
||||||
$sDisplayValue .= "<br/>".Dict::Format('UI:DownloadDocument_',
|
$sDisplayValue .= "<br/>".Dict::Format('UI:DownloadDocument_',
|
||||||
$oDocument->GetDownloadLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
$oDocument->GetDownloadLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -4003,7 +4003,9 @@ EOF
|
|||||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
|
$oKPI = new ExecutionKPI();
|
||||||
|
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
|
||||||
|
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
@@ -4020,13 +4022,16 @@ EOF
|
|||||||
|
|
||||||
protected function DBCloneTracked_Internal($newKey = null)
|
protected function DBCloneTracked_Internal($newKey = null)
|
||||||
{
|
{
|
||||||
$oNewObj = parent::DBCloneTracked_Internal($newKey);
|
/** @var cmdbAbstractObject $oNewObj */
|
||||||
|
$oNewObj = MetaModel::GetObject(get_class($this), parent::DBCloneTracked_Internal($newKey));
|
||||||
|
|
||||||
// Invoke extensions after insertion (the object must exist, have an id, etc.)
|
// Invoke extensions after insertion (the object must exist, have an id, etc.)
|
||||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
|
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
|
||||||
|
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $oNewObj;
|
return $oNewObj;
|
||||||
@@ -4054,7 +4059,9 @@ EOF
|
|||||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
||||||
|
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception $e)
|
catch (Exception $e)
|
||||||
@@ -4100,7 +4107,9 @@ EOF
|
|||||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
|
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
|
||||||
|
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::DBDeleteTracked_Internal($oDeletionPlan);
|
return parent::DBDeleteTracked_Internal($oDeletionPlan);
|
||||||
@@ -4118,7 +4127,10 @@ EOF
|
|||||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
if ($oExtensionInstance->OnIsModified($this))
|
$oKPI = new ExecutionKPI();
|
||||||
|
$bIsModified = $oExtensionInstance->OnIsModified($this);
|
||||||
|
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
|
||||||
|
if ($bIsModified)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -4162,7 +4174,9 @@ EOF
|
|||||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
|
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
|
||||||
|
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
|
||||||
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
|
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
|
||||||
{
|
{
|
||||||
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
|
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
|
||||||
@@ -4210,7 +4224,9 @@ EOF
|
|||||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
|
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
|
||||||
|
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
|
||||||
if (is_array($aNewIssues) && count($aNewIssues) > 0)
|
if (is_array($aNewIssues) && count($aNewIssues) > 0)
|
||||||
{
|
{
|
||||||
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
|
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
|
||||||
@@ -4722,7 +4738,7 @@ EOF
|
|||||||
$bResult = (count($aErrors) == 0);
|
$bResult = (count($aErrors) == 0);
|
||||||
if ($bResult)
|
if ($bResult)
|
||||||
{
|
{
|
||||||
list($bResult, $aErrors) = $oObj->CheckToWrite();
|
[$bResult, $aErrors] = $oObj->CheckToWrite();
|
||||||
}
|
}
|
||||||
if ($bPreview)
|
if ($bPreview)
|
||||||
{
|
{
|
||||||
@@ -4748,6 +4764,11 @@ EOF
|
|||||||
);
|
);
|
||||||
if ($bResult && (!$bPreview))
|
if ($bResult && (!$bPreview))
|
||||||
{
|
{
|
||||||
|
// doing the check will load multiple times same objects :/
|
||||||
|
// but it shouldn't cost too much on execution time
|
||||||
|
// user can mitigate by selecting less extkeys/lnk to set and/or less objects to update 🤷♂️
|
||||||
|
$oObj->CheckChangedExtKeysValues();
|
||||||
|
|
||||||
$oObj->DBUpdate();
|
$oObj->DBUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class CSVPage extends WebPage
|
|||||||
parent::__construct($s_title);
|
parent::__construct($s_title);
|
||||||
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
|
$this->add_header("Content-type: text/plain; charset=".self::PAGES_CHARSET);
|
||||||
$this->no_cache();
|
$this->no_cache();
|
||||||
$this->add_xframe_options();
|
$this->add_http_headers();
|
||||||
//$this->add_header("Content-Transfer-Encoding: binary");
|
//$this->add_header("Content-Transfer-Encoding: binary");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -529,10 +529,7 @@ EOF
|
|||||||
*/
|
*/
|
||||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
|
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
|
||||||
{
|
{
|
||||||
if (!array_key_exists('dashboard_div_id', $aExtraParams))
|
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
|
||||||
{
|
|
||||||
$aExtraParams['dashboard_div_id'] = utils::Sanitize($this->GetId(), '', 'element_identifier');
|
|
||||||
}
|
|
||||||
|
|
||||||
$oPage->add('<div class="dashboard-title-line"><div class="dashboard-title">'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</div></div>');
|
$oPage->add('<div class="dashboard-title-line"><div class="dashboard-title">'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</div></div>');
|
||||||
|
|
||||||
@@ -1002,7 +999,7 @@ EOF
|
|||||||
$sSelectorHtml .= '</div>';
|
$sSelectorHtml .= '</div>';
|
||||||
$sSelectorHtml = addslashes($sSelectorHtml);
|
$sSelectorHtml = addslashes($sSelectorHtml);
|
||||||
$sFile = addslashes($this->GetDefinitionFile());
|
$sFile = addslashes($this->GetDefinitionFile());
|
||||||
$sReloadURL = $this->GetReloadURL();
|
$sReloadURL = json_encode($this->GetReloadURL());
|
||||||
|
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
@@ -1059,7 +1056,7 @@ EOF
|
|||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
|
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
|
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
|
||||||
$sEditMenu = "<div id=\"DashboardMenu\"><ul><li><i class=\"top-right-icon icon-additional-arrow fas fa-pencil-alt\"></i><ul>";
|
$sEditMenu = "<div id=\"DashboardMenu\"><ul><li><i class=\"top-right-icon icon-additional-arrow fas fa-pencil-alt\"></i><ul>";
|
||||||
|
|
||||||
$aActions = array();
|
$aActions = array();
|
||||||
$sFile = addslashes($this->sDefinitionFile);
|
$sFile = addslashes($this->sDefinitionFile);
|
||||||
$sJSExtraParams = json_encode($aExtraParams);
|
$sJSExtraParams = json_encode($aExtraParams);
|
||||||
@@ -1091,6 +1088,7 @@ EOF
|
|||||||
|
|
||||||
EOF
|
EOF
|
||||||
);
|
);
|
||||||
|
$sReloadURL = json_encode($this->GetReloadURL());
|
||||||
$oPage->add_script(
|
$oPage->add_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
function EditDashboard(sId, sDashboardFile, aExtraParams)
|
function EditDashboard(sId, sDashboardFile, aExtraParams)
|
||||||
@@ -1188,19 +1186,19 @@ EOF
|
|||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
$oPage->add('<div id="event_bus"/>'); // For exchanging messages between the panes, same as in the designer
|
$oPage->add('<div id="event_bus"/>'); // For exchanging messages between the panes, same as in the designer
|
||||||
$oPage->add('</div>');
|
$oPage->add('</div>');
|
||||||
|
|
||||||
$sDialogTitle = Dict::S('UI:DashboardEdit:Title');
|
$sDialogTitle = Dict::S('UI:DashboardEdit:Title');
|
||||||
$sOkButtonLabel = Dict::S('UI:Button:Save');
|
$sOkButtonLabel = Dict::S('UI:Button:Save');
|
||||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||||
|
|
||||||
$sId = addslashes($this->sId);
|
$sId = json_encode($this->sId);
|
||||||
$sLayoutClass = addslashes($this->sLayoutClass);
|
$sLayoutClass = json_encode($this->sLayoutClass);
|
||||||
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
|
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
|
||||||
$sAutoReloadSec = (string) $this->iAutoReloadSec;
|
$sAutoReloadSec = (string) $this->iAutoReloadSec;
|
||||||
$sTitle = addslashes($this->sTitle);
|
$sTitle = json_encode($this->sTitle);
|
||||||
$sFile = addslashes($this->GetDefinitionFile());
|
$sFile = json_encode($this->GetDefinitionFile());
|
||||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
||||||
$sReloadURL = $this->GetReloadURL();
|
$sReloadURL = json_encode($this->GetReloadURL());
|
||||||
|
|
||||||
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
||||||
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
|
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
|
||||||
@@ -1250,15 +1248,15 @@ $('#dashboard_editor').dialog({
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('#dashboard_editor .ui-layout-center').runtimedashboard({
|
$('#dashboard_editor .ui-layout-center').runtimedashboard({
|
||||||
dashboard_id: '$sId',
|
dashboard_id: $sId,
|
||||||
layout_class: '$sLayoutClass',
|
layout_class: $sLayoutClass,
|
||||||
title: '$sTitle',
|
title: $sTitle,
|
||||||
auto_reload: $sAutoReload,
|
auto_reload: $sAutoReload,
|
||||||
auto_reload_sec: $sAutoReloadSec,
|
auto_reload_sec: $sAutoReloadSec,
|
||||||
submit_to: '$sUrl',
|
submit_to: '$sUrl',
|
||||||
submit_parameters: {operation: 'save_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
submit_parameters: {operation: 'save_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||||
render_to: '$sUrl',
|
render_to: '$sUrl',
|
||||||
render_parameters: {operation: 'render_dashboard', file: '$sFile', extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
render_parameters: {operation: 'render_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
|
||||||
new_dashlet_parameters: {operation: 'new_dashlet'}
|
new_dashlet_parameters: {operation: 'new_dashlet'}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -372,7 +372,7 @@ EOF;
|
|||||||
if (!$oPage->IsPrintableVersion())
|
if (!$oPage->IsPrintableVersion())
|
||||||
{
|
{
|
||||||
$sMenuTitle = Dict::S('UI:ConfigureThisList');
|
$sMenuTitle = Dict::S('UI:ConfigureThisList');
|
||||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
|
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li aria-label="'.Dict::S('UI:Menu:Toolkit').'"><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
|
||||||
|
|
||||||
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
|
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
|
||||||
$aActions = array(
|
$aActions = array(
|
||||||
|
|||||||
@@ -1009,6 +1009,7 @@ EOF
|
|||||||
$iTotalCount = 0;
|
$iTotalCount = 0;
|
||||||
$aValues = array();
|
$aValues = array();
|
||||||
$aURLs = array();
|
$aURLs = array();
|
||||||
|
|
||||||
foreach ($aRes as $iRow => $aRow)
|
foreach ($aRes as $iRow => $aRow)
|
||||||
{
|
{
|
||||||
$sValue = $aRow['grouped_by_1'];
|
$sValue = $aRow['grouped_by_1'];
|
||||||
@@ -1016,7 +1017,8 @@ EOF
|
|||||||
$aGroupBy[(int)$iRow] = (int) $aRow[$sFctVar];
|
$aGroupBy[(int)$iRow] = (int) $aRow[$sFctVar];
|
||||||
$iTotalCount += $aRow['_itop_count_'];
|
$iTotalCount += $aRow['_itop_count_'];
|
||||||
$aValues[] = array('label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'), 'label_html' => $sHtmlValue, 'value' => (int) $aRow[$sFctVar]);
|
$aValues[] = array('label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'), 'label_html' => $sHtmlValue, 'value' => (int) $aRow[$sFctVar]);
|
||||||
|
|
||||||
|
|
||||||
// Build the search for this subset
|
// Build the search for this subset
|
||||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||||
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
|
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
|
||||||
@@ -1030,16 +1032,23 @@ EOF
|
|||||||
{
|
{
|
||||||
case 'bars':
|
case 'bars':
|
||||||
$aNames = array();
|
$aNames = array();
|
||||||
|
$iMaxNbCharsInLabel = 0;
|
||||||
foreach($aValues as $idx => $aValue)
|
foreach($aValues as $idx => $aValue)
|
||||||
{
|
{
|
||||||
$aNames[$idx] = $aValue['label'];
|
$aNames[$idx] = $aValue['label'];
|
||||||
|
if ($iMaxNbCharsInLabel < mb_strlen($aValue['label'])) {
|
||||||
|
$iMaxNbCharsInLabel = mb_strlen($aValue['label']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$sJSNames = json_encode($aNames);
|
$sJSNames = json_encode($aNames);
|
||||||
|
|
||||||
$sJson = json_encode($aValues);
|
$sJson = json_encode($aValues);
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
|
var iChartDefaultHeight = 200,
|
||||||
|
iChartLegendHeight = 6 * $iMaxNbCharsInLabel,
|
||||||
|
iChartTotalHeight = iChartDefaultHeight + iChartLegendHeight;
|
||||||
|
$('#my_chart_$sId').height(iChartTotalHeight+ 'px');
|
||||||
var chart = c3.generate({
|
var chart = c3.generate({
|
||||||
bindto: d3.select('#my_chart_$sId'),
|
bindto: d3.select('#my_chart_$sId'),
|
||||||
data: {
|
data: {
|
||||||
@@ -1107,8 +1116,19 @@ EOF
|
|||||||
}
|
}
|
||||||
$sJSColumns = json_encode($aColumns);
|
$sJSColumns = json_encode($aColumns);
|
||||||
$sJSNames = json_encode($aNames);
|
$sJSNames = json_encode($aNames);
|
||||||
|
$iNbLinesToAddForName = 0;
|
||||||
|
if (count($aNames) > 50) {
|
||||||
|
// Calculation of the number of legends line add to the height of the graph to have a maximum of 5 legend columns
|
||||||
|
$iNbLinesIncludedInChartHeight = 10;
|
||||||
|
$iNbLinesToAddForName = ceil(count($aNames) / 5) - $iNbLinesIncludedInChartHeight;
|
||||||
|
}
|
||||||
$oPage->add_ready_script(
|
$oPage->add_ready_script(
|
||||||
<<<EOF
|
<<<EOF
|
||||||
|
// Calculate height of graph : 200px (minimum height for the chart) + 20*iNbLinesToAddForName for the legend
|
||||||
|
var iChartDefaultHeight = 200,
|
||||||
|
iChartLegendHeight = 20 * $iNbLinesToAddForName,
|
||||||
|
iChartTotalHeight = (iChartDefaultHeight + iChartLegendHeight);
|
||||||
|
$('#my_chart_$sId').height(iChartTotalHeight + 'px');
|
||||||
var chart = c3.generate({
|
var chart = c3.generate({
|
||||||
bindto: d3.select('#my_chart_$sId'),
|
bindto: d3.select('#my_chart_$sId'),
|
||||||
data: {
|
data: {
|
||||||
@@ -1915,11 +1935,13 @@ class MenuBlock extends DisplayBlock
|
|||||||
{
|
{
|
||||||
if (count($aFavoriteActions) > 0)
|
if (count($aFavoriteActions) > 0)
|
||||||
{
|
{
|
||||||
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:OtherActions')."<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
|
$sActionsMenuLabel = Dict::S('UI:Menu:OtherActions');
|
||||||
|
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li aria-label=\"{$sActionsMenuLabel}\">{$sActionsMenuLabel}<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li>".Dict::S('UI:Menu:Actions')."<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
|
$sActionsMenuLabel = Dict::S('UI:Menu:Actions');
|
||||||
|
$sHtml .= "<div class=\"itop_popup actions_menu\"><ul>\n<li aria-label=\"{$sActionsMenuLabel}\">{$sActionsMenuLabel}<i class=\"fas fa-caret-down\"></i>"."\n<ul>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$sHtml .= $oPage->RenderPopupMenuItems($aActions, $aFavoriteActions);
|
$sHtml .= $oPage->RenderPopupMenuItems($aActions, $aFavoriteActions);
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage
|
|||||||
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
|
$this->SetRootUrl(utils::GetAbsoluteUrlAppRoot());
|
||||||
$this->add_header("Content-type: text/html; charset=".self::PAGES_CHARSET);
|
$this->add_header("Content-type: text/html; charset=".self::PAGES_CHARSET);
|
||||||
$this->no_cache();
|
$this->no_cache();
|
||||||
$this->add_xframe_options();
|
$this->add_http_headers();
|
||||||
$this->add_linked_stylesheet("../css/jquery.treeview.css");
|
$this->add_linked_stylesheet("../css/jquery.treeview.css");
|
||||||
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
|
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
|
||||||
$this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css");
|
$this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css");
|
||||||
@@ -1219,7 +1219,7 @@ EOF;
|
|||||||
{
|
{
|
||||||
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
|
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
|
||||||
}
|
}
|
||||||
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><i class=\"top-right-icon icon-additional-arrow fas fa-power-off\"></i><ul>";
|
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li aria-label=\"" . Dict::S("UI:PowerMenu") . "\"><i class=\"top-right-icon icon-additional-arrow fas fa-power-off\"></i><ul>";
|
||||||
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
|
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
|
||||||
$aActions = array();
|
$aActions = array();
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class LoginBasic extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnCheckCredentials(&$iErrorCode)
|
protected function OnCheckCredentials(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'basic')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'basic')
|
||||||
{
|
{
|
||||||
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
|
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
|
||||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $_SESSION['login_mode'], 'internal'))
|
||||||
@@ -67,7 +67,7 @@ class LoginBasic extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnCredentialsOK(&$iErrorCode)
|
protected function OnCredentialsOK(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'basic')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'basic')
|
||||||
{
|
{
|
||||||
$sAuthUser = $_SESSION['auth_user'];
|
$sAuthUser = $_SESSION['auth_user'];
|
||||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
|
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
|
||||||
@@ -77,8 +77,13 @@ class LoginBasic extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnError(&$iErrorCode)
|
protected function OnError(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'basic')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'basic')
|
||||||
{
|
{
|
||||||
|
$iOnExit = LoginWebPage::getIOnExit();
|
||||||
|
if ($iOnExit === LoginWebPage::EXIT_RETURN)
|
||||||
|
{
|
||||||
|
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
||||||
|
}
|
||||||
LoginWebPage::HTTP401Error();
|
LoginWebPage::HTTP401Error();
|
||||||
}
|
}
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||||
@@ -86,7 +91,7 @@ class LoginBasic extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnConnected(&$iErrorCode)
|
protected function OnConnected(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'basic')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'basic')
|
||||||
{
|
{
|
||||||
$_SESSION['can_logoff'] = true;
|
$_SESSION['can_logoff'] = true;
|
||||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
{
|
{
|
||||||
self::ResetLoginSession();
|
self::ResetLoginSession();
|
||||||
$iOnExit = LoginWebPage::getIOnExit();
|
$iOnExit = LoginWebPage::getIOnExit();
|
||||||
if ($iOnExit == LoginWebPage::EXIT_RETURN)
|
if ($iOnExit === LoginWebPage::EXIT_RETURN)
|
||||||
{
|
{
|
||||||
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
||||||
}
|
}
|
||||||
@@ -93,6 +93,12 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
{
|
{
|
||||||
if (!isset($_SESSION['login_mode']))
|
if (!isset($_SESSION['login_mode']))
|
||||||
{
|
{
|
||||||
|
// N°6358 - if EXIT_RETURN was asked, send an error
|
||||||
|
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||||
|
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||||
|
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
// If no plugin validated the user, exit
|
// If no plugin validated the user, exit
|
||||||
self::ResetLoginSession();
|
self::ResetLoginSession();
|
||||||
exit();
|
exit();
|
||||||
@@ -111,6 +117,11 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
protected function OnConnected(&$iErrorCode)
|
protected function OnConnected(&$iErrorCode)
|
||||||
{
|
{
|
||||||
unset($_SESSION['login_temp_auth_user']);
|
unset($_SESSION['login_temp_auth_user']);
|
||||||
|
if (is_null(UserRights::GetUserObject())){
|
||||||
|
//N°7085 avoid infinite loop
|
||||||
|
IssueLog::Error("No user logged in. exit");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,4 +137,4 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class LoginExternal extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnCheckCredentials(&$iErrorCode)
|
protected function OnCheckCredentials(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'external')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'external')
|
||||||
{
|
{
|
||||||
$sAuthUser = $this->GetAuthUser();
|
$sAuthUser = $this->GetAuthUser();
|
||||||
if (!UserRights::CheckCredentials($sAuthUser, '', $_SESSION['login_mode'], 'external'))
|
if (!UserRights::CheckCredentials($sAuthUser, '', $_SESSION['login_mode'], 'external'))
|
||||||
@@ -51,7 +51,7 @@ class LoginExternal extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnCredentialsOK(&$iErrorCode)
|
protected function OnCredentialsOK(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'external')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'external')
|
||||||
{
|
{
|
||||||
$sAuthUser = $_SESSION['auth_user'];
|
$sAuthUser = $_SESSION['auth_user'];
|
||||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'external', $_SESSION['login_mode']);
|
LoginWebPage::OnLoginSuccess($sAuthUser, 'external', $_SESSION['login_mode']);
|
||||||
@@ -61,7 +61,7 @@ class LoginExternal extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnConnected(&$iErrorCode)
|
protected function OnConnected(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'external')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'external')
|
||||||
{
|
{
|
||||||
$_SESSION['can_logoff'] = false;
|
$_SESSION['can_logoff'] = false;
|
||||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
||||||
@@ -71,8 +71,13 @@ class LoginExternal extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnError(&$iErrorCode)
|
protected function OnError(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'external')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'external')
|
||||||
{
|
{
|
||||||
|
$iOnExit = LoginWebPage::getIOnExit();
|
||||||
|
if ($iOnExit === LoginWebPage::EXIT_RETURN)
|
||||||
|
{
|
||||||
|
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
||||||
|
}
|
||||||
LoginWebPage::HTTP401Error();
|
LoginWebPage::HTTP401Error();
|
||||||
}
|
}
|
||||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||||
|
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
// No credentials yet, display the form
|
// No credentials yet, display the form
|
||||||
$oPage = LoginWebPage::NewLoginWebPage();
|
$oPage = LoginWebPage::NewLoginWebPage();
|
||||||
$oPage->DisplayLoginForm($this->bForceFormOnError);
|
$oPage->DisplayLoginForm($this->bForceFormOnError);
|
||||||
@@ -62,7 +66,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
|||||||
*/
|
*/
|
||||||
protected function OnCheckCredentials(&$iErrorCode)
|
protected function OnCheckCredentials(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'form')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'form')
|
||||||
{
|
{
|
||||||
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
|
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
|
||||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
|
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
|
||||||
@@ -82,7 +86,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
|||||||
*/
|
*/
|
||||||
protected function OnCredentialsOK(&$iErrorCode)
|
protected function OnCredentialsOK(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'form')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'form')
|
||||||
{
|
{
|
||||||
$sAuthUser = $_SESSION['auth_user'];
|
$sAuthUser = $_SESSION['auth_user'];
|
||||||
// Store 'auth_user' in session for further use
|
// Store 'auth_user' in session for further use
|
||||||
@@ -96,7 +100,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
|||||||
*/
|
*/
|
||||||
protected function OnError(&$iErrorCode)
|
protected function OnError(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'form')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'form')
|
||||||
{
|
{
|
||||||
$this->bForceFormOnError = true;
|
$this->bForceFormOnError = true;
|
||||||
}
|
}
|
||||||
@@ -108,7 +112,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
|||||||
*/
|
*/
|
||||||
protected function OnConnected(&$iErrorCode)
|
protected function OnConnected(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'form')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'form')
|
||||||
{
|
{
|
||||||
$_SESSION['can_logoff'] = true;
|
$_SESSION['can_logoff'] = true;
|
||||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class LoginURL extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnReadCredentials(&$iErrorCode)
|
protected function OnReadCredentials(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'url')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'url')
|
||||||
{
|
{
|
||||||
$_SESSION['login_temp_auth_user'] = utils::ReadParam('auth_user', '', false, 'raw_data');
|
$_SESSION['login_temp_auth_user'] = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ class LoginURL extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnCheckCredentials(&$iErrorCode)
|
protected function OnCheckCredentials(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'url')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'url')
|
||||||
{
|
{
|
||||||
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
|
||||||
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
|
||||||
@@ -66,7 +66,7 @@ class LoginURL extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnCredentialsOK(&$iErrorCode)
|
protected function OnCredentialsOK(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'url')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'url')
|
||||||
{
|
{
|
||||||
$sAuthUser = $_SESSION['auth_user'];
|
$sAuthUser = $_SESSION['auth_user'];
|
||||||
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
|
LoginWebPage::OnLoginSuccess($sAuthUser, 'internal', $_SESSION['login_mode']);
|
||||||
@@ -76,7 +76,7 @@ class LoginURL extends AbstractLoginFSMExtension
|
|||||||
|
|
||||||
protected function OnError(&$iErrorCode)
|
protected function OnError(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'url')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'url')
|
||||||
{
|
{
|
||||||
$this->bErrorOccurred = true;
|
$this->bErrorOccurred = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class LoginWebPage extends NiceWebPage
|
|||||||
{
|
{
|
||||||
const EXIT_PROMPT = 0;
|
const EXIT_PROMPT = 0;
|
||||||
const EXIT_HTTP_401 = 1;
|
const EXIT_HTTP_401 = 1;
|
||||||
const EXIT_RETURN = 2;
|
const EXIT_RETURN = 2; // Non interactive mode (ajax, rest, ...)
|
||||||
|
|
||||||
const EXIT_CODE_OK = 0;
|
const EXIT_CODE_OK = 0;
|
||||||
const EXIT_CODE_MISSINGLOGIN = 1;
|
const EXIT_CODE_MISSINGLOGIN = 1;
|
||||||
@@ -85,7 +85,7 @@ class LoginWebPage extends NiceWebPage
|
|||||||
parent::__construct($sTitle);
|
parent::__construct($sTitle);
|
||||||
$this->SetStyleSheet();
|
$this->SetStyleSheet();
|
||||||
$this->no_cache();
|
$this->no_cache();
|
||||||
$this->add_xframe_options();
|
$this->add_http_headers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function SetStyleSheet()
|
public function SetStyleSheet()
|
||||||
@@ -352,14 +352,20 @@ class LoginWebPage extends NiceWebPage
|
|||||||
$this->output();
|
$this->output();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function ResetSession()
|
public static function ResetSession($bFullCleanup = false)
|
||||||
{
|
{
|
||||||
// Unset all of the session variables.
|
if ($bFullCleanup) {
|
||||||
unset($_SESSION['auth_user']);
|
// Unset all of the session variables.
|
||||||
unset($_SESSION['login_state']);
|
foreach (array_keys($_SESSION) as $sKey) {
|
||||||
unset($_SESSION['can_logoff']);
|
unset($_SESSION[$sKey]);
|
||||||
unset($_SESSION['archive_mode']);
|
}
|
||||||
unset($_SESSION['impersonate_user']);
|
} else {
|
||||||
|
unset($_SESSION['auth_user']);
|
||||||
|
unset($_SESSION['login_state']);
|
||||||
|
unset($_SESSION['can_logoff']);
|
||||||
|
unset($_SESSION['archive_mode']);
|
||||||
|
unset($_SESSION['impersonate_user']);
|
||||||
|
}
|
||||||
UserRights::_ResetSessionCache();
|
UserRights::_ResetSessionCache();
|
||||||
// If it's desired to kill the session, also delete the session cookie.
|
// If it's desired to kill the session, also delete the session cookie.
|
||||||
// Note: This will destroy the session, and not just the session data!
|
// Note: This will destroy the session, and not just the session data!
|
||||||
@@ -931,7 +937,7 @@ class LoginWebPage extends NiceWebPage
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ($iOnExit == self::EXIT_RETURN)
|
if ($iOnExit === self::EXIT_RETURN)
|
||||||
{
|
{
|
||||||
return self::EXIT_CODE_PORTALUSERNOTAUTHORIZED;
|
return self::EXIT_CODE_PORTALUSERNOTAUTHORIZED;
|
||||||
}
|
}
|
||||||
@@ -987,7 +993,7 @@ class LoginWebPage extends NiceWebPage
|
|||||||
{
|
{
|
||||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||||
{
|
{
|
||||||
if ($iOnExit == self::EXIT_RETURN)
|
if ($iOnExit === self::EXIT_RETURN)
|
||||||
{
|
{
|
||||||
return self::EXIT_CODE_MUSTBEADMIN;
|
return self::EXIT_CODE_MUSTBEADMIN;
|
||||||
}
|
}
|
||||||
@@ -1003,7 +1009,7 @@ class LoginWebPage extends NiceWebPage
|
|||||||
}
|
}
|
||||||
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
|
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
|
||||||
}
|
}
|
||||||
if ($iOnExit == self::EXIT_RETURN)
|
if ($iOnExit === self::EXIT_RETURN)
|
||||||
{
|
{
|
||||||
return $iRet;
|
return $iRet;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,4 +91,10 @@ else
|
|||||||
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
||||||
}
|
}
|
||||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
try {
|
||||||
|
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||||
|
}
|
||||||
|
catch (MySQLException $e) {
|
||||||
|
IssueLog::Debug($e->getMessage());
|
||||||
|
throw new MySQLException('Could not connect to the DB server', []);
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Combodo\iTop\Service\Module\ModuleService;
|
||||||
use ScssPhp\ScssPhp\Compiler;
|
use ScssPhp\ScssPhp\Compiler;
|
||||||
|
|
||||||
|
|
||||||
@@ -44,6 +45,74 @@ class FileUploadException extends Exception
|
|||||||
*/
|
*/
|
||||||
class utils
|
class utils
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 2.7.10 3.0.0
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_INTEGER = 'integer';
|
||||||
|
/**
|
||||||
|
* Datamodel class
|
||||||
|
* @var string
|
||||||
|
* @since 2.7.10 3.0.0
|
||||||
|
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606 update PHPDoc
|
||||||
|
* @uses MetaModel::IsValidClass()
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_CLASS = 'class';
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606
|
||||||
|
* @uses class_exists()
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_PHP_CLASS = 'php_class';
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 2.7.10 3.0.0
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_STRING = 'string';
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 2.7.10 3.0.0
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param';
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 2.7.10 3.0.0
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_PARAMETER = 'parameter';
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 2.7.10 3.0.0
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_FIELD_NAME = 'field_name';
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 2.7.10 3.0.0
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_TRANSACTION_ID = 'transaction_id';
|
||||||
|
/**
|
||||||
|
* @var string For XML / HTML node identifiers
|
||||||
|
* @since 2.7.10 3.0.0
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 2.7.10 3.0.0
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data';
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 3.0.2 3.1.0 N°4899
|
||||||
|
* @since 2.7.10 N°6606
|
||||||
|
*/
|
||||||
|
public const ENUM_SANITIZATION_FILTER_URL = 'url';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
* @since 3.0.0
|
||||||
|
* @since 2.7.10 N°6606
|
||||||
|
*/
|
||||||
|
public const DEFAULT_SANITIZATION_FILTER = self::ENUM_SANITIZATION_FILTER_RAW_DATA;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache when getting config from disk or set externally (using {@link SetConfig})
|
* Cache when getting config from disk or set externally (using {@link SetConfig})
|
||||||
* @internal
|
* @internal
|
||||||
@@ -130,16 +199,8 @@ class utils
|
|||||||
|
|
||||||
public static function IsModeCLI()
|
public static function IsModeCLI()
|
||||||
{
|
{
|
||||||
$sSAPIName = php_sapi_name();
|
$sCleanName = strtolower(trim(PHP_SAPI));
|
||||||
$sCleanName = strtolower(trim($sSAPIName));
|
return ($sCleanName === 'cli');
|
||||||
if ($sCleanName == 'cli')
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static $bPageMode = null;
|
protected static $bPageMode = null;
|
||||||
@@ -247,13 +308,13 @@ class utils
|
|||||||
}
|
}
|
||||||
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function ReadPostedParam($sName, $defaultValue = '', $sSanitizationFilter = 'parameter')
|
public static function ReadPostedParam($sName, $defaultValue = '', $sSanitizationFilter = 'parameter')
|
||||||
{
|
{
|
||||||
$retValue = isset($_POST[$sName]) ? $_POST[$sName] : $defaultValue;
|
$retValue = isset($_POST[$sName]) ? $_POST[$sName] : $defaultValue;
|
||||||
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function Sanitize($value, $defaultValue, $sSanitizationFilter)
|
public static function Sanitize($value, $defaultValue, $sSanitizationFilter)
|
||||||
{
|
{
|
||||||
if ($value === $defaultValue)
|
if ($value === $defaultValue)
|
||||||
@@ -269,13 +330,12 @@ class utils
|
|||||||
$retValue = $defaultValue;
|
$retValue = $defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $retValue;
|
return $retValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|string[] $value
|
* @param string|string[] $value
|
||||||
* @param string $sSanitizationFilter one of : integer, class, string, context_param, parameter, field_name,
|
* @param string $sSanitizationFilter one of utils::ENUM_SANITIZATION_* const
|
||||||
* element_identifier, transaction_id, parameter, raw_data
|
|
||||||
*
|
*
|
||||||
* @return string|string[]|bool boolean for :
|
* @return string|string[]|bool boolean for :
|
||||||
* * the 'class' filter (true if valid, false otherwise)
|
* * the 'class' filter (true if valid, false otherwise)
|
||||||
@@ -284,16 +344,20 @@ class utils
|
|||||||
* @since 2.5.2 2.6.0 new 'transaction_id' filter
|
* @since 2.5.2 2.6.0 new 'transaction_id' filter
|
||||||
* @since 2.7.0 new 'element_identifier' filter
|
* @since 2.7.0 new 'element_identifier' filter
|
||||||
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
|
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
|
||||||
|
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
|
||||||
|
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
|
||||||
|
*
|
||||||
|
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
|
||||||
*/
|
*/
|
||||||
protected static function Sanitize_Internal($value, $sSanitizationFilter)
|
protected static function Sanitize_Internal($value, $sSanitizationFilter)
|
||||||
{
|
{
|
||||||
switch ($sSanitizationFilter)
|
switch ($sSanitizationFilter)
|
||||||
{
|
{
|
||||||
case 'integer':
|
case static::ENUM_SANITIZATION_FILTER_INTEGER:
|
||||||
$retValue = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
|
$retValue = filter_var($value, FILTER_SANITIZE_NUMBER_INT);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'class':
|
case static::ENUM_SANITIZATION_FILTER_CLASS:
|
||||||
$retValue = $value;
|
$retValue = $value;
|
||||||
if (!MetaModel::IsValidClass($value))
|
if (!MetaModel::IsValidClass($value))
|
||||||
{
|
{
|
||||||
@@ -301,14 +365,21 @@ class utils
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'string':
|
case static::ENUM_SANITIZATION_FILTER_STRING:
|
||||||
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
|
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'context_param':
|
case static::ENUM_SANITIZATION_FILTER_PHP_CLASS:
|
||||||
case 'parameter':
|
$retValue = $value;
|
||||||
case 'field_name':
|
if (!class_exists($value)) {
|
||||||
case 'transaction_id':
|
$retValue = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
|
||||||
|
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
|
||||||
|
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
|
||||||
|
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
|
||||||
if (is_array($value))
|
if (is_array($value))
|
||||||
{
|
{
|
||||||
$retValue = array();
|
$retValue = array();
|
||||||
@@ -326,7 +397,7 @@ class utils
|
|||||||
{
|
{
|
||||||
switch ($sSanitizationFilter)
|
switch ($sSanitizationFilter)
|
||||||
{
|
{
|
||||||
case 'transaction_id':
|
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
|
||||||
// same as parameter type but keep the dot character
|
// same as parameter type but keep the dot character
|
||||||
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
|
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
|
||||||
// it must be included at the regexp beginning otherwise you'll get an invalid character error
|
// it must be included at the regexp beginning otherwise you'll get an invalid character error
|
||||||
@@ -334,18 +405,18 @@ class utils
|
|||||||
array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
|
array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'parameter':
|
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
|
||||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
|
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
|
||||||
array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
|
array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
|
||||||
// characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
|
// characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'field_name':
|
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
|
||||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
|
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
|
||||||
array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
|
array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'context_param':
|
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
|
||||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
|
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
|
||||||
array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
|
array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
|
||||||
break;
|
break;
|
||||||
@@ -355,17 +426,18 @@ class utils
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
// For XML / HTML node identifiers
|
// For XML / HTML node identifiers
|
||||||
case 'element_identifier':
|
case static::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER:
|
||||||
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
|
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// For URL
|
// For URL
|
||||||
case 'url':
|
case static::ENUM_SANITIZATION_FILTER_URL:
|
||||||
$retValue = filter_var($value, FILTER_SANITIZE_URL);
|
$retValue = filter_var($value, FILTER_SANITIZE_URL);
|
||||||
|
$retValue = filter_var($retValue, FILTER_VALIDATE_URL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
case 'raw_data':
|
case static::ENUM_SANITIZATION_FILTER_RAW_DATA:
|
||||||
$retValue = $value;
|
$retValue = $value;
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
@@ -401,11 +473,11 @@ class utils
|
|||||||
$sMimeType = self::GetFileMimeType($sTmpName);
|
$sMimeType = self::GetFileMimeType($sTmpName);
|
||||||
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
|
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UPLOAD_ERR_NO_FILE:
|
case UPLOAD_ERR_NO_FILE:
|
||||||
// no file to load, it's a normal case, just return an empty document
|
// no file to load, it's a normal case, just return an empty document
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UPLOAD_ERR_FORM_SIZE:
|
case UPLOAD_ERR_FORM_SIZE:
|
||||||
case UPLOAD_ERR_INI_SIZE:
|
case UPLOAD_ERR_INI_SIZE:
|
||||||
throw new FileUploadException(Dict::Format('UI:Error:UploadedFileTooBig', ini_get('upload_max_filesize')));
|
throw new FileUploadException(Dict::Format('UI:Error:UploadedFileTooBig', ini_get('upload_max_filesize')));
|
||||||
@@ -414,7 +486,7 @@ class utils
|
|||||||
case UPLOAD_ERR_PARTIAL:
|
case UPLOAD_ERR_PARTIAL:
|
||||||
throw new FileUploadException(Dict::S('UI:Error:UploadedFileTruncated.'));
|
throw new FileUploadException(Dict::S('UI:Error:UploadedFileTruncated.'));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UPLOAD_ERR_NO_TMP_DIR:
|
case UPLOAD_ERR_NO_TMP_DIR:
|
||||||
throw new FileUploadException(Dict::S('UI:Error:NoTmpDir'));
|
throw new FileUploadException(Dict::S('UI:Error:NoTmpDir'));
|
||||||
break;
|
break;
|
||||||
@@ -427,7 +499,7 @@ class utils
|
|||||||
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
||||||
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $sName));
|
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $sName));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $sError));
|
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $sError));
|
||||||
break;
|
break;
|
||||||
@@ -535,17 +607,17 @@ class utils
|
|||||||
|
|
||||||
return $aSelectedObj;
|
return $aSelectedObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function GetNewTransactionId()
|
public static function GetNewTransactionId()
|
||||||
{
|
{
|
||||||
return privUITransaction::GetNewTransactionId();
|
return privUITransaction::GetNewTransactionId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function IsTransactionValid($sId, $bRemoveTransaction = true)
|
public static function IsTransactionValid($sId, $bRemoveTransaction = true)
|
||||||
{
|
{
|
||||||
return privUITransaction::IsTransactionValid($sId, $bRemoveTransaction);
|
return privUITransaction::IsTransactionValid($sId, $bRemoveTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function RemoveTransaction($sId)
|
public static function RemoveTransaction($sId)
|
||||||
{
|
{
|
||||||
return privUITransaction::RemoveTransaction($sId);
|
return privUITransaction::RemoveTransaction($sId);
|
||||||
@@ -730,9 +802,9 @@ class utils
|
|||||||
$aDateTokens = array_keys($aSpec);
|
$aDateTokens = array_keys($aSpec);
|
||||||
$aDateRegexps = array_values($aSpec);
|
$aDateRegexps = array_values($aSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
$sDateRegexp = str_replace($aDateTokens, $aDateRegexps, $sFormat);
|
$sDateRegexp = str_replace($aDateTokens, $aDateRegexps, $sFormat);
|
||||||
|
|
||||||
if (preg_match('!^(?<head>)'.$sDateRegexp.'(?<tail>)$!', $sDate, $aMatches))
|
if (preg_match('!^(?<head>)'.$sDateRegexp.'(?<tail>)$!', $sDate, $aMatches))
|
||||||
{
|
{
|
||||||
$sYear = isset($aMatches['year']) ? $aMatches['year'] : 0;
|
$sYear = isset($aMatches['year']) ? $aMatches['year'] : 0;
|
||||||
@@ -749,7 +821,7 @@ class utils
|
|||||||
}
|
}
|
||||||
// http://www.spaweditor.com/scripts/regex/index.php
|
// http://www.spaweditor.com/scripts/regex/index.php
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert an old date/time format specification (using % placeholders)
|
* Convert an old date/time format specification (using % placeholders)
|
||||||
* to a format compatible with DateTime::createFromFormat
|
* to a format compatible with DateTime::createFromFormat
|
||||||
@@ -1168,7 +1240,7 @@ class utils
|
|||||||
{
|
{
|
||||||
$aArguments['param_file'] = $sParamFile;
|
$aArguments['param_file'] = $sParamFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
$aArgs = array();
|
$aArgs = array();
|
||||||
foreach($aArguments as $sName => $value)
|
foreach($aArguments as $sName => $value)
|
||||||
{
|
{
|
||||||
@@ -1177,7 +1249,7 @@ class utils
|
|||||||
$aArgs[] = "--$sName=".escapeshellarg($value);
|
$aArgs[] = "--$sName=".escapeshellarg($value);
|
||||||
}
|
}
|
||||||
$sArgs = implode(' ', $aArgs);
|
$sArgs = implode(' ', $aArgs);
|
||||||
|
|
||||||
$sScript = realpath(APPROOT.$sScriptName);
|
$sScript = realpath(APPROOT.$sScriptName);
|
||||||
if (!file_exists($sScript))
|
if (!file_exists($sScript))
|
||||||
{
|
{
|
||||||
@@ -1226,13 +1298,23 @@ class utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string A path to the folder into which data can be written
|
||||||
|
* @internal
|
||||||
|
* @since N°6097 2.7.10 3.0.4 3.1.1
|
||||||
|
*/
|
||||||
|
public static function GetDataPath(): string
|
||||||
|
{
|
||||||
|
return APPROOT.'data/';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string A path to a folder into which any module can store cache data
|
* @return string A path to a folder into which any module can store cache data
|
||||||
* The corresponding folder is created or cleaned upon code compilation
|
* The corresponding folder is created or cleaned upon code compilation
|
||||||
*/
|
*/
|
||||||
public static function GetCachePath()
|
public static function GetCachePath()
|
||||||
{
|
{
|
||||||
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
|
return static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @return string A path to a folder into which any module can store log
|
* @return string A path to a folder into which any module can store log
|
||||||
@@ -1258,7 +1340,7 @@ class utils
|
|||||||
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions, $sTableId = null, $sDataTableId = null)
|
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions, $sTableId = null, $sDataTableId = null)
|
||||||
{
|
{
|
||||||
// 1st - add standard built-in menu items
|
// 1st - add standard built-in menu items
|
||||||
//
|
//
|
||||||
switch($iMenuId)
|
switch($iMenuId)
|
||||||
{
|
{
|
||||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||||
@@ -1283,7 +1365,7 @@ class utils
|
|||||||
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
|
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO)
|
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO)
|
||||||
{
|
{
|
||||||
// Bulk export actions
|
// Bulk export actions
|
||||||
@@ -1297,7 +1379,7 @@ class utils
|
|||||||
}
|
}
|
||||||
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
|
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
|
||||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
|
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
||||||
@@ -1311,7 +1393,7 @@ class utils
|
|||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js');
|
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/tabularfieldsselector.js');
|
||||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js');
|
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.dragtable.js');
|
||||||
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css');
|
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/dragtable.css');
|
||||||
|
|
||||||
$aResult = array(
|
$aResult = array(
|
||||||
new SeparatorPopupMenuItem(),
|
new SeparatorPopupMenuItem(),
|
||||||
// Static menus: Email this page & CSV Export
|
// Static menus: Email this page & CSV Export
|
||||||
@@ -1375,7 +1457,7 @@ class utils
|
|||||||
if (is_object($oMenuItem))
|
if (is_object($oMenuItem))
|
||||||
{
|
{
|
||||||
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
|
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
|
||||||
|
|
||||||
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
|
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
|
||||||
{
|
{
|
||||||
$oPage->add_linked_script($sLinkedScript);
|
$oPage->add_linked_script($sLinkedScript);
|
||||||
@@ -1512,7 +1594,7 @@ class utils
|
|||||||
return $sProposed;
|
return $sProposed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some characters cause troubles with jQuery when used inside DOM IDs, so let's replace them by the safe _ (underscore)
|
* Some characters cause troubles with jQuery when used inside DOM IDs, so let's replace them by the safe _ (underscore)
|
||||||
* @param string $sId The ID to sanitize
|
* @param string $sId The ID to sanitize
|
||||||
@@ -1522,13 +1604,13 @@ class utils
|
|||||||
{
|
{
|
||||||
return str_replace(array(':', '[', ']', '+', '-'), '_', $sId);
|
return str_replace(array(':', '[', ']', '+', '-'), '_', $sId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to execute an HTTP POST request
|
* Helper to execute an HTTP POST request
|
||||||
* Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
|
* Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
|
||||||
* originaly named after do_post_request
|
* originaly named after do_post_request
|
||||||
* Does not require cUrl but requires openssl for performing https POSTs.
|
* Does not require cUrl but requires openssl for performing https POSTs.
|
||||||
*
|
*
|
||||||
* @param string $sUrl The URL to POST the data to
|
* @param string $sUrl The URL to POST the data to
|
||||||
* @param array $aData The data to POST as an array('param_name' => value)
|
* @param array $aData The data to POST as an array('param_name' => value)
|
||||||
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
|
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
|
||||||
@@ -1539,11 +1621,11 @@ class utils
|
|||||||
*
|
*
|
||||||
* @return string The result of the POST request
|
* @return string The result of the POST request
|
||||||
* @throws Exception with a specific error message depending on the cause
|
* @throws Exception with a specific error message depending on the cause
|
||||||
*/
|
*/
|
||||||
public static function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
|
public static function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
|
||||||
{
|
{
|
||||||
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
||||||
|
|
||||||
if (function_exists('curl_init'))
|
if (function_exists('curl_init'))
|
||||||
{
|
{
|
||||||
// If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
|
// If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
|
||||||
@@ -1577,7 +1659,7 @@ class utils
|
|||||||
CURLOPT_POSTFIELDS => http_build_query($aData),
|
CURLOPT_POSTFIELDS => http_build_query($aData),
|
||||||
CURLOPT_HTTPHEADER => $aHTTPHeaders,
|
CURLOPT_HTTPHEADER => $aHTTPHeaders,
|
||||||
);
|
);
|
||||||
|
|
||||||
$aAllOptions = $aCurlOptions + $aOptions;
|
$aAllOptions = $aCurlOptions + $aOptions;
|
||||||
$ch = curl_init($sUrl);
|
$ch = curl_init($sUrl);
|
||||||
curl_setopt_array($ch, $aAllOptions);
|
curl_setopt_array($ch, $aAllOptions);
|
||||||
@@ -1603,7 +1685,7 @@ class utils
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// cURL is not available let's try with streams and fopen...
|
// cURL is not available let's try with streams and fopen...
|
||||||
|
|
||||||
$sData = http_build_query($aData);
|
$sData = http_build_query($aData);
|
||||||
$aParams = array('http' => array(
|
$aParams = array('http' => array(
|
||||||
'method' => 'POST',
|
'method' => 'POST',
|
||||||
@@ -1615,7 +1697,7 @@ class utils
|
|||||||
$aParams['http']['header'] .= $sOptionnalHeaders;
|
$aParams['http']['header'] .= $sOptionnalHeaders;
|
||||||
}
|
}
|
||||||
$ctx = stream_context_create($aParams);
|
$ctx = stream_context_create($aParams);
|
||||||
|
|
||||||
$fp = @fopen($sUrl, 'rb', false, $ctx);
|
$fp = @fopen($sUrl, 'rb', false, $ctx);
|
||||||
if (!$fp)
|
if (!$fp)
|
||||||
{
|
{
|
||||||
@@ -1656,7 +1738,7 @@ class utils
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a standard list of character sets
|
* Get a standard list of character sets
|
||||||
*
|
*
|
||||||
* @param array $aAdditionalEncodings Additional values
|
* @param array $aAdditionalEncodings Additional values
|
||||||
* @return array of iconv code => english label, sorted by label
|
* @return array of iconv code => english label, sorted by label
|
||||||
*/
|
*/
|
||||||
@@ -1686,8 +1768,8 @@ class utils
|
|||||||
public static function HtmlEntities($sValue)
|
public static function HtmlEntities($sValue)
|
||||||
{
|
{
|
||||||
return htmlentities($sValue, ENT_QUOTES, 'UTF-8');
|
return htmlentities($sValue, ENT_QUOTES, 'UTF-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to encapsulation iTop's html_entity_decode
|
* Helper to encapsulation iTop's html_entity_decode
|
||||||
* @param string $sValue
|
* @param string $sValue
|
||||||
@@ -1716,7 +1798,7 @@ class utils
|
|||||||
return $e->getMessage();
|
return $e->getMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert (?) plain text to some HTML markup by replacing newlines by <br/> tags
|
* Convert (?) plain text to some HTML markup by replacing newlines by <br/> tags
|
||||||
* and escaping HTML entities
|
* and escaping HTML entities
|
||||||
@@ -1729,7 +1811,7 @@ class utils
|
|||||||
$sText = str_replace("\r", "\n", $sText);
|
$sText = str_replace("\r", "\n", $sText);
|
||||||
return str_replace("\n", '<br/>', htmlentities($sText, ENT_QUOTES, 'UTF-8'));
|
return str_replace("\n", '<br/>', htmlentities($sText, ENT_QUOTES, 'UTF-8'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eventually compiles the SASS (.scss) file into the CSS (.css) file
|
* Eventually compiles the SASS (.scss) file into the CSS (.css) file
|
||||||
*
|
*
|
||||||
@@ -1792,7 +1874,7 @@ class utils
|
|||||||
|
|
||||||
return $sCss;
|
return $sCss;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function GetImageSize($sImageData)
|
public static function GetImageSize($sImageData)
|
||||||
{
|
{
|
||||||
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
|
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
|
||||||
@@ -1849,7 +1931,7 @@ class utils
|
|||||||
case 'image/png':
|
case 'image/png':
|
||||||
$img = @imagecreatefromstring($oImage->GetData());
|
$img = @imagecreatefromstring($oImage->GetData());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Unsupported image type, return the image as-is
|
// Unsupported image type, return the image as-is
|
||||||
//throw new Exception("Unsupported image type: '".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
|
//throw new Exception("Unsupported image type: '".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
|
||||||
@@ -1863,14 +1945,14 @@ class utils
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Let's scale the image, preserving the transparency for GIFs and PNGs
|
// Let's scale the image, preserving the transparency for GIFs and PNGs
|
||||||
|
|
||||||
$fScale = min($iMaxImageWidth / $iWidth, $iMaxImageHeight / $iHeight);
|
$fScale = min($iMaxImageWidth / $iWidth, $iMaxImageHeight / $iHeight);
|
||||||
|
|
||||||
$iNewWidth = $iWidth * $fScale;
|
$iNewWidth = $iWidth * $fScale;
|
||||||
$iNewHeight = $iHeight * $fScale;
|
$iNewHeight = $iHeight * $fScale;
|
||||||
|
|
||||||
$new = imagecreatetruecolor($iNewWidth, $iNewHeight);
|
$new = imagecreatetruecolor($iNewWidth, $iNewHeight);
|
||||||
|
|
||||||
// Preserve transparency
|
// Preserve transparency
|
||||||
if(($oImage->GetMimeType() == "image/gif") || ($oImage->GetMimeType() == "image/png"))
|
if(($oImage->GetMimeType() == "image/gif") || ($oImage->GetMimeType() == "image/png"))
|
||||||
{
|
{
|
||||||
@@ -1878,38 +1960,38 @@ class utils
|
|||||||
imagealphablending($new, false);
|
imagealphablending($new, false);
|
||||||
imagesavealpha($new, true);
|
imagesavealpha($new, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
imagecopyresampled($new, $img, 0, 0, 0, 0, $iNewWidth, $iNewHeight, $iWidth, $iHeight);
|
imagecopyresampled($new, $img, 0, 0, 0, 0, $iNewWidth, $iNewHeight, $iWidth, $iHeight);
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
switch ($oImage->GetMimeType())
|
switch ($oImage->GetMimeType())
|
||||||
{
|
{
|
||||||
case 'image/gif':
|
case 'image/gif':
|
||||||
imagegif($new); // send image to output buffer
|
imagegif($new); // send image to output buffer
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'image/jpeg':
|
case 'image/jpeg':
|
||||||
imagejpeg($new, null, 80); // null = send image to output buffer, 80 = good quality
|
imagejpeg($new, null, 80); // null = send image to output buffer, 80 = good quality
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'image/png':
|
case 'image/png':
|
||||||
imagepng($new, null, 5); // null = send image to output buffer, 5 = medium compression
|
imagepng($new, null, 5); // null = send image to output buffer, 5 = medium compression
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
$oResampledImage = new ormDocument(ob_get_contents(), $oImage->GetMimeType(), $oImage->GetFileName());
|
$oResampledImage = new ormDocument(ob_get_contents(), $oImage->GetMimeType(), $oImage->GetFileName());
|
||||||
@ob_end_clean();
|
@ob_end_clean();
|
||||||
|
|
||||||
imagedestroy($img);
|
imagedestroy($img);
|
||||||
imagedestroy($new);
|
imagedestroy($new);
|
||||||
|
|
||||||
return $oResampledImage;
|
return $oResampledImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a 128 bit UUID in the format: {########-####-####-####-############}
|
* Create a 128 bit UUID in the format: {########-####-####-####-############}
|
||||||
*
|
*
|
||||||
* Note: this method can be run from the command line as well as from the web server.
|
* Note: this method can be run from the command line as well as from the web server.
|
||||||
* Note2: this method is not cryptographically secure! If you need a cryptographically secure value
|
* Note2: this method is not cryptographically secure! If you need a cryptographically secure value
|
||||||
* consider using open_ssl or PHP 7 methods.
|
* consider using open_ssl or PHP 7 methods.
|
||||||
@@ -1945,26 +2027,9 @@ class utils
|
|||||||
*/
|
*/
|
||||||
public static function GetCurrentModuleName($iCallDepth = 0)
|
public static function GetCurrentModuleName($iCallDepth = 0)
|
||||||
{
|
{
|
||||||
$sCurrentModuleName = '';
|
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
|
||||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
|
||||||
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
|
|
||||||
|
|
||||||
foreach(GetModulesInfo() as $sModuleName => $aInfo)
|
|
||||||
{
|
|
||||||
if ($aInfo['root_dir'] !== '')
|
|
||||||
{
|
|
||||||
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
|
|
||||||
|
|
||||||
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
|
|
||||||
{
|
|
||||||
$sCurrentModuleName = $sModuleName;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sCurrentModuleName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
|
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
|
||||||
*
|
*
|
||||||
@@ -1978,24 +2043,7 @@ class utils
|
|||||||
*/
|
*/
|
||||||
public static function GetCurrentModuleDir($iCallDepth)
|
public static function GetCurrentModuleDir($iCallDepth)
|
||||||
{
|
{
|
||||||
$sCurrentModuleDir = '';
|
return ModuleService::GetInstance()->GetCurrentModuleDir($iCallDepth);
|
||||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
|
||||||
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
|
|
||||||
|
|
||||||
foreach(GetModulesInfo() as $sModuleName => $aInfo)
|
|
||||||
{
|
|
||||||
if ($aInfo['root_dir'] !== '')
|
|
||||||
{
|
|
||||||
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
|
|
||||||
|
|
||||||
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
|
|
||||||
{
|
|
||||||
$sCurrentModuleDir = basename($sRootDir);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sCurrentModuleDir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2010,14 +2058,9 @@ class utils
|
|||||||
*/
|
*/
|
||||||
public static function GetCurrentModuleUrl()
|
public static function GetCurrentModuleUrl()
|
||||||
{
|
{
|
||||||
$sDir = static::GetCurrentModuleDir(1);
|
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
|
||||||
if ( $sDir !== '')
|
|
||||||
{
|
|
||||||
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sProperty The name of the property to retrieve
|
* @param string $sProperty The name of the property to retrieve
|
||||||
* @param mixed $defaultvalue
|
* @param mixed $defaultvalue
|
||||||
@@ -2025,24 +2068,18 @@ class utils
|
|||||||
*/
|
*/
|
||||||
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
||||||
{
|
{
|
||||||
$sModuleName = static::GetCurrentModuleName(1);
|
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
|
||||||
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sModuleName
|
* @param string $sModuleName
|
||||||
* @return string|NULL compiled version of a given module, as it was seen by the compiler
|
* @return string|NULL compiled version of a given module, as it was seen by the compiler
|
||||||
*/
|
*/
|
||||||
public static function GetCompiledModuleVersion($sModuleName)
|
public static function GetCompiledModuleVersion($sModuleName)
|
||||||
{
|
{
|
||||||
$aModulesInfo = GetModulesInfo();
|
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
|
||||||
if (array_key_exists($sModuleName, $aModulesInfo))
|
|
||||||
{
|
|
||||||
return $aModulesInfo[$sModuleName]['version'];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the given path/url is an http(s) URL
|
* Check if the given path/url is an http(s) URL
|
||||||
* @param string $sPath
|
* @param string $sPath
|
||||||
@@ -2057,7 +2094,7 @@ class utils
|
|||||||
}
|
}
|
||||||
return $bRet;
|
return $bRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the given URL is a link to download a document/image on the CURRENT iTop
|
* Check if the given URL is a link to download a document/image on the CURRENT iTop
|
||||||
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
|
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
|
||||||
@@ -2106,7 +2143,7 @@ class utils
|
|||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the content of a file (and retrieve its MIME type) from either:
|
* Read the content of a file (and retrieve its MIME type) from either:
|
||||||
* - an URL pointing to a blob (image/document) on the current iTop server
|
* - an URL pointing to a blob (image/document) on the current iTop server
|
||||||
@@ -2150,7 +2187,7 @@ class utils
|
|||||||
'html' => 'text/html',
|
'html' => 'text/html',
|
||||||
'exe' => 'application/octet-stream',
|
'exe' => 'application/octet-stream',
|
||||||
);
|
);
|
||||||
|
|
||||||
$sData = null;
|
$sData = null;
|
||||||
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
|
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
|
||||||
$sFileName = 'uploaded-file'; // Default name for downloaded-files
|
$sFileName = 'uploaded-file'; // Default name for downloaded-files
|
||||||
@@ -2206,7 +2243,7 @@ class utils
|
|||||||
}
|
}
|
||||||
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
|
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
|
||||||
$sFileName = basename($sPath);
|
$sFileName = basename($sPath);
|
||||||
|
|
||||||
if (array_key_exists($sExtension, $aKnownExtensions))
|
if (array_key_exists($sExtension, $aKnownExtensions))
|
||||||
{
|
{
|
||||||
$sMimeType = $aKnownExtensions[$sExtension];
|
$sMimeType = $aKnownExtensions[$sExtension];
|
||||||
@@ -2220,7 +2257,7 @@ class utils
|
|||||||
}
|
}
|
||||||
return $oUploadedDoc;
|
return $oUploadedDoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function ParseHeaders($aHeaders)
|
protected static function ParseHeaders($aHeaders)
|
||||||
{
|
{
|
||||||
$aCleanHeaders = array();
|
$aCleanHeaders = array();
|
||||||
@@ -2245,7 +2282,7 @@ class utils
|
|||||||
}
|
}
|
||||||
return $aCleanHeaders;
|
return $aCleanHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string a string based on compilation time or (if not available because the datamodel has not been loaded)
|
* @return string a string based on compilation time or (if not available because the datamodel has not been loaded)
|
||||||
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
|
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
|
||||||
@@ -2295,6 +2332,38 @@ class utils
|
|||||||
return in_array($sClass, $aHugeClasses);
|
return in_array($sClass, $aHugeClasses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper around the native strlen() PHP method to test a string for null or empty value
|
||||||
|
*
|
||||||
|
* @link https://www.php.net/releases/8.1/en.php#deprecations_and_bc_breaks "Passing null to non-nullable internal function parameters is deprecated"
|
||||||
|
*
|
||||||
|
* @param string|null $sString
|
||||||
|
*
|
||||||
|
* @return bool if string null or empty
|
||||||
|
* @since 3.0.2 N°5302
|
||||||
|
* @since 2.7.10 N°6458 add method in the 2.7 branch
|
||||||
|
*/
|
||||||
|
public static function IsNullOrEmptyString(?string $sString): bool
|
||||||
|
{
|
||||||
|
return $sString === null || strlen($sString) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper around the native strlen() PHP method to test a string not null or empty value
|
||||||
|
*
|
||||||
|
* @link https://www.php.net/releases/8.1/en.php#deprecations_and_bc_breaks "Passing null to non-nullable internal function parameters is deprecated"
|
||||||
|
*
|
||||||
|
* @param string|null $sString
|
||||||
|
*
|
||||||
|
* @return bool if string is not null and not empty
|
||||||
|
* @since 3.0.2 N°5302
|
||||||
|
* @since 2.7.10 N°6458 add method in the 2.7 branch
|
||||||
|
*/
|
||||||
|
public static function IsNotNullOrEmptyString(?string $sString): bool
|
||||||
|
{
|
||||||
|
return !static::IsNullOrEmptyString($sString);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if iTop is in a development environment (VCS vs build number)
|
* Check if iTop is in a development environment (VCS vs build number)
|
||||||
*
|
*
|
||||||
@@ -2368,7 +2437,7 @@ class utils
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
|
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2504,4 +2573,5 @@ class utils
|
|||||||
return (substr(PHP_OS,0,3) === 'WIN');
|
return (substr(PHP_OS,0,3) === 'WIN');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -483,12 +483,24 @@ class WebPage implements Page
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string|null $sHeaderValue for example `SAMESITE`. If null will set the header using the config parameter value.
|
* @param string|null $sXFrameOptionsHeaderValue passed to {@see add_xframe_options}
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°4368 method creation, replace {@see add_xframe_options} consumers call
|
||||||
|
*/
|
||||||
|
public function add_http_headers($sXFrameOptionsHeaderValue = null)
|
||||||
|
{
|
||||||
|
$this->add_xframe_options($sXFrameOptionsHeaderValue);
|
||||||
|
$this->add_xcontent_type_options();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|null $sHeaderValue for example `SAMESITE`. If null will set the header using the `security_header_xframe` config parameter value.
|
||||||
*
|
*
|
||||||
* @since 2.7.3 3.0.0 N°3416
|
* @since 2.7.3 3.0.0 N°3416
|
||||||
* @uses security_header_xframe config parameter
|
|
||||||
* @uses \utils::GetConfig()
|
* @uses \utils::GetConfig()
|
||||||
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
|
*
|
||||||
|
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options HTTP header MDN documentation
|
||||||
*/
|
*/
|
||||||
public function add_xframe_options($sHeaderValue = null)
|
public function add_xframe_options($sHeaderValue = null)
|
||||||
{
|
{
|
||||||
@@ -499,6 +511,38 @@ class WebPage implements Page
|
|||||||
$this->add_header('X-Frame-Options: '.$sHeaderValue);
|
$this->add_header('X-Frame-Options: '.$sHeaderValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warning : this header will trigger the Cross-Origin Read Blocking (CORB) protection for some mime types (HTML, XML except SVG, JSON, text/plain)
|
||||||
|
* In consequence some children pages will override this method.
|
||||||
|
*
|
||||||
|
* Sending header can be disabled globally using the `security.enable_header_xcontent_type_options` optional config parameter.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°4368 method creation
|
||||||
|
*
|
||||||
|
* @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options HTTP header MDN documentation
|
||||||
|
* @link https://chromium.googlesource.com/chromium/src/+/master/services/network/cross_origin_read_blocking_explainer.md#determining-whether-a-response-is-corb_protected "Determining whether a response is CORB-protected"
|
||||||
|
*/
|
||||||
|
public function add_xcontent_type_options()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$oConfig = utils::GetConfig();
|
||||||
|
} catch (ConfigException|CoreException $e) {
|
||||||
|
$oConfig = null;
|
||||||
|
}
|
||||||
|
if (is_null($oConfig)) {
|
||||||
|
$bSendXContentTypeOptionsHttpHeader = true;
|
||||||
|
} else {
|
||||||
|
$bSendXContentTypeOptionsHttpHeader = $oConfig->Get('security.enable_header_xcontent_type_options');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($bSendXContentTypeOptionsHttpHeader === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->add_header('X-Content-Type-Options: nosniff');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add needed headers to the page so that it will no be cached
|
* Add needed headers to the page so that it will no be cached
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -44,10 +44,20 @@ class XMLPage extends WebPage
|
|||||||
$this->m_bHeaderSent = false;
|
$this->m_bHeaderSent = false;
|
||||||
$this->add_header("Content-type: text/xml; charset=".self::PAGES_CHARSET);
|
$this->add_header("Content-type: text/xml; charset=".self::PAGES_CHARSET);
|
||||||
$this->no_cache();
|
$this->no_cache();
|
||||||
$this->add_xframe_options();
|
$this->add_http_headers();
|
||||||
$this->add_header("Content-location: export.xml");
|
$this->add_header("Content-location: export.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disabling sending the header so that resource won't be blocked by CORB. See parent method documentation.
|
||||||
|
* @return void
|
||||||
|
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°4368 method creation
|
||||||
|
*/
|
||||||
|
public function add_xcontent_type_options()
|
||||||
|
{
|
||||||
|
// Nothing to do !
|
||||||
|
}
|
||||||
|
|
||||||
public function output()
|
public function output()
|
||||||
{
|
{
|
||||||
if (!$this->m_bPassThrough)
|
if (!$this->m_bPassThrough)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ define('APPCONF', APPROOT.'conf/');
|
|||||||
* @used-by utils::GetItopVersionWikiSyntax()
|
* @used-by utils::GetItopVersionWikiSyntax()
|
||||||
* @used-by iTopModulesPhpVersionIntegrationTest
|
* @used-by iTopModulesPhpVersionIntegrationTest
|
||||||
*/
|
*/
|
||||||
define('ITOP_CORE_VERSION', '2.7.9');
|
define('ITOP_CORE_VERSION', '2.7.12');
|
||||||
|
|
||||||
|
|
||||||
require_once APPROOT.'bootstrap.inc.php';
|
require_once APPROOT.'bootstrap.inc.php';
|
||||||
|
|||||||
@@ -22,14 +22,8 @@ define('ITOP_DEFAULT_ENV', 'production');
|
|||||||
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
|
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
|
||||||
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
|
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
|
||||||
|
|
||||||
if (function_exists('microtime'))
|
$fItopStarted = microtime(true);
|
||||||
{
|
$iItopInitialMemory = memory_get_usage(true);
|
||||||
$fItopStarted = microtime(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$fItopStarted = 1000 * time();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false)
|
if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false)
|
||||||
{
|
{
|
||||||
@@ -54,7 +48,7 @@ if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
|
|||||||
http_response_code(503);
|
http_response_code(503);
|
||||||
// Display message depending on the request
|
// Display message depending on the request
|
||||||
include(APPROOT.'application/maintenancemsg.php');
|
include(APPROOT.'application/maintenancemsg.php');
|
||||||
$sSAPIName = strtoupper(trim(php_sapi_name()));
|
$sSAPIName = strtoupper(trim(PHP_SAPI));
|
||||||
|
|
||||||
switch (true)
|
switch (true)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
"combodo/tcpdf": "~6.4.4",
|
"combodo/tcpdf": "~6.4.4",
|
||||||
"firebase/php-jwt": "~6.4.0",
|
"firebase/php-jwt": "~6.4.0",
|
||||||
"guzzlehttp/guzzle": "^6.5.8",
|
"guzzlehttp/guzzle": "^6.5.8",
|
||||||
|
"guzzlehttp/psr7": "~1.9.1",
|
||||||
"laminas/laminas-mail": "^2.11",
|
"laminas/laminas-mail": "^2.11",
|
||||||
"laminas/laminas-servicemanager": "^3.5",
|
"laminas/laminas-servicemanager": "^3.5",
|
||||||
"league/oauth2-google": "^3.0",
|
"league/oauth2-google": "^3.0",
|
||||||
@@ -61,6 +62,7 @@
|
|||||||
"sources/application",
|
"sources/application",
|
||||||
"sources/Composer",
|
"sources/Composer",
|
||||||
"sources/Controller",
|
"sources/Controller",
|
||||||
|
"sources/Service",
|
||||||
"sources/Core"
|
"sources/Core"
|
||||||
],
|
],
|
||||||
"exclude-from-classmap": [
|
"exclude-from-classmap": [
|
||||||
|
|||||||
21
composer.lock
generated
21
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": "e5c0746c3d1bb9df9151c910e8f50955",
|
"content-hash": "abee0f7bd244530a88b08e1e338b0ec5",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "combodo/tcpdf",
|
"name": "combodo/tcpdf",
|
||||||
@@ -516,16 +516,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/psr7",
|
"name": "guzzlehttp/psr7",
|
||||||
"version": "1.9.0",
|
"version": "1.9.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/guzzle/psr7.git",
|
"url": "https://github.com/guzzle/psr7.git",
|
||||||
"reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318"
|
"reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/e98e3e6d4f86621a9b75f623996e6bbdeb4b9318",
|
"url": "https://api.github.com/repos/guzzle/psr7/zipball/e4490cabc77465aaee90b20cfc9a770f8c04be6b",
|
||||||
"reference": "e98e3e6d4f86621a9b75f623996e6bbdeb4b9318",
|
"reference": "e4490cabc77465aaee90b20cfc9a770f8c04be6b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -544,11 +544,6 @@
|
|||||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.9-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"files": [
|
"files": [
|
||||||
"src/functions_include.php"
|
"src/functions_include.php"
|
||||||
@@ -606,7 +601,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/guzzle/psr7/issues",
|
"issues": "https://github.com/guzzle/psr7/issues",
|
||||||
"source": "https://github.com/guzzle/psr7/tree/1.9.0"
|
"source": "https://github.com/guzzle/psr7/tree/1.9.1"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -622,7 +617,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-06-20T21:43:03+00:00"
|
"time": "2023-04-17T16:00:37+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laminas/laminas-loader",
|
"name": "laminas/laminas-loader",
|
||||||
@@ -4757,5 +4752,5 @@
|
|||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "7.1.3"
|
"php": "7.1.3"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.1.0"
|
"plugin-api-version": "2.3.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,8 +67,7 @@ class MyHelpers
|
|||||||
// format sss.mmmuuupppnnn
|
// format sss.mmmuuupppnnn
|
||||||
public static function getmicrotime()
|
public static function getmicrotime()
|
||||||
{
|
{
|
||||||
list($usec, $sec) = explode(" ",microtime());
|
return microtime(true);
|
||||||
return ((float)$usec + (float)$sec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -420,6 +419,7 @@ class MyHelpers
|
|||||||
//}
|
//}
|
||||||
return $sOutput;
|
return $sOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -524,5 +524,3 @@ class Str
|
|||||||
return (strtolower($sString) == $sString);
|
return (strtolower($sString) == $sString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ abstract class AttributeDefinition
|
|||||||
|
|
||||||
protected $aCSSClasses;
|
protected $aCSSClasses;
|
||||||
|
|
||||||
public function GetType()
|
public function GetType()
|
||||||
{
|
{
|
||||||
return Dict::S('Core:'.get_class($this));
|
return Dict::S('Core:'.get_class($this));
|
||||||
}
|
}
|
||||||
@@ -2695,6 +2695,11 @@ class AttributeObjectKey extends AttributeDBFieldVoid
|
|||||||
return ($proposedValue == 0);
|
return ($proposedValue == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param int|DBObject $proposedValue Object key or valid ({@see MetaModel::IsValidObject()}) datamodel object
|
||||||
|
*/
|
||||||
public function MakeRealValue($proposedValue, $oHostObj)
|
public function MakeRealValue($proposedValue, $oHostObj)
|
||||||
{
|
{
|
||||||
if (is_null($proposedValue))
|
if (is_null($proposedValue))
|
||||||
@@ -2707,7 +2712,6 @@ class AttributeObjectKey extends AttributeDBFieldVoid
|
|||||||
}
|
}
|
||||||
if (MetaModel::IsValidObject($proposedValue))
|
if (MetaModel::IsValidObject($proposedValue))
|
||||||
{
|
{
|
||||||
/** @var \DBObject $proposedValue */
|
|
||||||
return $proposedValue->GetKey();
|
return $proposedValue->GetKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3771,7 +3775,7 @@ class AttributeFinalClass extends AttributeString
|
|||||||
*/
|
*/
|
||||||
class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||||
{
|
{
|
||||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||||
@@ -3847,7 +3851,7 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
|||||||
*/
|
*/
|
||||||
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
|
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
|
||||||
{
|
{
|
||||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||||
|
|
||||||
static $sKey = null; // Encryption key used for all encrypted fields
|
static $sKey = null; // Encryption key used for all encrypted fields
|
||||||
static $sLibrary = null; // Encryption library used for all encrypted fields
|
static $sLibrary = null; // Encryption library used for all encrypted fields
|
||||||
@@ -5940,6 +5944,11 @@ class AttributeDateTime extends AttributeDBField
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*
|
||||||
|
* @param int|string $proposedValue timestamp ({@see DateTime::getTimestamp()) or date as string, following the {@see GetInternalFormat} format.
|
||||||
|
*/
|
||||||
public function MakeRealValue($proposedValue, $oHostObj)
|
public function MakeRealValue($proposedValue, $oHostObj)
|
||||||
{
|
{
|
||||||
if (is_null($proposedValue))
|
if (is_null($proposedValue))
|
||||||
@@ -7655,9 +7664,9 @@ class AttributeBlob extends AttributeDefinition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Users can provide the document from an URL (including an URL on iTop itself)
|
* {@inheritDoc}
|
||||||
* for CSV import. Administrators can even provide the path to a local file
|
*
|
||||||
* {@inheritDoc}
|
* @param string $proposedValue Can be an URL (including an URL to iTop itself), or a local path (CSV import)
|
||||||
*
|
*
|
||||||
* @see AttributeDefinition::MakeRealValue()
|
* @see AttributeDefinition::MakeRealValue()
|
||||||
*/
|
*/
|
||||||
@@ -9234,7 +9243,7 @@ class AttributeSubItem extends AttributeDefinition
|
|||||||
*/
|
*/
|
||||||
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy
|
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy
|
||||||
{
|
{
|
||||||
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
|
||||||
|
|||||||
@@ -149,7 +149,9 @@ abstract class BulkExport
|
|||||||
$this->oSearch = null;
|
$this->oSearch = null;
|
||||||
$this->iChunkSize = 0;
|
$this->iChunkSize = 0;
|
||||||
$this->sFormatCode = null;
|
$this->sFormatCode = null;
|
||||||
$this->aStatusInfo = array();
|
$this->aStatusInfo = [
|
||||||
|
'show_obsolete_data' => utils::ShowObsoleteData(),
|
||||||
|
];
|
||||||
$this->oBulkExportResult = null;
|
$this->oBulkExportResult = null;
|
||||||
$this->sTmpFile = '';
|
$this->sTmpFile = '';
|
||||||
$this->bLocalizeOutput = false;
|
$this->bLocalizeOutput = false;
|
||||||
@@ -203,15 +205,17 @@ abstract class BulkExport
|
|||||||
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
|
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
|
||||||
{
|
{
|
||||||
$sFormatCode = $oInfo->Get('format');
|
$sFormatCode = $oInfo->Get('format');
|
||||||
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
|
$aStatusInfo = json_decode($oInfo->Get('status_info'),true);
|
||||||
|
|
||||||
|
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
|
||||||
|
$oSearch->SetShowObsoleteData($aStatusInfo['show_obsolete_data']);
|
||||||
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
|
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
|
||||||
if ($oBulkExporter)
|
if ($oBulkExporter)
|
||||||
{
|
{
|
||||||
$oBulkExporter->SetFormat($sFormatCode);
|
$oBulkExporter->SetFormat($sFormatCode);
|
||||||
$oBulkExporter->SetObjectList($oSearch);
|
$oBulkExporter->SetObjectList($oSearch);
|
||||||
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
|
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
|
||||||
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
|
$oBulkExporter->SetStatusInfo($aStatusInfo);
|
||||||
|
|
||||||
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
|
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
|
||||||
|
|
||||||
@@ -289,6 +293,7 @@ abstract class BulkExport
|
|||||||
*/
|
*/
|
||||||
public function SetObjectList(DBSearch $oSearch)
|
public function SetObjectList(DBSearch $oSearch)
|
||||||
{
|
{
|
||||||
|
$oSearch->SetShowObsoleteData($this->aStatusInfo['show_obsolete_data']);
|
||||||
$this->oSearch = $oSearch;
|
$this->oSearch = $oSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class MySQLHasGoneAwayException extends MySQLException
|
|||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
2006,
|
2006,
|
||||||
2013
|
2013,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +134,12 @@ class CMDBSource
|
|||||||
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
|
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
|
||||||
const ENUM_DB_VENDOR_PERCONA = 'Percona';
|
const ENUM_DB_VENDOR_PERCONA = 'Percona';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.7.10 3.0.4 3.1.2 3.0.2 N°6889 constant creation
|
||||||
|
* @internal will be removed in a future version
|
||||||
|
*/
|
||||||
|
const MYSQL_DEFAULT_PORT = 3306;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
|
* Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
|
||||||
* Message: Lock wait timeout exceeded; try restarting transaction
|
* Message: Lock wait timeout exceeded; try restarting transaction
|
||||||
@@ -319,16 +325,19 @@ class CMDBSource
|
|||||||
/**
|
/**
|
||||||
* @param string $sDbHost initial value ("p:domain:port" syntax)
|
* @param string $sDbHost initial value ("p:domain:port" syntax)
|
||||||
* @param string $sServer server variable to update
|
* @param string $sServer server variable to update
|
||||||
* @param int $iPort port variable to update
|
* @param int|null $iPort port variable to update, will return null if nothing is specified in $sDbHost
|
||||||
|
*
|
||||||
|
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°6889 will return null in $iPort if port isn't present in $sDbHost. Use {@see MYSQL_DEFAULT_PORT} if needed
|
||||||
|
*
|
||||||
|
* @link http://php.net/manual/en/mysqli.persistconns.php documentation for the "p:" prefix (persistent connexion)
|
||||||
*/
|
*/
|
||||||
public static function InitServerAndPort($sDbHost, &$sServer, &$iPort)
|
public static function InitServerAndPort($sDbHost, &$sServer, &$iPort)
|
||||||
{
|
{
|
||||||
$aConnectInfo = explode(':', $sDbHost);
|
$aConnectInfo = explode(':', $sDbHost);
|
||||||
|
|
||||||
$bUsePersistentConnection = false;
|
$bUsePersistentConnection = false;
|
||||||
if (strcasecmp($aConnectInfo[0], 'p') == 0)
|
if (strcasecmp($aConnectInfo[0], 'p') === 0)
|
||||||
{
|
{
|
||||||
// we might have "p:" prefix to use persistent connections (see http://php.net/manual/en/mysqli.persistconns.php)
|
|
||||||
$bUsePersistentConnection = true;
|
$bUsePersistentConnection = true;
|
||||||
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
|
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
|
||||||
}
|
}
|
||||||
@@ -346,10 +355,6 @@ class CMDBSource
|
|||||||
{
|
{
|
||||||
$iPort = (int)($aConnectInfo[1]);
|
$iPort = (int)($aConnectInfo[1]);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
$iPort = 3306;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -535,6 +540,7 @@ class CMDBSource
|
|||||||
{
|
{
|
||||||
self::$m_sDBName = '';
|
self::$m_sDBName = '';
|
||||||
}
|
}
|
||||||
|
self::_TablesInfoCacheReset(); // reset the table info cache!
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function CreateTable($sQuery)
|
public static function CreateTable($sQuery)
|
||||||
@@ -668,10 +674,9 @@ class CMDBSource
|
|||||||
/**
|
/**
|
||||||
* @param string $sSQLQuery
|
* @param string $sSQLQuery
|
||||||
*
|
*
|
||||||
* @return \mysqli_result|null
|
* @return mysqli_result|null
|
||||||
* @throws \MySQLException
|
* @throws MySQLException
|
||||||
* @throws \MySQLHasGoneAwayException
|
* @throws MySQLHasGoneAwayException
|
||||||
* @throws \CoreException
|
|
||||||
*
|
*
|
||||||
* @since 2.7.0 N°679 handles nested transactions
|
* @since 2.7.0 N°679 handles nested transactions
|
||||||
*/
|
*/
|
||||||
@@ -731,8 +736,9 @@ class CMDBSource
|
|||||||
{
|
{
|
||||||
self::LogDeadLock($e);
|
self::LogDeadLock($e);
|
||||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||||
}
|
} finally {
|
||||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||||
|
}
|
||||||
if ($oResult === false)
|
if ($oResult === false)
|
||||||
{
|
{
|
||||||
$aContext = array('query' => $sSql);
|
$aContext = array('query' => $sSql);
|
||||||
@@ -1290,8 +1296,8 @@ class CMDBSource
|
|||||||
*/
|
*/
|
||||||
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
|
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
|
||||||
{
|
{
|
||||||
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
[$sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||||
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
[$sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||||
|
|
||||||
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
|
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
|
||||||
{
|
{
|
||||||
@@ -1728,8 +1734,20 @@ class CMDBSource
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function GetClusterNb()
|
||||||
* @return string query to upgrade database charset and collation if needed, null if not
|
{
|
||||||
|
$result = 0;
|
||||||
|
$sSql = "SHOW STATUS LIKE 'wsrep_cluster_size';";
|
||||||
|
$aRows = self::QueryToArray($sSql);
|
||||||
|
if (count($aRows) > 0)
|
||||||
|
{
|
||||||
|
$result = $aRows[0]['Value'];
|
||||||
|
}
|
||||||
|
return intval($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string query to upgrade database charset and collation if needed, null if not
|
||||||
* @throws \MySQLException
|
* @throws \MySQLException
|
||||||
*
|
*
|
||||||
* @since 2.5.0 N°1001 switch to utf8mb4
|
* @since 2.5.0 N°1001 switch to utf8mb4
|
||||||
|
|||||||
@@ -963,6 +963,14 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
),
|
),
|
||||||
|
'log_kpi_generate_legacy_report' => array(
|
||||||
|
'type' => 'bool',
|
||||||
|
'description' => 'Generate the legacy KPI report (kpi.html)',
|
||||||
|
'default' => true,
|
||||||
|
'value' => '',
|
||||||
|
'source_of_value' => '',
|
||||||
|
'show_in_conf_sample' => false,
|
||||||
|
),
|
||||||
'max_linkset_output' => array(
|
'max_linkset_output' => array(
|
||||||
'type' => 'integer',
|
'type' => 'integer',
|
||||||
'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',
|
'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',
|
||||||
@@ -1312,6 +1320,14 @@ class Config
|
|||||||
'source_of_value' => '',
|
'source_of_value' => '',
|
||||||
'show_in_conf_sample' => false,
|
'show_in_conf_sample' => false,
|
||||||
],
|
],
|
||||||
|
'security.enable_header_xcontent_type_options' => [
|
||||||
|
'type' => 'bool',
|
||||||
|
'description' => 'If set to false, iTop will stop sending the X-Content-Type-Options HTTP header. This header could trigger CORB protection on certain resources (JSON, XML, HTML, text) therefore blocking them.',
|
||||||
|
'default' => true,
|
||||||
|
'value' => '',
|
||||||
|
'source_of_value' => '',
|
||||||
|
'show_in_conf_sample' => false,
|
||||||
|
],
|
||||||
'behind_reverse_proxy' => [
|
'behind_reverse_proxy' => [
|
||||||
'type' => 'bool',
|
'type' => 'bool',
|
||||||
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',
|
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',
|
||||||
@@ -1618,7 +1634,7 @@ class Config
|
|||||||
}
|
}
|
||||||
if (strlen($sNoise) > 0)
|
if (strlen($sNoise) > 0)
|
||||||
{
|
{
|
||||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||||
throw new ConfigException('Syntax error in configuration file',
|
throw new ConfigException('Syntax error in configuration file',
|
||||||
array('file' => $sConfigFile, 'error' => '<tt>'.htmlentities($sNoise, ENT_QUOTES, 'UTF-8').'</tt>'));
|
array('file' => $sConfigFile, 'error' => '<tt>'.htmlentities($sNoise, ENT_QUOTES, 'UTF-8').'</tt>'));
|
||||||
}
|
}
|
||||||
@@ -1893,6 +1909,24 @@ class Config
|
|||||||
$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
|
$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.7.11 N°7085
|
||||||
|
* Add login mode if not configured already
|
||||||
|
* @param string $sLoginMode
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function AddAllowedLoginTypes($sLoginMode)
|
||||||
|
{
|
||||||
|
$aAllowedLoginTypes = $this->GetAllowedLoginTypes();
|
||||||
|
if (in_array($sLoginMode, $aAllowedLoginTypes)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$aAllowedLoginTypes[] = $sLoginMode;
|
||||||
|
$this->SetAllowedLoginTypes($aAllowedLoginTypes);
|
||||||
|
}
|
||||||
|
|
||||||
public function SetExternalAuthenticationVariable($sExtAuthVariable)
|
public function SetExternalAuthenticationVariable($sExtAuthVariable)
|
||||||
{
|
{
|
||||||
$this->m_sExtAuthVariable = $sExtAuthVariable;
|
$this->m_sExtAuthVariable = $sExtAuthVariable;
|
||||||
@@ -2412,7 +2446,7 @@ class ConfigPlaceholdersResolver
|
|||||||
}
|
}
|
||||||
|
|
||||||
$sPattern = '/\%(env|server)\((\w+)\)(?:\?:(\w*))?\%/'; //3 capturing groups, ie `%env(HTTP_PORT)?:8080%` produce: `env` `HTTP_PORT` and `8080`.
|
$sPattern = '/\%(env|server)\((\w+)\)(?:\?:(\w*))?\%/'; //3 capturing groups, ie `%env(HTTP_PORT)?:8080%` produce: `env` `HTTP_PORT` and `8080`.
|
||||||
|
|
||||||
if (! preg_match_all($sPattern, $rawValue, $aMatchesCollection, PREG_SET_ORDER))
|
if (! preg_match_all($sPattern, $rawValue, $aMatchesCollection, PREG_SET_ORDER))
|
||||||
{
|
{
|
||||||
return $rawValue;
|
return $rawValue;
|
||||||
@@ -2465,4 +2499,4 @@ class ConfigPlaceholdersResolver
|
|||||||
IssueLog::Error($sErrorMessage, self::class, array($sSourceName, $sKey, $sDefault, $sWholeMask));
|
IssueLog::Error($sErrorMessage, self::class, array($sSourceName, $sKey, $sDefault, $sWholeMask));
|
||||||
throw new ConfigException($sErrorMessage);
|
throw new ConfigException($sErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,12 +229,47 @@ class CoreUnexpectedValue extends CoreException
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6458 object creation
|
||||||
|
*/
|
||||||
|
class InvalidExternalKeyValueException extends CoreUnexpectedValue
|
||||||
|
{
|
||||||
|
private const ENUM_PARAMS_OBJECT = 'current_object';
|
||||||
|
private const ENUM_PARAMS_ATTCODE = 'attcode';
|
||||||
|
private const ENUM_PARAMS_ATTVALUE = 'attvalue';
|
||||||
|
private const ENUM_PARAMS_USER = 'current_user';
|
||||||
|
|
||||||
|
public function __construct($oObject, $sAttCode, $aContextData = null, $oPrevious = null)
|
||||||
|
{
|
||||||
|
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject) . '::' . $oObject->GetKey();
|
||||||
|
$aContextData[self::ENUM_PARAMS_ATTCODE] = $sAttCode;
|
||||||
|
$aContextData[self::ENUM_PARAMS_ATTVALUE] = $oObject->Get($sAttCode);
|
||||||
|
|
||||||
|
$oCurrentUser = UserRights::GetUserObject();
|
||||||
|
if (false === is_null($oCurrentUser)) {
|
||||||
|
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser) . '::' . $oCurrentUser->GetKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct('Attribute pointing to an object that is either non existing or not readable by the current user', $aContextData, '', $oPrevious);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetAttCode(): string
|
||||||
|
{
|
||||||
|
return $this->getContextData()[self::ENUM_PARAMS_ATTCODE];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetAttValue(): string
|
||||||
|
{
|
||||||
|
return $this->getContextData()[self::ENUM_PARAMS_ATTVALUE];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SecurityException extends CoreException
|
class SecurityException extends CoreException
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throwned when querying on an object that exists in the database but is archived
|
* Thrown when querying on an object that exists in the database but is archived
|
||||||
*
|
*
|
||||||
* @see N.1108
|
* @see N.1108
|
||||||
* @since 2.5.1
|
* @since 2.5.1
|
||||||
|
|||||||
@@ -188,8 +188,8 @@ final class ItopCounter
|
|||||||
|
|
||||||
if (!$hDBLink)
|
if (!$hDBLink)
|
||||||
{
|
{
|
||||||
throw new Exception("Could not connect to the DB server (host=$sDBHost, user=$sDBUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $hDBLink;
|
return $hDBLink;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
use Combodo\iTop\Application\Helper\ExportHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bulk export: CSV export
|
* Bulk export: CSV export
|
||||||
@@ -113,6 +114,7 @@ class CSVBulkExport extends TabularBulkExport
|
|||||||
|
|
||||||
case 'csv_options':
|
case 'csv_options':
|
||||||
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:CSVOptions').'</legend>');
|
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:CSVOptions').'</legend>');
|
||||||
|
$oP->add(ExportHelper::GetAlertForExcelMaliciousInjection());
|
||||||
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
|
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
|
||||||
$oP->add('<h3>'.Dict::S('UI:CSVImport:SeparatorCharacter').'</h3>');
|
$oP->add('<h3>'.Dict::S('UI:CSVImport:SeparatorCharacter').'</h3>');
|
||||||
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');
|
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');
|
||||||
|
|||||||
@@ -532,11 +532,10 @@ abstract class DBObject implements iDisplay
|
|||||||
* Attributes setter
|
* Attributes setter
|
||||||
*
|
*
|
||||||
* Set $sAttCode to $value.
|
* Set $sAttCode to $value.
|
||||||
* The value must be valid according to the type of attribute.
|
* The value must be valid according to the type of attribute : see the different {@see AttributeDefinition::MakeRealValue()} implementations
|
||||||
* The value will not be recorded into the DB until DBObject::DBWrite() is called.
|
* The value will not be recorded into the DB until DBObject::DBWrite() is called.
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @see DBWrite()
|
|
||||||
*
|
*
|
||||||
* @param string $sAttCode
|
* @param string $sAttCode
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
@@ -544,6 +543,8 @@ abstract class DBObject implements iDisplay
|
|||||||
* @return bool
|
* @return bool
|
||||||
* @throws CoreException
|
* @throws CoreException
|
||||||
* @throws CoreUnexpectedValue
|
* @throws CoreUnexpectedValue
|
||||||
|
*
|
||||||
|
* @see DBWrite()
|
||||||
*/
|
*/
|
||||||
public function Set($sAttCode, $value)
|
public function Set($sAttCode, $value)
|
||||||
{
|
{
|
||||||
@@ -2084,16 +2085,17 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param array $aUniquenessRuleProperties uniqueness rule properties
|
||||||
|
*
|
||||||
|
* @param string $sUniquenessRuleId uniqueness rule ID
|
||||||
|
* @return \DBSearch
|
||||||
|
* @throws \OQLException
|
||||||
|
* @throws \CoreException
|
||||||
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*
|
*
|
||||||
* @param string $sUniquenessRuleId uniqueness rule ID
|
* @since 2.6.0 N°659 uniqueness constraint
|
||||||
* @param array $aUniquenessRuleProperties uniqueness rule properties
|
* @since 2.7.11 3.1.2 3.2.0 N°4314 Fix Uniqueness rules not working with Silo
|
||||||
*
|
|
||||||
* @return \DBSearch
|
|
||||||
* @throws \CoreException
|
|
||||||
* @throws \OQLException
|
|
||||||
* @since 2.6.0 N°659 uniqueness constraint
|
|
||||||
* @api
|
|
||||||
*/
|
*/
|
||||||
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
|
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
|
||||||
{
|
{
|
||||||
@@ -2123,8 +2125,10 @@ abstract class DBObject implements iDisplay
|
|||||||
$oUniquenessQuery->AddConditionForInOperatorUsingParam('finalclass', $aChildClassesWithRuleDisabled, false);
|
$oUniquenessQuery->AddConditionForInOperatorUsingParam('finalclass', $aChildClassesWithRuleDisabled, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $oUniquenessQuery;
|
$oUniquenessQuery->AllowAllData();
|
||||||
}
|
|
||||||
|
return $oUniquenessQuery;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check integrity rules (before inserting or updating the object)
|
* Check integrity rules (before inserting or updating the object)
|
||||||
@@ -2140,7 +2144,6 @@ abstract class DBObject implements iDisplay
|
|||||||
* @throws \ArchivedObjectException
|
* @throws \ArchivedObjectException
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
* @throws \OQLException
|
* @throws \OQLException
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function DoCheckToWrite()
|
public function DoCheckToWrite()
|
||||||
{
|
{
|
||||||
@@ -2195,7 +2198,6 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @api
|
* @api
|
||||||
* @api-advanced
|
* @api-advanced
|
||||||
*
|
*
|
||||||
@@ -2211,7 +2213,6 @@ abstract class DBObject implements iDisplay
|
|||||||
* @throws \ArchivedObjectException
|
* @throws \ArchivedObjectException
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
* @throws \OQLException
|
* @throws \OQLException
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
final public function CheckToWrite()
|
final public function CheckToWrite()
|
||||||
{
|
{
|
||||||
@@ -2225,7 +2226,7 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
$oKPI = new ExecutionKPI();
|
$oKPI = new ExecutionKPI();
|
||||||
$this->DoCheckToWrite();
|
$this->DoCheckToWrite();
|
||||||
$oKPI->ComputeStats('CheckToWrite', get_class($this));
|
$oKPI->ComputeStatsForExtension($this, 'DoCheckToWrite');
|
||||||
if (count($this->m_aCheckIssues) == 0)
|
if (count($this->m_aCheckIssues) == 0)
|
||||||
{
|
{
|
||||||
$this->m_bCheckStatus = true;
|
$this->m_bCheckStatus = true;
|
||||||
@@ -2238,6 +2239,87 @@ abstract class DBObject implements iDisplay
|
|||||||
return array($this->m_bCheckStatus, $this->m_aCheckIssues, $this->m_bSecurityIssue);
|
return array($this->m_bCheckStatus, $this->m_aCheckIssues, $this->m_bSecurityIssue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for extkey attributes values. This will throw exception on non-existing as well as non-accessible objects (silo, scopes).
|
||||||
|
* That's why the test is done for all users including Administrators
|
||||||
|
*
|
||||||
|
* Note that due to perf issues, this isn't called directly by the ORM, but has to be called by consumers when possible.
|
||||||
|
*
|
||||||
|
* @param callable(string, string):bool|null $oIsObjectLoadableCallback Override to check if object is accessible.
|
||||||
|
* Parameters are object class and key
|
||||||
|
* Return value should be false if cannot access object, true otherwise
|
||||||
|
* @return void
|
||||||
|
*
|
||||||
|
* @throws ArchivedObjectException
|
||||||
|
* @throws CoreException if cannot get object attdef list
|
||||||
|
* @throws CoreUnexpectedValue
|
||||||
|
* @throws InvalidExternalKeyValueException
|
||||||
|
* @throws MySQLException
|
||||||
|
* @throws SecurityException if one extkey is pointing to an invalid value
|
||||||
|
*
|
||||||
|
* @link https://github.com/Combodo/iTop/security/advisories/GHSA-245j-66p9-pwmh
|
||||||
|
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6458
|
||||||
|
*
|
||||||
|
* @see \RestUtils::FindObjectFromKey for the same check in the REST endpoint
|
||||||
|
*/
|
||||||
|
final public function CheckChangedExtKeysValues(callable $oIsObjectLoadableCallback = null)
|
||||||
|
{
|
||||||
|
if (is_null($oIsObjectLoadableCallback)) {
|
||||||
|
$oIsObjectLoadableCallback = function ($sClass, $sId) {
|
||||||
|
$oRemoteObject = MetaModel::GetObject($sClass, $sId, false);
|
||||||
|
if (is_null($oRemoteObject)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$aChanges = $this->ListChanges();
|
||||||
|
$aAttCodesChanged = array_keys($aChanges);
|
||||||
|
foreach ($aAttCodesChanged as $sAttDefCode) {
|
||||||
|
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttDefCode);
|
||||||
|
|
||||||
|
if ($oAttDef instanceof AttributeLinkedSetIndirect) {
|
||||||
|
/** @var ormLinkSet $oOrmSet */
|
||||||
|
$oOrmSet = $this->Get($sAttDefCode);
|
||||||
|
while ($oLnk = $oOrmSet->Fetch()) {
|
||||||
|
$oLnk->CheckChangedExtKeysValues($oIsObjectLoadableCallback);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @noinspection PhpConditionCheckedByNextConditionInspection */
|
||||||
|
/** @noinspection NotOptimalIfConditionsInspection */
|
||||||
|
if (($oAttDef instanceof AttributeHierarchicalKey) || ($oAttDef instanceof AttributeExternalKey)) {
|
||||||
|
$sRemoteObjectClass = $oAttDef->GetTargetClass();
|
||||||
|
$sRemoteObjectKey = $this->Get($sAttDefCode);
|
||||||
|
} else if ($oAttDef instanceof AttributeObjectKey) {
|
||||||
|
$sRemoteObjectClassAttCode = $oAttDef->Get('class_attcode');
|
||||||
|
$sRemoteObjectClass = $this->Get($sRemoteObjectClassAttCode);
|
||||||
|
$sRemoteObjectKey = $this->Get($sAttDefCode);
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (utils::IsNullOrEmptyString($sRemoteObjectClass)
|
||||||
|
|| utils::IsNullOrEmptyString($sRemoteObjectKey)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 : Undefined ext. key (EG. non-mandatory and no value provided)
|
||||||
|
// < 0 : Non yet persisted object
|
||||||
|
/** @noinspection TypeUnsafeComparisonInspection Non-strict comparison as object ID can be string */
|
||||||
|
if ($sRemoteObjectKey <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (false === $oIsObjectLoadableCallback($sRemoteObjectClass, $sRemoteObjectKey)) {
|
||||||
|
throw new InvalidExternalKeyValueException($this, $sAttDefCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if it is allowed to delete the existing object from the database
|
* Check if it is allowed to delete the existing object from the database
|
||||||
*
|
*
|
||||||
@@ -2383,13 +2465,13 @@ abstract class DBObject implements iDisplay
|
|||||||
* @api
|
* @api
|
||||||
* @api-advanced
|
* @api-advanced
|
||||||
*
|
*
|
||||||
* @see \DBObject::ListPreviousValuesForUpdatedAttributes() to get previous values anywhere in the CRUD stack
|
* @return array attcode => currentvalue List the attributes that have been changed using {@see DBObject::Set()}.
|
||||||
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Asequence_crud iTop CRUD stack documentation
|
|
||||||
* @return array attname => currentvalue List the attributes that have been changed using {@see DBObject::Set()}.
|
|
||||||
* Reset during {@see DBObject::DBUpdate()}
|
* Reset during {@see DBObject::DBUpdate()}
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
|
* @see \DBObject::ListPreviousValuesForUpdatedAttributes() to get previous values anywhere in the CRUD stack
|
||||||
|
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Asequence_crud iTop CRUD stack documentation
|
||||||
* @uses m_aCurrValues
|
* @uses m_aCurrValues
|
||||||
*/
|
*/
|
||||||
public function ListChanges()
|
public function ListChanges()
|
||||||
{
|
{
|
||||||
if ($this->m_bIsInDB)
|
if ($this->m_bIsInDB)
|
||||||
@@ -2680,7 +2762,6 @@ abstract class DBObject implements iDisplay
|
|||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*
|
*
|
||||||
* @internal
|
* @internal
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public function DBInsertNoReload()
|
public function DBInsertNoReload()
|
||||||
{
|
{
|
||||||
@@ -2693,8 +2774,12 @@ abstract class DBObject implements iDisplay
|
|||||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||||
|
|
||||||
// Ensure the update of the values (we are accessing the data directly)
|
// Ensure the update of the values (we are accessing the data directly)
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$this->DoComputeValues();
|
$this->DoComputeValues();
|
||||||
|
$oKPI->ComputeStatsForExtension($this, 'DoComputeValues');
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$this->OnInsert();
|
$this->OnInsert();
|
||||||
|
$oKPI->ComputeStatsForExtension($this, 'OnInsert');
|
||||||
|
|
||||||
if ($this->m_iKey < 0)
|
if ($this->m_iKey < 0)
|
||||||
{
|
{
|
||||||
@@ -2712,7 +2797,7 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ultimate check - ensure DB integrity
|
// Ultimate check - ensure DB integrity
|
||||||
list($bRes, $aIssues) = $this->CheckToWrite();
|
[$bRes, $aIssues] = $this->CheckToWrite();
|
||||||
if (!$bRes)
|
if (!$bRes)
|
||||||
{
|
{
|
||||||
throw new CoreCannotSaveObjectException(array('issues' => $aIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
throw new CoreCannotSaveObjectException(array('issues' => $aIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
||||||
@@ -2818,7 +2903,9 @@ abstract class DBObject implements iDisplay
|
|||||||
$this->m_aOrigValues[$sAttCode] = $value;
|
$this->m_aOrigValues[$sAttCode] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$this->AfterInsert();
|
$this->AfterInsert();
|
||||||
|
$oKPI->ComputeStatsForExtension($this, 'AfterInsert');
|
||||||
|
|
||||||
// Activate any existing trigger
|
// Activate any existing trigger
|
||||||
$sClass = get_class($this);
|
$sClass = get_class($this);
|
||||||
@@ -2954,8 +3041,6 @@ abstract class DBObject implements iDisplay
|
|||||||
* Persist an object to the DB, for the first time
|
* Persist an object to the DB, for the first time
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @see DBWrite
|
|
||||||
*
|
|
||||||
* @return int|null inserted object key
|
* @return int|null inserted object key
|
||||||
*
|
*
|
||||||
* @throws \ArchivedObjectException
|
* @throws \ArchivedObjectException
|
||||||
@@ -2965,10 +3050,12 @@ abstract class DBObject implements iDisplay
|
|||||||
* @throws \CoreWarning
|
* @throws \CoreWarning
|
||||||
* @throws \MySQLException
|
* @throws \MySQLException
|
||||||
* @throws \OQLException
|
* @throws \OQLException
|
||||||
|
*
|
||||||
|
* @see DBWrite
|
||||||
*/
|
*/
|
||||||
public function DBInsert()
|
public function DBInsert()
|
||||||
{
|
{
|
||||||
$this->DBInsertNoReload();
|
$this->DBInsertNoReload();
|
||||||
|
|
||||||
if (MetaModel::DBIsReadOnly())
|
if (MetaModel::DBIsReadOnly())
|
||||||
{
|
{
|
||||||
@@ -3067,13 +3154,13 @@ abstract class DBObject implements iDisplay
|
|||||||
* Update an object in DB
|
* Update an object in DB
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
* @see DBObject::DBWrite()
|
|
||||||
*
|
|
||||||
* @return int object key
|
* @return int object key
|
||||||
*
|
*
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
* @throws \CoreCannotSaveObjectException if CheckToWrite() returns issues
|
* @throws \CoreCannotSaveObjectException if CheckToWrite() returns issues
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
|
*
|
||||||
|
* @see DBObject::DBWrite()
|
||||||
*/
|
*/
|
||||||
public function DBUpdate()
|
public function DBUpdate()
|
||||||
{
|
{
|
||||||
@@ -3081,6 +3168,7 @@ abstract class DBObject implements iDisplay
|
|||||||
{
|
{
|
||||||
throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead");
|
throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Protect against reentrance (e.g. cascading the update of ticket logs)
|
// Protect against reentrance (e.g. cascading the update of ticket logs)
|
||||||
static $aUpdateReentrance = array();
|
static $aUpdateReentrance = array();
|
||||||
$sKey = get_class($this).'::'.$this->GetKey();
|
$sKey = get_class($this).'::'.$this->GetKey();
|
||||||
@@ -3094,8 +3182,11 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$this->DoComputeValues();
|
$this->DoComputeValues();
|
||||||
// Stop watches
|
$oKPI->ComputeStatsForExtension($this, 'DoComputeValues');
|
||||||
|
|
||||||
|
// Stop watches
|
||||||
$sState = $this->GetState();
|
$sState = $this->GetState();
|
||||||
if ($sState != '')
|
if ($sState != '')
|
||||||
{
|
{
|
||||||
@@ -3114,7 +3205,9 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->OnUpdate();
|
$oKPI = new ExecutionKPI();
|
||||||
|
$this->OnUpdate();
|
||||||
|
$oKPI->ComputeStatsForExtension($this, 'OnUpdate');
|
||||||
|
|
||||||
$aChanges = $this->ListChanges();
|
$aChanges = $this->ListChanges();
|
||||||
if (count($aChanges) == 0)
|
if (count($aChanges) == 0)
|
||||||
@@ -3126,7 +3219,7 @@ abstract class DBObject implements iDisplay
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ultimate check - ensure DB integrity
|
// Ultimate check - ensure DB integrity
|
||||||
list($bRes, $aIssues) = $this->CheckToWrite();
|
[$bRes, $aIssues] = $this->CheckToWrite();
|
||||||
if (!$bRes)
|
if (!$bRes)
|
||||||
{
|
{
|
||||||
throw new CoreCannotSaveObjectException(array(
|
throw new CoreCannotSaveObjectException(array(
|
||||||
@@ -3326,7 +3419,9 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
$this->AfterUpdate();
|
$oKPI = new ExecutionKPI();
|
||||||
|
$this->AfterUpdate();
|
||||||
|
$oKPI->ComputeStatsForExtension($this, 'AfterUpdate');
|
||||||
|
|
||||||
// Reload to get the external attributes
|
// Reload to get the external attributes
|
||||||
if ($bHasANewExternalKeyValue)
|
if ($bHasANewExternalKeyValue)
|
||||||
@@ -3405,13 +3500,18 @@ abstract class DBObject implements iDisplay
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Make the current changes persistent - clever wrapper for Insert or Update
|
* Make the current changes persistent - clever wrapper for Insert or Update
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*
|
*
|
||||||
* @throws \CoreCannotSaveObjectException
|
* @throws ArchivedObjectException
|
||||||
* @throws \CoreException
|
* @throws CoreCannotSaveObjectException
|
||||||
|
* @throws CoreException
|
||||||
|
* @throws CoreUnexpectedValue
|
||||||
|
* @throws CoreWarning
|
||||||
|
* @throws MySQLException
|
||||||
|
* @throws OQLException
|
||||||
*/
|
*/
|
||||||
public function DBWrite()
|
public function DBWrite()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -763,7 +763,10 @@ class DBObjectSet implements iDBObjectSetIterator
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$this->m_oSQLResult = CMDBSource::Query($sSQL);
|
$this->m_oSQLResult = CMDBSource::Query($sSQL);
|
||||||
|
$sOQL = $this->GetPseudoOQL($this->m_oFilter, $this->GetRealSortOrder(), $this->m_iLimitCount, $this->m_iLimitStart, false);
|
||||||
|
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||||
} catch (MySQLException $e)
|
} catch (MySQLException $e)
|
||||||
{
|
{
|
||||||
// 1116 = ER_TOO_MANY_TABLES
|
// 1116 = ER_TOO_MANY_TABLES
|
||||||
@@ -843,8 +846,11 @@ class DBObjectSet implements iDBObjectSetIterator
|
|||||||
{
|
{
|
||||||
if (is_null($this->m_iNumTotalDBRows))
|
if (is_null($this->m_iNumTotalDBRows))
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
|
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
|
||||||
$resQuery = CMDBSource::Query($sSQL);
|
$resQuery = CMDBSource::Query($sSQL);
|
||||||
|
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), 0, 0, true);
|
||||||
|
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||||
if (!$resQuery) return 0;
|
if (!$resQuery) return 0;
|
||||||
|
|
||||||
$aRow = CMDBSource::FetchArray($resQuery);
|
$aRow = CMDBSource::FetchArray($resQuery);
|
||||||
@@ -855,6 +861,42 @@ class DBObjectSet implements iDBObjectSetIterator
|
|||||||
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
|
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DBSearch $oFilter
|
||||||
|
* @param array $aOrder
|
||||||
|
* @param int $iLimitCount
|
||||||
|
* @param int $iLimitStart
|
||||||
|
* @param bool $bCount
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function GetPseudoOQL($oFilter, $aOrder, $iLimitCount, $iLimitStart, $bCount)
|
||||||
|
{
|
||||||
|
$sOQL = '';
|
||||||
|
if ($bCount) {
|
||||||
|
$sOQL .= 'COUNT ';
|
||||||
|
}
|
||||||
|
$sOQL .= $oFilter->ToOQL();
|
||||||
|
|
||||||
|
if ($iLimitCount > 0) {
|
||||||
|
$sOQL .= ' LIMIT ';
|
||||||
|
if ($iLimitStart > 0) {
|
||||||
|
$sOQL .= "$iLimitStart, ";
|
||||||
|
}
|
||||||
|
$sOQL .= "$iLimitCount";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($aOrder) > 0) {
|
||||||
|
$sOQL .= ' ORDER BY ';
|
||||||
|
$aOrderBy = [];
|
||||||
|
foreach ($aOrder as $sAttCode => $bAsc) {
|
||||||
|
$aOrderBy[] = $sAttCode.' '.($bAsc ? 'ASC' : 'DESC');
|
||||||
|
}
|
||||||
|
$sOQL .= implode(', ', $aOrderBy);
|
||||||
|
}
|
||||||
|
return $sOQL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the count exceeds a given limit
|
* Check if the count exceeds a given limit
|
||||||
*
|
*
|
||||||
@@ -871,8 +913,11 @@ class DBObjectSet implements iDBObjectSetIterator
|
|||||||
{
|
{
|
||||||
if (is_null($this->m_iNumTotalDBRows))
|
if (is_null($this->m_iNumTotalDBRows))
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||||
$resQuery = CMDBSource::Query($sSQL);
|
$resQuery = CMDBSource::Query($sSQL);
|
||||||
|
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
|
||||||
|
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||||
if ($resQuery)
|
if ($resQuery)
|
||||||
{
|
{
|
||||||
$aRow = CMDBSource::FetchArray($resQuery);
|
$aRow = CMDBSource::FetchArray($resQuery);
|
||||||
@@ -883,7 +928,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
|||||||
{
|
{
|
||||||
$iCount = 0;
|
$iCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$iCount = $this->m_iNumTotalDBRows;
|
$iCount = $this->m_iNumTotalDBRows;
|
||||||
@@ -908,8 +953,11 @@ class DBObjectSet implements iDBObjectSetIterator
|
|||||||
{
|
{
|
||||||
if (is_null($this->m_iNumTotalDBRows))
|
if (is_null($this->m_iNumTotalDBRows))
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||||
$resQuery = CMDBSource::Query($sSQL);
|
$resQuery = CMDBSource::Query($sSQL);
|
||||||
|
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
|
||||||
|
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||||
if ($resQuery)
|
if ($resQuery)
|
||||||
{
|
{
|
||||||
$aRow = CMDBSource::FetchArray($resQuery);
|
$aRow = CMDBSource::FetchArray($resQuery);
|
||||||
@@ -920,7 +968,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
|||||||
{
|
{
|
||||||
$iCount = 0;
|
$iCount = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
$iCount = $this->m_iNumTotalDBRows;
|
$iCount = $this->m_iNumTotalDBRows;
|
||||||
|
|||||||
@@ -866,11 +866,11 @@ abstract class DBSearch
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count($aColumns) == 0)
|
if (count($aColumns) == 0)
|
||||||
{
|
{
|
||||||
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
|
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
|
||||||
// Add the standard id (as first column)
|
// Add the standard id (as first column)
|
||||||
array_unshift($aColumns, 'id');
|
array_unshift($aColumns, 'id');
|
||||||
}
|
}
|
||||||
|
|
||||||
$aQueryCols = CMDBSource::GetColumns($resQuery, $sSQL);
|
$aQueryCols = CMDBSource::GetColumns($resQuery, $sSQL);
|
||||||
@@ -900,6 +900,55 @@ abstract class DBSearch
|
|||||||
return $aRes;
|
return $aRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects a column ($sAttCode) from the specified class ($sClassAlias - default main class) of the DBsearch object and gives the result as an array
|
||||||
|
* @param string $sAttCode
|
||||||
|
* @param string|null $sClassAlias
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
* @throws ConfigException
|
||||||
|
* @throws CoreException
|
||||||
|
* @throws MissingQueryArgument
|
||||||
|
* @throws MySQLException
|
||||||
|
* @throws MySQLHasGoneAwayException
|
||||||
|
*/
|
||||||
|
public function SelectAttributeToArray(string $sAttCode, ?string $sClassAlias = null):array
|
||||||
|
{
|
||||||
|
if(is_null($sClassAlias)) {
|
||||||
|
$sClassAlias = $this->GetClassAlias();
|
||||||
|
}
|
||||||
|
|
||||||
|
$sClass = $this->GetClass();
|
||||||
|
if($sAttCode === 'id'){
|
||||||
|
$aAttToLoad[$sClassAlias]=[];
|
||||||
|
} else {
|
||||||
|
$aAttToLoad[$sClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
$sSQL = $this->MakeSelectQuery([], [], $aAttToLoad);
|
||||||
|
$resQuery = CMDBSource::Query($sSQL);
|
||||||
|
if (!$resQuery)
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$sColName = $sClassAlias.$sAttCode;
|
||||||
|
|
||||||
|
$aRes = [];
|
||||||
|
while ($aRow = CMDBSource::FetchArray($resQuery))
|
||||||
|
{
|
||||||
|
$aMappedRow = array();
|
||||||
|
if($sAttCode === 'id') {
|
||||||
|
$aMappedRow[$sAttCode] = $aRow[$sColName];
|
||||||
|
} else {
|
||||||
|
$aMappedRow[$sAttCode] = $aAttToLoad[$sClassAlias][$sAttCode]->FromSQLToValue($aRow, $sColName);
|
||||||
|
}
|
||||||
|
$aRes[] = $aMappedRow;
|
||||||
|
}
|
||||||
|
CMDBSource::FreeResult($resQuery);
|
||||||
|
return $aRes;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Construction of the SQL queries
|
// Construction of the SQL queries
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// This file is part of iTop.
|
// This file is part of iTop.
|
||||||
//
|
//
|
||||||
// iTop is free software; you can redistribute it and/or modify
|
// iTop is free software; you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU Affero General Public License as published by
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Dict
|
* Class Dict
|
||||||
* Management of localizable strings
|
* Management of localizable strings
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
@@ -144,33 +144,50 @@ class Dict
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||||
|
{
|
||||||
|
$aInfo = self::GetLabelAndLangCode($sStringCode, $sDefault, $bUserLanguageOnly);
|
||||||
|
return $aInfo['label'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a localised string from the dictonary with its associated lang code
|
||||||
|
*
|
||||||
|
* @param string $sStringCode The code identifying the dictionary entry
|
||||||
|
* @param string $sDefault Default value if there is no match in the dictionary
|
||||||
|
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
|
||||||
|
*
|
||||||
|
* @return array{
|
||||||
|
* lang: string, label: string
|
||||||
|
* } with localized label string and used lang code
|
||||||
|
*/
|
||||||
|
private static function GetLabelAndLangCode($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||||
{
|
{
|
||||||
// Attempt to find the string in the user language
|
// Attempt to find the string in the user language
|
||||||
//
|
//
|
||||||
$sLangCode = self::GetUserLanguage();
|
$sLangCode = self::GetUserLanguage();
|
||||||
self::InitLangIfNeeded($sLangCode);
|
self::InitLangIfNeeded($sLangCode);
|
||||||
|
|
||||||
if (!array_key_exists($sLangCode, self::$m_aData))
|
if (! array_key_exists($sLangCode, self::$m_aData))
|
||||||
{
|
{
|
||||||
IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed");
|
IssueLog::Warning("Cannot find $sLangCode in all registered dictionaries.");
|
||||||
// It may happen, when something happens before the dictionaries get loaded
|
// It may happen, when something happens before the dictionaries get loaded
|
||||||
return $sStringCode;
|
return [ 'label' => $sStringCode, 'lang' => $sLangCode ];
|
||||||
}
|
}
|
||||||
$aCurrentDictionary = self::$m_aData[$sLangCode];
|
$aCurrentDictionary = self::$m_aData[$sLangCode];
|
||||||
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
|
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
|
||||||
{
|
{
|
||||||
return $aCurrentDictionary[$sStringCode];
|
return [ 'label' => $aCurrentDictionary[$sStringCode], 'lang' => $sLangCode ];
|
||||||
}
|
}
|
||||||
if (!$bUserLanguageOnly)
|
if (!$bUserLanguageOnly)
|
||||||
{
|
{
|
||||||
// Attempt to find the string in the default language
|
// Attempt to find the string in the default language
|
||||||
//
|
//
|
||||||
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
||||||
|
|
||||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||||
{
|
{
|
||||||
return $aDefaultDictionary[$sStringCode];
|
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => self::$m_sDefaultLanguage ];
|
||||||
}
|
}
|
||||||
// Attempt to find the string in english
|
// Attempt to find the string in english
|
||||||
//
|
//
|
||||||
@@ -179,17 +196,17 @@ class Dict
|
|||||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||||
{
|
{
|
||||||
return $aDefaultDictionary[$sStringCode];
|
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => 'EN US' ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Could not find the string...
|
// Could not find the string...
|
||||||
//
|
//
|
||||||
if (is_null($sDefault))
|
if (is_null($sDefault))
|
||||||
{
|
{
|
||||||
return $sStringCode;
|
return [ 'label' => $sStringCode, 'lang' => null ];
|
||||||
}
|
}
|
||||||
|
|
||||||
return $sDefault;
|
return [ 'label' => $sDefault, 'lang' => null ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -205,19 +222,25 @@ class Dict
|
|||||||
*/
|
*/
|
||||||
public static function Format($sFormatCode /*, ... arguments ....*/)
|
public static function Format($sFormatCode /*, ... arguments ....*/)
|
||||||
{
|
{
|
||||||
$sLocalizedFormat = self::S($sFormatCode);
|
['label' => $sLocalizedFormat, 'lang' => $sLangCode] = self::GetLabelAndLangCode($sFormatCode);
|
||||||
|
|
||||||
$aArguments = func_get_args();
|
$aArguments = func_get_args();
|
||||||
array_shift($aArguments);
|
array_shift($aArguments);
|
||||||
|
|
||||||
if ($sLocalizedFormat == $sFormatCode)
|
if ($sLocalizedFormat == $sFormatCode)
|
||||||
{
|
{
|
||||||
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
|
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
|
||||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
return vsprintf($sLocalizedFormat, $aArguments);
|
try{
|
||||||
|
return vsprintf($sLocalizedFormat, $aArguments);
|
||||||
|
} catch(\Throwable $e){
|
||||||
|
\IssueLog::Error("Cannot format dict key", null, ["sFormatCode" => $sFormatCode, "sLangCode" => $sLangCode, 'exception_msg' => $e->getMessage() ]);
|
||||||
|
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a the entries for a given language (replaces the former Add() method)
|
* Initialize a the entries for a given language (replaces the former Add() method)
|
||||||
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
|
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
|
||||||
@@ -227,7 +250,7 @@ class Dict
|
|||||||
{
|
{
|
||||||
self::$m_aData[$sLanguageCode] = $aEntries;
|
self::$m_aData[$sLanguageCode] = $aEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the list of available languages
|
* Set the list of available languages
|
||||||
* @param hash $aLanguagesList
|
* @param hash $aLanguagesList
|
||||||
@@ -288,7 +311,7 @@ class Dict
|
|||||||
{
|
{
|
||||||
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
|
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
|
||||||
require_once($sDictFile);
|
require_once($sDictFile);
|
||||||
|
|
||||||
if (self::GetApcService()->function_exists('apc_store')
|
if (self::GetApcService()->function_exists('apc_store')
|
||||||
&& (self::$m_sApplicationPrefix !== null))
|
&& (self::$m_sApplicationPrefix !== null))
|
||||||
{
|
{
|
||||||
@@ -298,7 +321,7 @@ class Dict
|
|||||||
}
|
}
|
||||||
return $bResult;
|
return $bResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable caching (cached using APC)
|
* Enable caching (cached using APC)
|
||||||
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
|
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
|
||||||
@@ -342,14 +365,14 @@ class Dict
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
|
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
|
||||||
{
|
{
|
||||||
$aMissing = array(); // Strings missing for the target language
|
$aMissing = array(); // Strings missing for the target language
|
||||||
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
|
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
|
||||||
$aNotTranslated = array(); // Strings having the same value in both dictionaries
|
$aNotTranslated = array(); // Strings having the same value in both dictionaries
|
||||||
$aOK = array(); // Strings having different values in both dictionaries
|
$aOK = array(); // Strings having different values in both dictionaries
|
||||||
|
|
||||||
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
|
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
|
||||||
{
|
{
|
||||||
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
|
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
|
||||||
@@ -357,7 +380,7 @@ class Dict
|
|||||||
$aMissing[$sStringCode] = $sValue;
|
$aMissing[$sStringCode] = $sValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
|
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
|
||||||
{
|
{
|
||||||
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
|
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
|
||||||
@@ -380,7 +403,7 @@ class Dict
|
|||||||
}
|
}
|
||||||
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
|
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function Dump()
|
public static function Dump()
|
||||||
{
|
{
|
||||||
MyHelpers::var_dump_html(self::$m_aData);
|
MyHelpers::var_dump_html(self::$m_aData);
|
||||||
@@ -403,7 +426,7 @@ class Dict
|
|||||||
// No need to actually load the strings since it's only used to know the list of languages
|
// No need to actually load the strings since it's only used to know the list of languages
|
||||||
// at setup time !!
|
// at setup time !!
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export all the dictionary entries - of the given language - whose code matches the given prefix
|
* Export all the dictionary entries - of the given language - whose code matches the given prefix
|
||||||
* missing entries in the current language will be replaced by entries in the default language
|
* missing entries in the current language will be replaced by entries in the default language
|
||||||
@@ -416,7 +439,7 @@ class Dict
|
|||||||
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
||||||
$aEntries = array();
|
$aEntries = array();
|
||||||
$iLength = strlen($sStartingWith);
|
$iLength = strlen($sStartingWith);
|
||||||
|
|
||||||
// First prefill the array with entries from the default language
|
// First prefill the array with entries from the default language
|
||||||
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
|
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
|
||||||
{
|
{
|
||||||
@@ -425,7 +448,7 @@ class Dict
|
|||||||
$aEntries[$sCode] = $sEntry;
|
$aEntries[$sCode] = $sEntry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now put (overwrite) the entries for the user language
|
// Now put (overwrite) the entries for the user language
|
||||||
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
|
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,6 +23,8 @@
|
|||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use Combodo\iTop\Application\Helper\ExportHelper;
|
||||||
|
|
||||||
require_once(APPROOT.'application/xlsxwriter.class.php');
|
require_once(APPROOT.'application/xlsxwriter.class.php');
|
||||||
|
|
||||||
class ExcelBulkExport extends TabularBulkExport
|
class ExcelBulkExport extends TabularBulkExport
|
||||||
@@ -89,6 +91,7 @@ class ExcelBulkExport extends TabularBulkExport
|
|||||||
|
|
||||||
case 'xlsx_options':
|
case 'xlsx_options':
|
||||||
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:XLSXOptions').'</legend>');
|
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:XLSXOptions').'</legend>');
|
||||||
|
$oP->add(ExportHelper::GetAlertForExcelMaliciousInjection());
|
||||||
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
|
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
|
||||||
|
|
||||||
$sChecked = (utils::ReadParam('formatted_text', 0) == 1) ? ' checked ' : '';
|
$sChecked = (utils::ReadParam('formatted_text', 0) == 1) ? ' checked ' : '';
|
||||||
|
|||||||
@@ -49,10 +49,9 @@ abstract class HTMLSanitizer
|
|||||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||||
} else if (false === is_subclass_of($sSanitizerClass, HTMLSanitizer::class)) {
|
} else if (false === is_subclass_of($sSanitizerClass, HTMLSanitizer::class)) {
|
||||||
if ($sConfigKey === 'html_sanitizer') {
|
if ($sConfigKey === 'html_sanitizer') {
|
||||||
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
|
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.'. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||||
}
|
} else {
|
||||||
if ($sConfigKey === 'svg_sanitizer') {
|
|
||||||
IssueLog::Error('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.' ! Won\'t sanitize string.');
|
IssueLog::Error('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.' ! Won\'t sanitize string.');
|
||||||
|
|
||||||
return $sHTML;
|
return $sHTML;
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Affero General Public License
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
use Combodo\iTop\Core\Kpi\KpiLogData;
|
||||||
|
use Combodo\iTop\Service\Module\ModuleService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,6 +32,8 @@ class ExecutionKPI
|
|||||||
static protected $m_bEnabled_Memory = false;
|
static protected $m_bEnabled_Memory = false;
|
||||||
static protected $m_bBlameCaller = false;
|
static protected $m_bBlameCaller = false;
|
||||||
static protected $m_sAllowedUser = '*';
|
static protected $m_sAllowedUser = '*';
|
||||||
|
static protected $m_bGenerateLegacyReport = true;
|
||||||
|
static protected $m_fSlowQueries = 0;
|
||||||
|
|
||||||
static protected $m_aStats = array(); // Recurrent operations
|
static protected $m_aStats = array(); // Recurrent operations
|
||||||
static protected $m_aExecData = array(); // One shot operations
|
static protected $m_aExecData = array(); // One shot operations
|
||||||
@@ -77,14 +81,39 @@ class ExecutionKPI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public function SetGenerateLegacyReport($bReportExtensionsOnly)
|
||||||
|
{
|
||||||
|
self::$m_bGenerateLegacyReport = $bReportExtensionsOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function SetSlowQueries($fSlowQueries)
|
||||||
|
{
|
||||||
|
self::$m_fSlowQueries = $fSlowQueries;
|
||||||
|
}
|
||||||
|
|
||||||
static public function GetDescription()
|
static public function GetDescription()
|
||||||
{
|
{
|
||||||
$aFeatures = array();
|
$aFeatures = array();
|
||||||
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
|
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
|
||||||
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
|
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
|
||||||
$sFeatures = implode(', ', $aFeatures);
|
$sFeatures = 'Measures: '.implode(', ', $aFeatures);
|
||||||
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
|
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
|
||||||
return "KPI logging is active for $sFor. Measures: $sFeatures";
|
$sSlowQueries = '';
|
||||||
|
if (self::$m_fSlowQueries > 0) {
|
||||||
|
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
|
||||||
|
}
|
||||||
|
|
||||||
|
$aExtensions = [];
|
||||||
|
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||||
|
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||||
|
$aExtensions[] = ModuleService::GetInstance()->GetModuleNameFromObject($oExtensionInstance);
|
||||||
|
}
|
||||||
|
$sExtensions = '';
|
||||||
|
if (count($aExtensions) > 0) {
|
||||||
|
$sExtensions = '. KPI Extensions: ['.implode(', ', $aExtensions).']';
|
||||||
|
}
|
||||||
|
|
||||||
|
return "KPI logging is active for $sFor. $sFeatures$sSlowQueries$sExtensions";
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function ReportStats()
|
static public function ReportStats()
|
||||||
@@ -92,7 +121,28 @@ class ExecutionKPI
|
|||||||
if (!self::IsEnabled()) return;
|
if (!self::IsEnabled()) return;
|
||||||
|
|
||||||
global $fItopStarted;
|
global $fItopStarted;
|
||||||
|
global $iItopInitialMemory;
|
||||||
$sExecId = microtime(); // id to differentiate the hrefs!
|
$sExecId = microtime(); // id to differentiate the hrefs!
|
||||||
|
$sRequest = $_SERVER['REQUEST_URI'].' ('.$_SERVER['REQUEST_METHOD'].')';
|
||||||
|
if (isset($_POST['operation'])) {
|
||||||
|
$sRequest .= ' operation: '.$_POST['operation'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$fStop = MyHelpers::getmicrotime();
|
||||||
|
if (($fStop - $fItopStarted) > self::$m_fSlowQueries) {
|
||||||
|
// Invoke extensions to log the KPI operation
|
||||||
|
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||||
|
$iCurrentMemory = self::memory_get_usage();
|
||||||
|
$iPeakMemory = self::memory_get_peak_usage();
|
||||||
|
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||||
|
$oKPILogData = new KpiLogData(KpiLogData::TYPE_REQUEST, 'Page', $sRequest, $fItopStarted, $fStop, '', $iItopInitialMemory, $iCurrentMemory, $iPeakMemory);
|
||||||
|
$oExtensionInstance->LogOperation($oKPILogData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self::$m_bGenerateLegacyReport) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$aBeginTimes = array();
|
$aBeginTimes = array();
|
||||||
foreach (self::$m_aExecData as $aOpStats)
|
foreach (self::$m_aExecData as $aOpStats)
|
||||||
@@ -105,7 +155,7 @@ class ExecutionKPI
|
|||||||
|
|
||||||
self::Report("<hr/>");
|
self::Report("<hr/>");
|
||||||
self::Report("<div style=\"background-color: grey; padding: 10px;\">");
|
self::Report("<div style=\"background-color: grey; padding: 10px;\">");
|
||||||
self::Report("<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>");
|
self::Report("<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>");
|
||||||
self::Report("<p>".date('Y-m-d H:i:s', $fItopStarted)."</p>");
|
self::Report("<p>".date('Y-m-d H:i:s', $fItopStarted)."</p>");
|
||||||
self::Report("<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>");
|
self::Report("<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>");
|
||||||
self::Report("<div>");
|
self::Report("<div>");
|
||||||
@@ -200,8 +250,6 @@ class ExecutionKPI
|
|||||||
|
|
||||||
self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>");
|
self::Report("<p><a href=\"#end-".md5($sExecId)."\">Next page stats</a></p>");
|
||||||
|
|
||||||
$fSlowQueries = MetaModel::GetConfig()->Get('log_kpi_slow_queries');
|
|
||||||
|
|
||||||
// Report operation details
|
// Report operation details
|
||||||
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
||||||
{
|
{
|
||||||
@@ -245,7 +293,7 @@ class ExecutionKPI
|
|||||||
$sTotalInter = round($fTotalInter, 3);
|
$sTotalInter = round($fTotalInter, 3);
|
||||||
$sMinInter = round($fMinInter, 3);
|
$sMinInter = round($fMinInter, 3);
|
||||||
$sMaxInter = round($fMaxInter, 3);
|
$sMaxInter = round($fMaxInter, 3);
|
||||||
if (($fTotalInter >= $fSlowQueries))
|
if (($fTotalInter >= self::$m_fSlowQueries))
|
||||||
{
|
{
|
||||||
if ($bDisplayHeader)
|
if ($bDisplayHeader)
|
||||||
{
|
{
|
||||||
@@ -271,11 +319,19 @@ class ExecutionKPI
|
|||||||
self::Report('<a name="end-'.md5($sExecId).'"> </a>');
|
self::Report('<a name="end-'.md5($sExecId).'"> </a>');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function InitStats()
|
||||||
|
{
|
||||||
|
// Invoke extensions to initialize the KPI statistics
|
||||||
|
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||||
|
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||||
|
$oExtensionInstance->InitStats();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->ResetCounters();
|
$this->ResetCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the duration since startup, and reset the counter for the next measure
|
// Get the duration since startup, and reset the counter for the next measure
|
||||||
//
|
//
|
||||||
@@ -283,8 +339,14 @@ class ExecutionKPI
|
|||||||
{
|
{
|
||||||
global $fItopStarted;
|
global $fItopStarted;
|
||||||
|
|
||||||
|
if (!self::IsEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$aNewEntry = null;
|
$aNewEntry = null;
|
||||||
|
|
||||||
|
$fStarted = $this->m_fStarted;
|
||||||
|
$fStopped = $this->m_fStarted;
|
||||||
if (self::$m_bEnabled_Duration)
|
if (self::$m_bEnabled_Duration)
|
||||||
{
|
{
|
||||||
$fStopped = MyHelpers::getmicrotime();
|
$fStopped = MyHelpers::getmicrotime();
|
||||||
@@ -297,6 +359,9 @@ class ExecutionKPI
|
|||||||
$this->m_fStarted = $fStopped;
|
$this->m_fStarted = $fStopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||||
|
$iCurrentMemory = 0;
|
||||||
|
$iPeakMemory = 0;
|
||||||
if (self::$m_bEnabled_Memory)
|
if (self::$m_bEnabled_Memory)
|
||||||
{
|
{
|
||||||
$iCurrentMemory = self::memory_get_usage();
|
$iCurrentMemory = self::memory_get_usage();
|
||||||
@@ -306,41 +371,102 @@ class ExecutionKPI
|
|||||||
}
|
}
|
||||||
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
|
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
|
||||||
$aNewEntry['mem_end'] = $iCurrentMemory;
|
$aNewEntry['mem_end'] = $iCurrentMemory;
|
||||||
if (function_exists('memory_get_peak_usage'))
|
$iPeakMemory = self::memory_get_peak_usage();
|
||||||
{
|
$aNewEntry['mem_peak'] = $iPeakMemory;
|
||||||
$aNewEntry['mem_peak'] = memory_get_peak_usage();
|
|
||||||
}
|
|
||||||
// Reset for the next operation (if the object is recycled)
|
// Reset for the next operation (if the object is recycled)
|
||||||
$this->m_iInitialMemory = $iCurrentMemory;
|
$this->m_iInitialMemory = $iCurrentMemory;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_null($aNewEntry))
|
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
|
||||||
|
// Invoke extensions to log the KPI operation
|
||||||
|
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||||
|
foreach(MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance)
|
||||||
|
{
|
||||||
|
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||||
|
$oKPILogData = new KpiLogData(
|
||||||
|
KpiLogData::TYPE_REPORT,
|
||||||
|
'Step',
|
||||||
|
$sOperationDesc,
|
||||||
|
$fStarted,
|
||||||
|
$fStopped,
|
||||||
|
$sExtension,
|
||||||
|
$iInitialMemory,
|
||||||
|
$iCurrentMemory,
|
||||||
|
$iPeakMemory);
|
||||||
|
$oExtensionInstance->LogOperation($oKPILogData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_null($aNewEntry) && self::$m_bGenerateLegacyReport)
|
||||||
{
|
{
|
||||||
self::$m_aExecData[] = $aNewEntry;
|
self::$m_aExecData[] = $aNewEntry;
|
||||||
}
|
}
|
||||||
$this->ResetCounters();
|
$this->ResetCounters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function ComputeStatsForExtension($object, $sMethod)
|
||||||
|
{
|
||||||
|
if (!self::IsEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
|
||||||
|
if (utils::StartsWith($sSignature, '[')) {
|
||||||
|
$this->ComputeStats('Extension', $sSignature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function ComputeStats($sOperation, $sArguments)
|
public function ComputeStats($sOperation, $sArguments)
|
||||||
{
|
{
|
||||||
|
if (!self::IsEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self::$m_bEnabled_Duration)
|
if (self::$m_bEnabled_Duration)
|
||||||
{
|
{
|
||||||
$fStopped = MyHelpers::getmicrotime();
|
$fStopped = MyHelpers::getmicrotime();
|
||||||
$fDuration = $fStopped - $this->m_fStarted;
|
$fDuration = $fStopped - $this->m_fStarted;
|
||||||
if (self::$m_bBlameCaller)
|
$aCallstack = [];
|
||||||
{
|
if (self::$m_bGenerateLegacyReport) {
|
||||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
if (self::$m_bBlameCaller) {
|
||||||
'time' => $fDuration,
|
$aCallstack = MyHelpers::get_callstack(1);
|
||||||
'callers' => MyHelpers::get_callstack(1),
|
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||||
);
|
'time' => $fDuration,
|
||||||
}
|
'callers' => $aCallstack,
|
||||||
else
|
];
|
||||||
{
|
} else {
|
||||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||||
'time' => $fDuration
|
'time' => $fDuration
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||||
|
$iCurrentMemory = 0;
|
||||||
|
$iPeakMemory = 0;
|
||||||
|
if (self::$m_bEnabled_Memory)
|
||||||
|
{
|
||||||
|
$iCurrentMemory = self::memory_get_usage();
|
||||||
|
$iPeakMemory = self::memory_get_peak_usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoke extensions to log the KPI operation
|
||||||
|
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||||
|
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||||
|
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||||
|
$oKPILogData = new KpiLogData(
|
||||||
|
KpiLogData::TYPE_STATS,
|
||||||
|
$sOperation,
|
||||||
|
$sArguments,
|
||||||
|
$this->m_fStarted,
|
||||||
|
$fStopped,
|
||||||
|
$sExtension,
|
||||||
|
$iInitialMemory,
|
||||||
|
$iCurrentMemory,
|
||||||
|
$iPeakMemory,
|
||||||
|
$aCallstack);
|
||||||
|
$oExtensionInstance->LogOperation($oKPILogData);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function ResetCounters()
|
protected function ResetCounters()
|
||||||
@@ -370,35 +496,7 @@ class ExecutionKPI
|
|||||||
|
|
||||||
static protected function memory_get_usage()
|
static protected function memory_get_usage()
|
||||||
{
|
{
|
||||||
if (function_exists('memory_get_usage'))
|
return memory_get_usage(true);
|
||||||
{
|
|
||||||
return memory_get_usage(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from the PHP manual
|
|
||||||
//
|
|
||||||
//If its Windows
|
|
||||||
//Tested on Win XP Pro SP2. Should work on Win 2003 Server too
|
|
||||||
//Doesn't work for 2000
|
|
||||||
//If you need it to work for 2000 look at http://us2.php.net/manual/en/function.memory-get-usage.php#54642
|
|
||||||
if (substr(PHP_OS,0,3) == 'WIN')
|
|
||||||
{
|
|
||||||
$output = array();
|
|
||||||
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST', $output);
|
|
||||||
|
|
||||||
return preg_replace( '/[\D]/', '', $output[5] ) * 1024;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//We now assume the OS is UNIX
|
|
||||||
//Tested on Mac OS X 10.4.6 and Linux Red Hat Enterprise 4
|
|
||||||
//This should work on most UNIX systems
|
|
||||||
$pid = getmypid();
|
|
||||||
exec("ps -eo%mem,rss,pid | grep $pid", $output);
|
|
||||||
$output = explode(" ", $output[0]);
|
|
||||||
//rss is given in 1024 byte units
|
|
||||||
return $output[1] * 1024;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static public function memory_get_peak_usage($bRealUsage = false)
|
static public function memory_get_peak_usage($bRealUsage = false)
|
||||||
|
|||||||
@@ -741,7 +741,9 @@ class ToolsLog extends LogAPI
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @see \CMDBSource::LogDeadLock()
|
* @see \CMDBSource::LogDeadLock()
|
||||||
* @since 2.7.1
|
* @since 2.7.1 PR #139
|
||||||
|
*
|
||||||
|
* @link https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks.html
|
||||||
*/
|
*/
|
||||||
class DeadLockLog extends LogAPI
|
class DeadLockLog extends LogAPI
|
||||||
{
|
{
|
||||||
@@ -761,14 +763,15 @@ class DeadLockLog extends LogAPI
|
|||||||
parent::Enable($sTargetFile);
|
parent::Enable($sTargetFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @noinspection PhpUnreachableStatementInspection */
|
||||||
private static function GetChannelFromMysqlErrorNo($iMysqlErrorNo)
|
private static function GetChannelFromMysqlErrorNo($iMysqlErrorNo)
|
||||||
{
|
{
|
||||||
switch ($iMysqlErrorNo)
|
switch ($iMysqlErrorNo)
|
||||||
{
|
{
|
||||||
case 1205:
|
case CMDBSource::MYSQL_ERRNO_WAIT_TIMEOUT:
|
||||||
return self::CHANNEL_WAIT_TIMEOUT;
|
return self::CHANNEL_WAIT_TIMEOUT;
|
||||||
break;
|
break;
|
||||||
case 1213:
|
case CMDBSource::MYSQL_ERRNO_DEADLOCK:
|
||||||
return self::CHANNEL_DEADLOCK_FOUND;
|
return self::CHANNEL_DEADLOCK_FOUND;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1213,8 +1213,10 @@ abstract class MetaModel
|
|||||||
*
|
*
|
||||||
* @return AttributeDefinition[]
|
* @return AttributeDefinition[]
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
|
*
|
||||||
|
* @see GetAttributesList for attcode list
|
||||||
*/
|
*/
|
||||||
final static public function ListAttributeDefs($sClass)
|
final public static function ListAttributeDefs($sClass)
|
||||||
{
|
{
|
||||||
self::_check_subclass($sClass);
|
self::_check_subclass($sClass);
|
||||||
return self::$m_aAttribDefs[$sClass];
|
return self::$m_aAttribDefs[$sClass];
|
||||||
@@ -1223,8 +1225,10 @@ abstract class MetaModel
|
|||||||
/**
|
/**
|
||||||
* @param string $sClass
|
* @param string $sClass
|
||||||
*
|
*
|
||||||
* @return array
|
* @return string[] list of attcodes
|
||||||
* @throws \CoreException
|
* @throws \CoreException
|
||||||
|
*
|
||||||
|
* @see ListAttributeDefs to get AttributeDefinition array instead
|
||||||
*/
|
*/
|
||||||
final public static function GetAttributesList($sClass)
|
final public static function GetAttributesList($sClass)
|
||||||
{
|
{
|
||||||
@@ -2778,7 +2782,23 @@ abstract class MetaModel
|
|||||||
|
|
||||||
// Build the list of available extensions
|
// Build the list of available extensions
|
||||||
//
|
//
|
||||||
$aInterfaces = array('iApplicationUIExtension', 'iPreferencesExtension', 'iApplicationObjectExtension', 'iLoginFSMExtension', 'iLoginUIExtension', 'iLogoutExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider', 'iModuleExtension');
|
$aInterfaces = [
|
||||||
|
'iLoginFSMExtension',
|
||||||
|
'iLogoutExtension',
|
||||||
|
'iLoginUIExtension',
|
||||||
|
'iPreferencesExtension',
|
||||||
|
'iApplicationUIExtension',
|
||||||
|
'iApplicationObjectExtension',
|
||||||
|
'iPopupMenuExtension',
|
||||||
|
'iPageUIExtension',
|
||||||
|
'iPortalUIExtension',
|
||||||
|
'iQueryModifier',
|
||||||
|
'iOnClassInitialization',
|
||||||
|
'iModuleExtension',
|
||||||
|
'iKPILoggerExtension',
|
||||||
|
'ModuleHandlerApiInterface',
|
||||||
|
'iNewsroomProvider',
|
||||||
|
];
|
||||||
foreach($aInterfaces as $sInterface)
|
foreach($aInterfaces as $sInterface)
|
||||||
{
|
{
|
||||||
self::$m_aExtensionClasses[$sInterface] = array();
|
self::$m_aExtensionClasses[$sInterface] = array();
|
||||||
@@ -6348,7 +6368,9 @@ abstract class MetaModel
|
|||||||
|
|
||||||
ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration'));
|
ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration'));
|
||||||
ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory'));
|
ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory'));
|
||||||
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
|
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
|
||||||
|
ExecutionKPI::SetGenerateLegacyReport(self::$m_oConfig->Get('log_kpi_generate_legacy_report'));
|
||||||
|
ExecutionKPI::SetSlowQueries(self::$m_oConfig->Get('log_kpi_slow_queries'));
|
||||||
|
|
||||||
self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write');
|
self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write');
|
||||||
self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys');
|
self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys');
|
||||||
@@ -6485,6 +6507,7 @@ abstract class MetaModel
|
|||||||
|
|
||||||
CMDBSource::InitFromConfig(self::$m_oConfig);
|
CMDBSource::InitFromConfig(self::$m_oConfig);
|
||||||
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
|
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
|
||||||
|
ExecutionKPI::InitStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6704,7 +6727,13 @@ abstract class MetaModel
|
|||||||
|
|
||||||
if ($bMustBeFound && empty($aRow))
|
if ($bMustBeFound && empty($aRow))
|
||||||
{
|
{
|
||||||
throw new CoreException("No result for the single row query: '$sSQL'");
|
$sNotFoundErrorMessage = "No result for the single row query";
|
||||||
|
IssueLog::Info($sNotFoundErrorMessage, LogChannels::CMDB_SOURCE, [
|
||||||
|
'class' => $sClass,
|
||||||
|
'key' => $iKey,
|
||||||
|
'sql_query' => $sSQL,
|
||||||
|
]);
|
||||||
|
throw new CoreException($sNotFoundErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $aRow;
|
return $aRow;
|
||||||
@@ -6784,28 +6813,21 @@ abstract class MetaModel
|
|||||||
* $bMustBeFound=false)
|
* $bMustBeFound=false)
|
||||||
* @throws CoreException if no result found and $bMustBeFound=true
|
* @throws CoreException if no result found and $bMustBeFound=true
|
||||||
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
|
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
|
||||||
* @throws \Exception
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
|
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
|
||||||
{
|
{
|
||||||
$oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
|
$oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
|
||||||
|
|
||||||
if (empty($oObject))
|
if (empty($oObject)) {
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
{
|
return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $oObject;
|
return $oObject;
|
||||||
|
|||||||
@@ -22,7 +22,9 @@
|
|||||||
* A class to serialize the execution of some code sections
|
* A class to serialize the execution of some code sections
|
||||||
* Emulates the API of PECL Mutex class
|
* Emulates the API of PECL Mutex class
|
||||||
* Relies on MySQL locks because the API sem_get is not always present in the
|
* Relies on MySQL locks because the API sem_get is not always present in the
|
||||||
* installed PHP.
|
* installed PHP.
|
||||||
|
*
|
||||||
|
* @link https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html MySQL locking functions documentation
|
||||||
*
|
*
|
||||||
* @copyright Copyright (C) 2013-2018 Combodo SARL
|
* @copyright Copyright (C) 2013-2018 Combodo SARL
|
||||||
* @license http://opensource.org/licenses/AGPL-3.0
|
* @license http://opensource.org/licenses/AGPL-3.0
|
||||||
@@ -257,7 +259,7 @@ class iTopMutex
|
|||||||
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
|
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
|
||||||
|
|
||||||
if (!$this->hDBLink) {
|
if (!$this->hDBLink) {
|
||||||
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
|
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
|
||||||
|
|||||||
@@ -34,7 +34,9 @@
|
|||||||
*/
|
*/
|
||||||
class ObjectResult
|
class ObjectResult
|
||||||
{
|
{
|
||||||
public $code;
|
use SanitizeTrait;
|
||||||
|
|
||||||
|
public $code;
|
||||||
public $message;
|
public $message;
|
||||||
public $class;
|
public $class;
|
||||||
public $key;
|
public $key;
|
||||||
@@ -122,6 +124,19 @@ class ObjectResult
|
|||||||
{
|
{
|
||||||
$this->fields[$sAttCode] = $this->MakeResultValue($oObject, $sAttCode, $bExtendedOutput);
|
$this->fields[$sAttCode] = $this->MakeResultValue($oObject, $sAttCode, $bExtendedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function SanitizeContent()
|
||||||
|
{
|
||||||
|
foreach($this->fields as $sFieldAttCode => $fieldValue)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$oAttDef = MetaModel::GetAttributeDef($this->class, $sFieldAttCode);
|
||||||
|
} catch (Exception $e) { // for special cases like ID
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$this->SanitizeFieldIfSensitive($this->fields, $sFieldAttCode, $fieldValue, $oAttDef);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -181,6 +196,16 @@ class RestResultWithObjects extends RestResult
|
|||||||
$sObjKey = get_class($oObject).'::'.$oObject->GetKey();
|
$sObjKey = get_class($oObject).'::'.$oObject->GetKey();
|
||||||
$this->objects[$sObjKey] = $oObjRes;
|
$this->objects[$sObjKey] = $oObjRes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function SanitizeContent()
|
||||||
|
{
|
||||||
|
parent::SanitizeContent();
|
||||||
|
|
||||||
|
foreach($this->objects as $sObjKey => $oObjRes)
|
||||||
|
{
|
||||||
|
$oObjRes->SanitizeContent();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RestResultWithRelations extends RestResultWithObjects
|
class RestResultWithRelations extends RestResultWithObjects
|
||||||
@@ -247,9 +272,10 @@ class RestDelete
|
|||||||
*
|
*
|
||||||
* @package Core
|
* @package Core
|
||||||
*/
|
*/
|
||||||
class CoreServices implements iRestServiceProvider
|
class CoreServices implements iRestServiceProvider, iRestInputSanitizer
|
||||||
{
|
{
|
||||||
/**
|
use SanitizeTrait;
|
||||||
|
/**
|
||||||
* Enumerate services delivered by this class
|
* Enumerate services delivered by this class
|
||||||
*
|
*
|
||||||
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
* @param string $sVersion The version (e.g. 1.0) supported by the services
|
||||||
@@ -663,6 +689,33 @@ class CoreServices implements iRestServiceProvider
|
|||||||
}
|
}
|
||||||
return $oResult;
|
return $oResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function SanitizeJsonInput(string $sJsonInput): string
|
||||||
|
{
|
||||||
|
$sSanitizedJsonInput = $sJsonInput;
|
||||||
|
$aJsonData = json_decode($sSanitizedJsonInput, true);
|
||||||
|
$sOperation = $aJsonData['operation'];
|
||||||
|
|
||||||
|
switch ($sOperation) {
|
||||||
|
case 'core/check_credentials':
|
||||||
|
if (isset($aJsonData['password'])) {
|
||||||
|
$aJsonData['password'] = '*****';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'core/update':
|
||||||
|
case 'core/create':
|
||||||
|
default :
|
||||||
|
$sClass = $aJsonData['class'];
|
||||||
|
if (isset($aJsonData['fields'])) {
|
||||||
|
foreach ($aJsonData['fields'] as $sFieldAttCode => $fieldValue) {
|
||||||
|
$oAttDef = MetaModel::GetAttributeDef($sClass, $sFieldAttCode);
|
||||||
|
$this->SanitizeFieldIfSensitive($aJsonData['fields'], $sFieldAttCode, $fieldValue, $oAttDef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return json_encode($aJsonData, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper for object deletion
|
* Helper for object deletion
|
||||||
@@ -802,3 +855,50 @@ class CoreServices implements iRestServiceProvider
|
|||||||
return $iLimit * max(0, $iPage - 1);
|
return $iLimit * max(0, $iPage - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait SanitizeTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Sanitize a field if it is sensitive.
|
||||||
|
*
|
||||||
|
* @param array $fields The fields array
|
||||||
|
* @param string $sFieldAttCode The attribute code
|
||||||
|
* @param mixed $oAttDef The attribute definition
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function SanitizeFieldIfSensitive(array &$fields, string $sFieldAttCode, $fieldValue, $oAttDef): void
|
||||||
|
{
|
||||||
|
// for simple attribute
|
||||||
|
if ($oAttDef instanceof iAttributeNoGroupBy) // iAttributeNoGroupBy is equivalent to sensitive attribute
|
||||||
|
{
|
||||||
|
$fields[$sFieldAttCode] = '*****';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// for 1-n / n-n relation
|
||||||
|
if ($oAttDef instanceof AttributeLinkedSet) {
|
||||||
|
foreach ($fieldValue as $i => $aLnkValues) {
|
||||||
|
foreach ($aLnkValues as $sLnkAttCode => $sLnkValue) {
|
||||||
|
$oLnkAttDef = MetaModel::GetAttributeDef($oAttDef->GetLinkedClass(), $sLnkAttCode);
|
||||||
|
if ($oLnkAttDef instanceof iAttributeNoGroupBy) { // 1-n relation
|
||||||
|
$fields[$sFieldAttCode][$i][$sLnkAttCode] = '*****';
|
||||||
|
}
|
||||||
|
elseif ($oAttDef instanceof AttributeLinkedSetIndirect && $oLnkAttDef instanceof AttributeExternalField) { // for n-n relation
|
||||||
|
$oExtKeyAttDef = MetaModel::GetAttributeDef($oLnkAttDef->GetTargetClass(), $oLnkAttDef->GetExtAttCode());
|
||||||
|
if ($oExtKeyAttDef instanceof iAttributeNoGroupBy) {
|
||||||
|
$fields[$sFieldAttCode][$i][$sLnkAttCode] = '*****';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for external attribute
|
||||||
|
if ($oAttDef instanceof AttributeExternalField) {
|
||||||
|
$oExtKeyAttDef = MetaModel::GetAttributeDef($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
|
||||||
|
if ($oExtKeyAttDef instanceof iAttributeNoGroupBy) {
|
||||||
|
$fields[$sFieldAttCode] = '*****';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -537,7 +537,7 @@ EOF
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
|
throw new Exception('graphviz not found');
|
||||||
}
|
}
|
||||||
return $sHtml;
|
return $sHtml;
|
||||||
}
|
}
|
||||||
@@ -592,7 +592,7 @@ EOF
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
|
throw new Exception('graphviz not found');
|
||||||
}
|
}
|
||||||
return $sHtml;
|
return $sHtml;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -126,7 +126,9 @@ abstract class Trigger extends cmdbAbstractObject
|
|||||||
$oAction = MetaModel::GetObject('Action', $iActionId);
|
$oAction = MetaModel::GetObject('Action', $iActionId);
|
||||||
if ($oAction->IsActive())
|
if ($oAction->IsActive())
|
||||||
{
|
{
|
||||||
|
$oKPI = new ExecutionKPI();
|
||||||
$oAction->DoExecute($this, $aContextArgs);
|
$oAction->DoExecute($this, $aContextArgs);
|
||||||
|
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ abstract class UserRightsAddOnAPI
|
|||||||
$oSearchSharers->AllowAllData();
|
$oSearchSharers->AllowAllData();
|
||||||
$oSearchSharers->AddCondition_ReferencedBy($oShareSearch, 'sharing_org_id');
|
$oSearchSharers->AddCondition_ReferencedBy($oShareSearch, 'sharing_org_id');
|
||||||
$aSharers = array();
|
$aSharers = array();
|
||||||
foreach($oSearchSharers->ToDataArray(array('id')) as $aRow)
|
foreach($oSearchSharers->SelectAttributeToArray('id') as $aRow)
|
||||||
{
|
{
|
||||||
$aSharers[] = $aRow['id'];
|
$aSharers[] = $aRow['id'];
|
||||||
}
|
}
|
||||||
@@ -135,7 +135,7 @@ abstract class UserRightsAddOnAPI
|
|||||||
$oOrgField = new FieldExpression('org_id', $sShareClass);
|
$oOrgField = new FieldExpression('org_id', $sShareClass);
|
||||||
$oSearchShares->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
|
$oSearchShares->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
|
||||||
$aShared = array();
|
$aShared = array();
|
||||||
foreach($oSearchShares->ToDataArray(array($sShareAttCode)) as $aRow)
|
foreach($oSearchShares->SelectAttributeToArray($sShareAttCode) as $aRow)
|
||||||
{
|
{
|
||||||
$aShared[] = $aRow[$sShareAttCode];
|
$aShared[] = $aRow[$sShareAttCode];
|
||||||
}
|
}
|
||||||
@@ -817,6 +817,9 @@ class UserRights
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string connected {@see User} login field value, otherwise empty string
|
||||||
|
*/
|
||||||
public static function GetUser()
|
public static function GetUser()
|
||||||
{
|
{
|
||||||
if (is_null(self::$m_oUser))
|
if (is_null(self::$m_oUser))
|
||||||
@@ -829,7 +832,9 @@ class UserRights
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** User */
|
/**
|
||||||
|
* @return User|null
|
||||||
|
*/
|
||||||
public static function GetUserObject()
|
public static function GetUserObject()
|
||||||
{
|
{
|
||||||
if (is_null(self::$m_oUser))
|
if (is_null(self::$m_oUser))
|
||||||
@@ -1029,7 +1034,7 @@ class UserRights
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sClass
|
* @param string $sClass
|
||||||
* @param int $iActionCode
|
* @param int $iActionCode see UR_ACTION_* constants
|
||||||
* @param DBObjectSet $oInstanceSet
|
* @param DBObjectSet $oInstanceSet
|
||||||
* @param User $oUser
|
* @param User $oUser
|
||||||
* @return int (UR_ALLOWED_YES|UR_ALLOWED_NO|UR_ALLOWED_DEPENDS)
|
* @return int (UR_ALLOWED_YES|UR_ALLOWED_NO|UR_ALLOWED_DEPENDS)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
|
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
|
||||||
$version: "v2.7.9";
|
$version: "v2.7.12";
|
||||||
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||||
|
|
||||||
// Base colors
|
// Base colors
|
||||||
|
|||||||
@@ -15,10 +15,14 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
*/
|
*/
|
||||||
|
/* integrityCheck: begin (do not remove/edit) */
|
||||||
/* Helpers classes */
|
/* Helpers classes */
|
||||||
.center {
|
.center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
/* Animations */
|
/* Animations */
|
||||||
@keyframes progress_bar_color_ongoing {
|
@keyframes progress_bar_color_ongoing {
|
||||||
from {
|
from {
|
||||||
@@ -265,4 +269,4 @@ fieldset > legend {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: #e60000b8;
|
color: #e60000b8;
|
||||||
}
|
}
|
||||||
|
/* integrityCheck: end (do not remove/edit) */
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ $progress-bar-error-bg-color: #F56565 !default;
|
|||||||
.center {
|
.center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Animations */
|
/* Animations */
|
||||||
@keyframes progress_bar_color_ongoing {
|
@keyframes progress_bar_color_ongoing {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'authent-cas/2.7.9',
|
'authent-cas/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
|
|
||||||
protected function OnReadCredentials(&$iErrorCode)
|
protected function OnReadCredentials(&$iErrorCode)
|
||||||
{
|
{
|
||||||
|
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||||
|
// Not allowed if not already connected
|
||||||
|
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($_SESSION['login_mode']) || ($_SESSION['login_mode'] == 'cas'))
|
if (!isset($_SESSION['login_mode']) || ($_SESSION['login_mode'] == 'cas'))
|
||||||
{
|
{
|
||||||
static::InitCASClient();
|
static::InitCASClient();
|
||||||
@@ -71,7 +76,8 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$_SESSION['login_mode'] = 'cas';
|
|
||||||
|
$_SESSION['login_mode'] = 'cas';
|
||||||
phpCAS::forceAuthentication(); // Redirect to CAS and exit
|
phpCAS::forceAuthentication(); // Redirect to CAS and exit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +86,7 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
|
|
||||||
protected function OnCheckCredentials(&$iErrorCode)
|
protected function OnCheckCredentials(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'cas')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'cas')
|
||||||
{
|
{
|
||||||
if (!isset($_SESSION['auth_user']))
|
if (!isset($_SESSION['auth_user']))
|
||||||
{
|
{
|
||||||
@@ -97,7 +103,7 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
|
|
||||||
protected function OnCredentialsOK(&$iErrorCode)
|
protected function OnCredentialsOK(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'cas')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'cas')
|
||||||
{
|
{
|
||||||
$sAuthUser = $_SESSION['auth_user'];
|
$sAuthUser = $_SESSION['auth_user'];
|
||||||
if (!LoginWebPage::CheckUser($sAuthUser))
|
if (!LoginWebPage::CheckUser($sAuthUser))
|
||||||
@@ -112,9 +118,15 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
|
|
||||||
protected function OnError(&$iErrorCode)
|
protected function OnError(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'cas')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'cas')
|
||||||
{
|
{
|
||||||
unset($_SESSION['phpCAS']);
|
unset($_SESSION['phpCAS']);
|
||||||
|
|
||||||
|
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||||
|
// don't display the login page
|
||||||
|
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
if ($iErrorCode != LoginWebPage::EXIT_CODE_MISSINGLOGIN)
|
if ($iErrorCode != LoginWebPage::EXIT_CODE_MISSINGLOGIN)
|
||||||
{
|
{
|
||||||
$oLoginWebPage = new LoginWebPage();
|
$oLoginWebPage = new LoginWebPage();
|
||||||
@@ -127,7 +139,7 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
|
|
||||||
protected function OnConnected(&$iErrorCode)
|
protected function OnConnected(&$iErrorCode)
|
||||||
{
|
{
|
||||||
if ($_SESSION['login_mode'] == 'cas')
|
if (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'cas')
|
||||||
{
|
{
|
||||||
$_SESSION['can_logoff'] = true;
|
$_SESSION['can_logoff'] = true;
|
||||||
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
return LoginWebPage::CheckLoggedUser($iErrorCode);
|
||||||
@@ -156,7 +168,7 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
|||||||
{
|
{
|
||||||
phpCAS::setLogger(new CASLogger(APPROOT.'log/cas.log'));
|
phpCAS::setLogger(new CASLogger(APPROOT.'log/cas.log'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize phpCAS
|
// Initialize phpCAS
|
||||||
$sCASVersion = Config::Get('cas_version');
|
$sCASVersion = Config::Get('cas_version');
|
||||||
$sCASHost = Config::Get('cas_host');
|
$sCASHost = Config::Get('cas_host');
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'authent-external/2.7.9',
|
'authent-external/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ if (function_exists('ldap_connect'))
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'authent-ldap/2.7.9',
|
'authent-ldap/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'authent-local/2.7.9',
|
'authent-local/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'combodo-db-tools/2.7.9',
|
'combodo-db-tools/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-attachments/2.7.9',
|
'itop-attachments/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -53,14 +53,7 @@ class DBRestore extends DBBackup
|
|||||||
$sUser = self::EscapeShellArg($this->sDBUser);
|
$sUser = self::EscapeShellArg($this->sDBUser);
|
||||||
$sPwd = self::EscapeShellArg($this->sDBPwd);
|
$sPwd = self::EscapeShellArg($this->sDBPwd);
|
||||||
$sDBName = self::EscapeShellArg($this->sDBName);
|
$sDBName = self::EscapeShellArg($this->sDBName);
|
||||||
if (empty($this->sMySQLBinDir))
|
$sMySQLExe = DBBackup::MakeSafeMySQLCommand($this->sMySQLBinDir, 'mysql');
|
||||||
{
|
|
||||||
$sMySQLExe = 'mysql';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$sMySQLExe = '"'.$this->sMySQLBinDir.'/mysql"';
|
|
||||||
}
|
|
||||||
if (is_null($this->iDBPort))
|
if (is_null($this->iDBPort))
|
||||||
{
|
{
|
||||||
$sPortOption = '';
|
$sPortOption = '';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-backup/2.7.9',
|
'itop-backup/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -56,15 +56,7 @@ try
|
|||||||
//
|
//
|
||||||
$sMySQLBinDir = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '');
|
$sMySQLBinDir = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '');
|
||||||
$sMySQLBinDir = utils::ReadParam('mysql_bindir', $sMySQLBinDir, true);
|
$sMySQLBinDir = utils::ReadParam('mysql_bindir', $sMySQLBinDir, true);
|
||||||
if (empty($sMySQLBinDir))
|
$sMySQLDump = DBBackup::MakeSafeMySQLCommand($sMySQLBinDir, 'mysqldump');
|
||||||
{
|
|
||||||
$sMySQLDump = 'mysqldump';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//echo 'Info - Found mysql_bindir: '.$sMySQLBinDir;
|
|
||||||
$sMySQLDump = '"'.$sMySQLBinDir.'/mysqldump"';
|
|
||||||
}
|
|
||||||
$sCommand = "$sMySQLDump -V 2>&1";
|
$sCommand = "$sMySQLDump -V 2>&1";
|
||||||
|
|
||||||
$aOutput = array();
|
$aOutput = array();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-bridge-virtualization-storage/2.7.9',
|
'itop-bridge-virtualization-storage/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-change-mgmt-itil/2.7.9',
|
'itop-change-mgmt-itil/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-change-mgmt/2.7.9',
|
'itop-change-mgmt/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-config-mgmt/2.7.9',
|
'itop-config-mgmt/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-config/2.7.9',
|
'itop-config/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-core-update/2.7.9',
|
'itop-core-update/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-datacenter-mgmt/2.7.9',
|
'itop-datacenter-mgmt/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-endusers-devices/2.7.9',
|
'itop-endusers-devices/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-files-information/2.7.9',
|
'itop-files-information/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-full-itil/2.7.9',
|
'itop-full-itil/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
19
datamodels/2.x/itop-hub-connector/TokenValidation.php
Normal file
19
datamodels/2.x/itop-hub-connector/TokenValidation.php
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class TokenValidation
|
||||||
|
{
|
||||||
|
// construct function
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public function isSetupTokenValid($sParamToken) : bool
|
||||||
|
{
|
||||||
|
if (!file_exists(APPROOT.'data/.setup')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$sSetupToken = trim(file_get_contents(APPROOT.'data/.setup'));
|
||||||
|
unlink(APPROOT.'data/.setup');
|
||||||
|
return $sParamToken === $sSetupToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ class HubConnectorPage extends NiceWebPage
|
|||||||
parent::__construct($sTitle);
|
parent::__construct($sTitle);
|
||||||
|
|
||||||
$this->no_cache();
|
$this->no_cache();
|
||||||
$this->add_xframe_options();
|
$this->add_http_headers();
|
||||||
|
|
||||||
$sImagesDir = utils::GetAbsoluteUrlAppRoot().'images';
|
$sImagesDir = utils::GetAbsoluteUrlAppRoot().'images';
|
||||||
$sModuleImagesDir = utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/images';
|
$sModuleImagesDir = utils::GetAbsoluteUrlModulesRoot().'itop-hub-connector/images';
|
||||||
|
|||||||
@@ -280,6 +280,7 @@ try
|
|||||||
require_once ('hubconnectorpage.class.inc.php');
|
require_once ('hubconnectorpage.class.inc.php');
|
||||||
|
|
||||||
require_once (APPROOT.'/application/startup.inc.php');
|
require_once (APPROOT.'/application/startup.inc.php');
|
||||||
|
require_once('TokenValidation.php');
|
||||||
|
|
||||||
$sTargetRoute = utils::ReadParam('target', ''); // ||browse_extensions|deploy_extensions|
|
$sTargetRoute = utils::ReadParam('target', ''); // ||browse_extensions|deploy_extensions|
|
||||||
|
|
||||||
@@ -299,11 +300,20 @@ try
|
|||||||
case 'inform_after_setup':
|
case 'inform_after_setup':
|
||||||
// Hidden IFRAME at the end of the setup
|
// Hidden IFRAME at the end of the setup
|
||||||
require_once (APPROOT.'/application/ajaxwebpage.class.inc.php');
|
require_once (APPROOT.'/application/ajaxwebpage.class.inc.php');
|
||||||
$oPage = new NiceWebPage('');
|
|
||||||
$aDataToPost = MakeDataToPost($sTargetRoute);
|
$sParamToken = utils::ReadParam('setup_token');
|
||||||
$oPage->add('<form id="hub_launch_form" action="'.$sHubUrlStateless.'" method="post">');
|
$oTokenValidation = new TokenValidation();
|
||||||
$oPage->add('<input type="hidden" name="json" value="'.htmlentities(json_encode($aDataToPost), ENT_QUOTES, 'UTF-8').'">');
|
$bIsTokenValid = $oTokenValidation->isSetupTokenValid($sParamToken);
|
||||||
$oPage->add_ready_script('$("#hub_launch_form").submit();');
|
if (UserRights::IsAdministrator() || $bIsTokenValid) {
|
||||||
|
$oPage = new NiceWebPage('');
|
||||||
|
$aDataToPost = MakeDataToPost($sTargetRoute);
|
||||||
|
$oPage->add('<form id="hub_launch_form" action="' . $sHubUrlStateless . '" method="post">');
|
||||||
|
$oPage->add('<input type="hidden" name="json" value="' . htmlentities(json_encode($aDataToPost), ENT_QUOTES, 'UTF-8') . '">');
|
||||||
|
$oPage->add_ready_script('$("#hub_launch_form").submit();');
|
||||||
|
} else {
|
||||||
|
IssueLog::Error('TokenValidation failed on inform_after_setup page');
|
||||||
|
throw new Exception("Not allowed");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-hub-connector/2.7.9',
|
'itop-hub-connector/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-incident-mgmt-itil/2.7.9',
|
'itop-incident-mgmt-itil/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-knownerror-mgmt/2.7.9',
|
'itop-knownerror-mgmt/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ const OAuthConnect = function(sClass, sId, sAjaxUri) {
|
|||||||
function (oData) {
|
function (oData) {
|
||||||
if (oData.status === 'success') {
|
if (oData.status === 'success') {
|
||||||
oOpenSignInWindow(oData.data.authorization_url, 'OAuth authorization')
|
oOpenSignInWindow(oData.data.authorization_url, 'OAuth authorization')
|
||||||
|
} else {
|
||||||
|
alert(oData.error_description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -202,6 +202,7 @@
|
|||||||
$this->Set('refresh_token', $oAccessToken->getRefreshToken());
|
$this->Set('refresh_token', $oAccessToken->getRefreshToken());
|
||||||
}
|
}
|
||||||
$this->Set('status', 'active');
|
$this->Set('status', 'active');
|
||||||
|
$this->AllowWrite();
|
||||||
$this->DBUpdate();
|
$this->DBUpdate();
|
||||||
}
|
}
|
||||||
]]></code>
|
]]></code>
|
||||||
@@ -338,6 +339,11 @@
|
|||||||
<default_value>no</default_value>
|
<default_value>no</default_value>
|
||||||
<is_null_allowed>true</is_null_allowed>
|
<is_null_allowed>true</is_null_allowed>
|
||||||
</field>
|
</field>
|
||||||
|
<field id="tenant" xsi:type="AttributeString">
|
||||||
|
<sql>tenant</sql>
|
||||||
|
<default_value>common</default_value>
|
||||||
|
<is_null_allowed>false</is_null_allowed>
|
||||||
|
</field>
|
||||||
</fields>
|
</fields>
|
||||||
<presentation>
|
<presentation>
|
||||||
<details>
|
<details>
|
||||||
@@ -363,15 +369,18 @@
|
|||||||
<item id="redirect_url">
|
<item id="redirect_url">
|
||||||
<rank>50</rank>
|
<rank>50</rank>
|
||||||
</item>
|
</item>
|
||||||
<item id="client_id">
|
<item id="tenant">
|
||||||
<rank>60</rank>
|
<rank>60</rank>
|
||||||
</item>
|
</item>
|
||||||
<item id="client_secret">
|
<item id="client_id">
|
||||||
<rank>70</rank>
|
<rank>70</rank>
|
||||||
</item>
|
</item>
|
||||||
<item id="mailbox_list">
|
<item id="client_secret">
|
||||||
<rank>80</rank>
|
<rank>80</rank>
|
||||||
</item>
|
</item>
|
||||||
|
<item id="mailbox_list">
|
||||||
|
<rank>90</rank>
|
||||||
|
</item>
|
||||||
</items>
|
</items>
|
||||||
</item>
|
</item>
|
||||||
</items>
|
</items>
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
|||||||
'Class:OAuthClientAzure/Attribute:used_for_smtp+' => 'At least one OAuth client must have this flag to “Yes”, if you want iTop to use it for sending mails',
|
'Class:OAuthClientAzure/Attribute:used_for_smtp+' => 'At least one OAuth client must have this flag to “Yes”, if you want iTop to use it for sending mails',
|
||||||
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:yes' => 'Yes',
|
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:yes' => 'Yes',
|
||||||
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:no' => 'No',
|
'Class:OAuthClientAzure/Attribute:used_for_smtp/Value:no' => 'No',
|
||||||
|
'Class:OAuthClientAzure/Attribute:tenant' => 'Tenant',
|
||||||
|
'Class:OAuthClientAzure/Attribute:tenant+' => 'Tenant ID of the configured application. For multi-tenant application, use common.',
|
||||||
));
|
));
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-oauth-client/2.7.9',
|
'itop-oauth-client/2.7.12',
|
||||||
array(
|
array(
|
||||||
// Identification
|
// Identification
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ use cmdbAbstractObject;
|
|||||||
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||||
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory;
|
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory;
|
||||||
use Dict;
|
use Dict;
|
||||||
|
use Exception;
|
||||||
use IssueLog;
|
use IssueLog;
|
||||||
|
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||||
use MetaModel;
|
use MetaModel;
|
||||||
use utils;
|
use utils;
|
||||||
|
|
||||||
@@ -30,8 +32,13 @@ class AjaxOauthClientController extends Controller
|
|||||||
|
|
||||||
$aResult = ['status' => 'success', 'data' => []];
|
$aResult = ['status' => 'success', 'data' => []];
|
||||||
|
|
||||||
$sAuthorizationUrl = OAuthClientProviderFactory::GetAuthorizationUrl($oOAuthClient);
|
try {
|
||||||
$aResult['data']['authorization_url'] = $sAuthorizationUrl;
|
$sAuthorizationUrl = OAuthClientProviderFactory::GetAuthorizationUrl($oOAuthClient);
|
||||||
|
$aResult['data']['authorization_url'] = $sAuthorizationUrl;
|
||||||
|
} catch (Exception $oException) {
|
||||||
|
$aResult['status'] = 'error';
|
||||||
|
$aResult['error_description'] = $oException->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
$this->DisplayJSONPage($aResult);
|
$this->DisplayJSONPage($aResult);
|
||||||
}
|
}
|
||||||
@@ -64,13 +71,15 @@ class AjaxOauthClientController extends Controller
|
|||||||
}
|
}
|
||||||
if (isset($aQuery['code'])) {
|
if (isset($aQuery['code'])) {
|
||||||
$sCode = $aQuery['code'];
|
$sCode = $aQuery['code'];
|
||||||
$oAccessToken = OAuthClientProviderFactory::GetAccessTokenFromCode($oOAuthClient, $sCode);
|
try {
|
||||||
|
$oAccessToken = OAuthClientProviderFactory::GetAccessTokenFromCode($oOAuthClient, $sCode);
|
||||||
$oOAuthClient->SetAccessToken($oAccessToken);
|
$oOAuthClient->SetAccessToken($oAccessToken);
|
||||||
|
$aResult['status'] = 'success';
|
||||||
|
}
|
||||||
|
catch (IdentityProviderException $e) {
|
||||||
$aResult['status'] = 'success';
|
$aResult['status'] = 'error';
|
||||||
|
$aResult['error_description'] = $e->getMessage();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$aResult['status'] = 'error';
|
$aResult['status'] = 'error';
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
/** @noinspection PhpUnhandledExceptionInspection */
|
/** @noinspection PhpUnhandledExceptionInspection */
|
||||||
SetupWebPage::AddModule(
|
SetupWebPage::AddModule(
|
||||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||||
'itop-portal-base/2.7.9', array(
|
'itop-portal-base/2.7.12', array(
|
||||||
// Identification
|
// Identification
|
||||||
'label' => 'Portal Development Library',
|
'label' => 'Portal Development Library',
|
||||||
'category' => 'Portal',
|
'category' => 'Portal',
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ foreach ($aPortalConf['properties']['themes'] as $sKey => $value)
|
|||||||
{
|
{
|
||||||
if (!is_array($value))
|
if (!is_array($value))
|
||||||
{
|
{
|
||||||
$aPortalConf['properties']['themes'][$sKey] = $_ENV['COMBODO_ABSOLUTE_URL'].utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$value,
|
$aPortalConf['properties']['themes'][$sKey] = utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$value,
|
||||||
$aImportPaths);
|
$aImportPaths);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -72,7 +72,7 @@ foreach ($aPortalConf['properties']['themes'] as $sKey => $value)
|
|||||||
$aValues = array();
|
$aValues = array();
|
||||||
foreach ($value as $sSubValue)
|
foreach ($value as $sSubValue)
|
||||||
{
|
{
|
||||||
$aValues[] = $_ENV['COMBODO_ABSOLUTE_URL'].utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$sSubValue,
|
$aValues[] = utils::GetCSSFromSASS('env-'.utils::GetCurrentEnvironment().'/'.$sSubValue,
|
||||||
$aImportPaths);
|
$aImportPaths);
|
||||||
}
|
}
|
||||||
$aPortalConf['properties']['themes'][$sKey] = $aValues;
|
$aPortalConf['properties']['themes'][$sKey] = $aValues;
|
||||||
|
|||||||
@@ -15,6 +15,11 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with iTop. If not, see <http://www.gnu.org/licenses/>
|
# along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
|
||||||
|
p_user_profile_brick_edit_person:
|
||||||
|
path: '/user/edit_person'
|
||||||
|
defaults:
|
||||||
|
_controller: 'Combodo\iTop\Portal\Controller\UserProfileBrickController::EditPerson'
|
||||||
|
|
||||||
p_user_profile_brick:
|
p_user_profile_brick:
|
||||||
path: '/user/{sBrickId}'
|
path: '/user/{sBrickId}'
|
||||||
defaults:
|
defaults:
|
||||||
|
|||||||
@@ -1379,3 +1379,19 @@ table .group-actions {
|
|||||||
.wiki_broken_link {
|
.wiki_broken_link {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
@media print {
|
||||||
|
/* Prevent URLs from being displayed */
|
||||||
|
a[href]::after, img[src]::after {
|
||||||
|
content: none !important;
|
||||||
|
/* Force modals to be displayed one after another instead of stacked */
|
||||||
|
}
|
||||||
|
.modal.in {
|
||||||
|
position: relative;
|
||||||
|
top: unset;
|
||||||
|
z-index: unset;
|
||||||
|
overflow-y: unset;
|
||||||
|
}
|
||||||
|
#drag_overlay {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1463,3 +1463,27 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
|
|||||||
.wiki_broken_link {
|
.wiki_broken_link {
|
||||||
text-decoration: line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
/* Prevent URLs from being displayed */
|
||||||
|
a[href], img[src] {
|
||||||
|
&::after {
|
||||||
|
content: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force modals to be displayed one after another instead of stacked */
|
||||||
|
.modal {
|
||||||
|
&.in {
|
||||||
|
position: relative;
|
||||||
|
top: unset;
|
||||||
|
z-index: unset;
|
||||||
|
overflow-y: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#drag_overlay {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -49,6 +49,7 @@ use Symfony\Component\HttpFoundation\JsonResponse;
|
|||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use UnaryExpression;
|
use UnaryExpression;
|
||||||
use URLButtonItem;
|
use URLButtonItem;
|
||||||
|
use utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ManageBrickController
|
* Class ManageBrickController
|
||||||
@@ -259,6 +260,7 @@ class ManageBrickController extends BrickController
|
|||||||
'oBrick' => $oBrick,
|
'oBrick' => $oBrick,
|
||||||
'sBrickId' => $sBrickId,
|
'sBrickId' => $sBrickId,
|
||||||
'sToken' => $oExporter->SaveState(),
|
'sToken' => $oExporter->SaveState(),
|
||||||
|
'sWikiUrl' => 'https://www.itophub.io/wiki/page?id='.utils::GetItopVersionWikiSyntax().'%3Auser%3Alists#excel_export',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->render(static::EXCEL_EXPORT_TEMPLATE_PATH, $aData);
|
return $this->render(static::EXCEL_EXPORT_TEMPLATE_PATH, $aData);
|
||||||
|
|||||||
@@ -1035,7 +1035,11 @@ class ObjectController extends BrickController
|
|||||||
// When reaching to an Attachment, we have to check security on its host object instead of the Attachment itself
|
// When reaching to an Attachment, we have to check security on its host object instead of the Attachment itself
|
||||||
if ($sObjectClass === 'Attachment')
|
if ($sObjectClass === 'Attachment')
|
||||||
{
|
{
|
||||||
$oAttachment = MetaModel::GetObject($sObjectClass, $sObjectId, true, true);
|
|
||||||
|
$oAttachment = MetaModel::GetObject($sObjectClass, $sObjectId, false, true);
|
||||||
|
if ($oAttachment === null) {
|
||||||
|
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
|
||||||
|
}
|
||||||
$sHostClass = $oAttachment->Get('item_class');
|
$sHostClass = $oAttachment->Get('item_class');
|
||||||
$sHostId = $oAttachment->Get('item_id');
|
$sHostId = $oAttachment->Get('item_id');
|
||||||
}
|
}
|
||||||
@@ -1242,7 +1246,12 @@ class ObjectController extends BrickController
|
|||||||
$bIgnoreSilos = $oScopeValidator->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass);
|
$bIgnoreSilos = $oScopeValidator->IsAllDataAllowedForScope(UserRights::ListProfiles(), $sObjectClass);
|
||||||
$aParams = array('objects_id' => $aObjectIds);
|
$aParams = array('objects_id' => $aObjectIds);
|
||||||
$oSearch = DBObjectSearch::FromOQL("SELECT $sObjectClass WHERE id IN (:objects_id)");
|
$oSearch = DBObjectSearch::FromOQL("SELECT $sObjectClass WHERE id IN (:objects_id)");
|
||||||
if ($bIgnoreSilos === true)
|
if (!$oScopeValidator->AddScopeToQuery($oSearch, $sObjectClass)
|
||||||
|
) {
|
||||||
|
IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' not allowed to read ' . $sObjectClass . ' object.');
|
||||||
|
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
|
||||||
|
}
|
||||||
|
if ($bIgnoreSilos === true)
|
||||||
{
|
{
|
||||||
$oSearch->AllowAllData();
|
$oSearch->AllowAllData();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ use Symfony\Component\HttpFoundation\Response;
|
|||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
use UserRights;
|
use UserRights;
|
||||||
use utils;
|
use utils;
|
||||||
|
use Dict;
|
||||||
/**
|
/**
|
||||||
* Class UserProfileBrickController
|
* Class UserProfileBrickController
|
||||||
*
|
*
|
||||||
@@ -66,34 +66,9 @@ class UserProfileBrickController extends BrickController
|
|||||||
$oRequestManipulator = $this->get('request_manipulator');
|
$oRequestManipulator = $this->get('request_manipulator');
|
||||||
/** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $ObjectFormHandler */
|
/** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $ObjectFormHandler */
|
||||||
$ObjectFormHandler = $this->get('object_form_handler');
|
$ObjectFormHandler = $this->get('object_form_handler');
|
||||||
/** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */
|
$oBrick = $this->GetBrick($sBrickId);
|
||||||
$oBrickCollection = $this->get('brick_collection');
|
|
||||||
|
|
||||||
// If the brick id was not specified, we get the first one registered that is an instance of UserProfileBrick as default
|
$aData = array();
|
||||||
if ($sBrickId === null)
|
|
||||||
{
|
|
||||||
/** @var \Combodo\iTop\Portal\Brick\PortalBrick $oTmpBrick */
|
|
||||||
foreach ($oBrickCollection->GetBricks() as $oTmpBrick)
|
|
||||||
{
|
|
||||||
if ($oTmpBrick instanceof UserProfileBrick)
|
|
||||||
{
|
|
||||||
$oBrick = $oTmpBrick;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We make sure a UserProfileBrick was found
|
|
||||||
if (!isset($oBrick) || $oBrick === null)
|
|
||||||
{
|
|
||||||
$oBrick = new UserProfileBrick();
|
|
||||||
//throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'UserProfileBrick : Brick could not be loaded as there was no UserProfileBrick loaded in the application.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$oBrick = $oBrickCollection->GetBrickById($sBrickId);
|
|
||||||
}
|
|
||||||
|
|
||||||
$aData = array();
|
|
||||||
|
|
||||||
// Setting form mode regarding the demo mode parameter
|
// Setting form mode regarding the demo mode parameter
|
||||||
$bDemoMode = MetaModel::GetConfig()->Get('demo_mode');
|
$bDemoMode = MetaModel::GetConfig()->Get('demo_mode');
|
||||||
@@ -130,11 +105,12 @@ class UserProfileBrickController extends BrickController
|
|||||||
$oCurContact = UserRights::GetContactObject();
|
$oCurContact = UserRights::GetContactObject();
|
||||||
$sCurContactClass = get_class($oCurContact);
|
$sCurContactClass = get_class($oCurContact);
|
||||||
$sCurContactId = $oCurContact->GetKey();
|
$sCurContactId = $oCurContact->GetKey();
|
||||||
|
$aForm = $oBrick->GetForm();
|
||||||
|
$aForm['submit_endpoint'] = $this->generateUrl('p_user_profile_brick_edit_person', ['sBrickId' => $sBrickId]);
|
||||||
// Preparing forms
|
// Preparing forms
|
||||||
$aData['forms']['contact'] = $ObjectFormHandler->HandleForm($oRequest, $sFormMode, $sCurContactClass, $sCurContactId,
|
$aData['forms']['contact'] = $ObjectFormHandler->HandleForm($oRequest, $sFormMode, $sCurContactClass, $sCurContactId,
|
||||||
$oBrick->GetForm());
|
$aForm);
|
||||||
$aData['forms']['preferences'] = $this->HandlePreferencesForm($oRequest, $sFormMode);
|
$aData['forms']['preferences'] = $this->HandlePreferencesForm($oRequest, $sFormMode);
|
||||||
// - If user can change password, we display the form
|
// - If user can change password, we display the form
|
||||||
$aData['forms']['password'] = (UserRights::CanChangePassword()) ? $this->HandlePasswordForm($oRequest, $sFormMode) : null;
|
$aData['forms']['password'] = (UserRights::CanChangePassword()) ? $this->HandlePasswordForm($oRequest, $sFormMode) : null;
|
||||||
|
|
||||||
@@ -150,6 +126,35 @@ class UserProfileBrickController extends BrickController
|
|||||||
return $oResponse;
|
return $oResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function EditPerson(Request $oRequest)
|
||||||
|
{
|
||||||
|
/** @var \Combodo\iTop\Portal\Helper\ObjectFormHandlerHelper $oObjectFormHandler */
|
||||||
|
$oObjectFormHandler = $this->get('object_form_handler');
|
||||||
|
/** @var \Combodo\iTop\Portal\Helper\SecurityHelper $oSecurityHelper */
|
||||||
|
$oSecurityHelper = $this->get('security_helper');
|
||||||
|
|
||||||
|
$oCurContact = UserRights::GetContactObject();
|
||||||
|
$sObjectClass = get_class($oCurContact);
|
||||||
|
$sObjectId = $oCurContact->GetKey();
|
||||||
|
|
||||||
|
// Checking security layers
|
||||||
|
// Warning : This is a dirty quick fix to allow editing its own contact information
|
||||||
|
$bAllowWrite = ($sObjectClass === 'Person' && $sObjectId == UserRights::GetContactId());
|
||||||
|
if (!$oSecurityHelper->IsActionAllowed(UR_ACTION_MODIFY, $sObjectClass, $sObjectId) && !$bAllowWrite) {
|
||||||
|
IssueLog::Warning(__METHOD__ . ' at line ' . __LINE__ . ' : User #' . UserRights::GetUserId() . ' not allowed to modify ' . $sObjectClass . '::' . $sObjectId . ' object.');
|
||||||
|
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$aForm = $this->GetBrick()->GetForm();
|
||||||
|
$aForm['submit_endpoint'] = $this->generateUrl('p_user_profile_brick_edit_person');
|
||||||
|
|
||||||
|
$aData = ['sMode' => 'edit'];
|
||||||
|
$aData['form'] = $oObjectFormHandler->HandleForm($oRequest, $aData['sMode'], $sObjectClass, $sObjectId, $aForm);
|
||||||
|
|
||||||
|
return new JsonResponse($aData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param \Symfony\Component\HttpFoundation\Request $oRequest
|
* @param \Symfony\Component\HttpFoundation\Request $oRequest
|
||||||
* @param string $sFormMode
|
* @param string $sFormMode
|
||||||
@@ -388,4 +393,34 @@ class UserProfileBrickController extends BrickController
|
|||||||
return $aFormData;
|
return $aFormData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $sBrickId
|
||||||
|
* @return \Combodo\iTop\Portal\Brick\PortalBrick|UserProfileBrick
|
||||||
|
* @throws \Combodo\iTop\Portal\Brick\BrickNotFoundException
|
||||||
|
*/
|
||||||
|
public function GetBrick($sBrickId = null)
|
||||||
|
{
|
||||||
|
/** @var \Combodo\iTop\Portal\Brick\BrickCollection $oBrickCollection */
|
||||||
|
$oBrickCollection = $this->get('brick_collection');
|
||||||
|
|
||||||
|
// If the brick id was not specified, we get the first one registered that is an instance of UserProfileBrick as default
|
||||||
|
if ($sBrickId === null) {
|
||||||
|
/** @var \Combodo\iTop\Portal\Brick\PortalBrick $oTmpBrick */
|
||||||
|
foreach ($oBrickCollection->GetBricks() as $oTmpBrick) {
|
||||||
|
if ($oTmpBrick instanceof UserProfileBrick) {
|
||||||
|
$oBrick = $oTmpBrick;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We make sure a UserProfileBrick was found
|
||||||
|
if (!isset($oBrick) || $oBrick === null) {
|
||||||
|
$oBrick = new UserProfileBrick();
|
||||||
|
//throw new HttpException(Response::HTTP_INTERNAL_SERVER_ERROR, 'UserProfileBrick : Brick could not be loaded as there was no UserProfileBrick loaded in the application.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$oBrick = $oBrickCollection->GetBrickById($sBrickId);
|
||||||
|
}
|
||||||
|
return $oBrick;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ use Symfony\Component\HttpFoundation\Response;
|
|||||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||||
use UserRights;
|
use UserRights;
|
||||||
|
use utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class UserProvider
|
* Class UserProvider
|
||||||
@@ -91,6 +92,9 @@ class UserProvider implements ContainerAwareInterface
|
|||||||
}
|
}
|
||||||
$this->oContainer->set('combodo.current_user', $oUser);
|
$this->oContainer->set('combodo.current_user', $oUser);
|
||||||
|
|
||||||
|
// User allowed to log off or not
|
||||||
|
$this->oContainer->set('combodo.current_user.can_logoff', utils::CanLogOff());
|
||||||
|
|
||||||
// Allowed portals
|
// Allowed portals
|
||||||
$aAllowedPortals = UserRights::GetAllowedPortals();
|
$aAllowedPortals = UserRights::GetAllowedPortals();
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -132,10 +132,8 @@ class ObjectFormHandlerHelper
|
|||||||
$bModal = ($oRequest->isXmlHttpRequest() && empty($sOperation));
|
$bModal = ($oRequest->isXmlHttpRequest() && empty($sOperation));
|
||||||
|
|
||||||
// - Retrieve form properties
|
// - Retrieve form properties
|
||||||
if ($aFormProperties === null)
|
$aFormProperties = $aFormProperties ?? ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
|
||||||
{
|
|
||||||
$aFormProperties = ApplicationHelper::GetLoadedFormFromClass($this->aCombodoPortalInstanceConf['forms'], $sObjectClass, $sMode);
|
|
||||||
}
|
|
||||||
// - Create and
|
// - Create and
|
||||||
if (empty($sOperation))
|
if (empty($sOperation))
|
||||||
{
|
{
|
||||||
@@ -243,13 +241,17 @@ class ObjectFormHandlerHelper
|
|||||||
case static::ENUM_MODE_CREATE:
|
case static::ENUM_MODE_CREATE:
|
||||||
case static::ENUM_MODE_EDIT:
|
case static::ENUM_MODE_EDIT:
|
||||||
case static::ENUM_MODE_VIEW:
|
case static::ENUM_MODE_VIEW:
|
||||||
$sFormEndpoint = $this->oUrlGenerator->generate(
|
if(array_key_exists('submit_endpoint', $aFormProperties)) {
|
||||||
'p_object_'.$sMode,
|
$sFormEndpoint = $aFormProperties['submit_endpoint'];
|
||||||
array(
|
} else {
|
||||||
'sObjectClass' => $sObjectClass,
|
$sFormEndpoint = $this->oUrlGenerator->generate(
|
||||||
'sObjectId' => $sObjectId,
|
'p_object_' . $sMode,
|
||||||
)
|
array(
|
||||||
);
|
'sObjectClass' => $sObjectClass,
|
||||||
|
'sObjectId' => $sObjectId,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case static::ENUM_MODE_APPLY_STIMULUS:
|
case static::ENUM_MODE_APPLY_STIMULUS:
|
||||||
@@ -282,7 +284,8 @@ class ObjectFormHandlerHelper
|
|||||||
->SetActionRulesToken($sActionRulesToken)
|
->SetActionRulesToken($sActionRulesToken)
|
||||||
->SetRenderer($oFormRenderer)
|
->SetRenderer($oFormRenderer)
|
||||||
->SetFormProperties($aFormProperties);
|
->SetFormProperties($aFormProperties);
|
||||||
|
$oFormManager->PrepareFormAndHTMLDocument();
|
||||||
|
$oFormManager->PrepareFields();
|
||||||
$oFormManager->Build();
|
$oFormManager->Build();
|
||||||
$aFormData['hidden_fields'] = $oFormManager->GetHiddenFieldsId();
|
$aFormData['hidden_fields'] = $oFormManager->GetHiddenFieldsId();
|
||||||
// Check the number of editable fields
|
// Check the number of editable fields
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="export-feedback">
|
<div id="export-feedback">
|
||||||
|
<p id="export-excel-warning" class="alert alert-warning" role="alert">{{ 'UI:Bulk:Export:MaliciousInjection:Alert:Message'|dict_format(sWikiUrl)|raw }}</p>
|
||||||
<p class="export-message" style="text-align:center;">{{ 'ExcelExport:PreparingExport'|dict_s }}</p>
|
<p class="export-message" style="text-align:center;">{{ 'ExcelExport:PreparingExport'|dict_s }}</p>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
<div class="progress-bar" role="progressbar" style="width: 0%"
|
<div class="progress-bar" role="progressbar" style="width: 0%"
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
|
|
||||||
{% if app['combodo.current_user'] is defined and app['combodo.current_user'] is not null %}
|
{% if app['combodo.current_user'] is defined and app['combodo.current_user'] is not null %}
|
||||||
{% set bUserConnected = true %}
|
{% set bUserConnected = true %}
|
||||||
|
{% set bUserCanLogOff = app['combodo.current_user.can_logoff'] %}
|
||||||
{% set sUserFullname = app['combodo.current_user'].Get('first_name') ~ ' ' ~ app['combodo.current_user'].Get('last_name') %}
|
{% set sUserFullname = app['combodo.current_user'].Get('first_name') ~ ' ' ~ app['combodo.current_user'].Get('last_name') %}
|
||||||
{% set sUserEmail = app['combodo.current_user'].Get('email') %}
|
{% set sUserEmail = app['combodo.current_user'].Get('email') %}
|
||||||
{% set sUserPhotoUrl = app['combodo.current_contact.photo_url'] %}
|
{% set sUserPhotoUrl = app['combodo.current_contact.photo_url'] %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% set bUserConnected = false %}
|
{% set bUserConnected = false %}
|
||||||
|
{% set bUserCanLogOff = false %}
|
||||||
{% set sUserFullname = '' %}
|
{% set sUserFullname = '' %}
|
||||||
{% set sUserEmail = '' %}
|
{% set sUserEmail = '' %}
|
||||||
{% set sUserPhotoUrl = app['combodo.portal.base.absolute_url'] ~ 'img/user-profile-default-256px.png' %}
|
{% set sUserPhotoUrl = app['combodo.portal.base.absolute_url'] ~ 'img/user-profile-default-256px.png' %}
|
||||||
@@ -51,9 +53,9 @@
|
|||||||
<link href="{{ app['combodo.absolute_url'] ~ 'css/c3.min.css'|add_itop_version }}" rel="stylesheet">
|
<link href="{{ app['combodo.absolute_url'] ~ 'css/c3.min.css'|add_itop_version }}" rel="stylesheet">
|
||||||
<link href="{{ app['combodo.absolute_url'] ~ 'js/ckeditor/plugins/codesnippet/lib/highlight/styles/obsidian.css'|add_itop_version }}" rel="stylesheet">
|
<link href="{{ app['combodo.absolute_url'] ~ 'js/ckeditor/plugins/codesnippet/lib/highlight/styles/obsidian.css'|add_itop_version }}" rel="stylesheet">
|
||||||
{# - Bootstrap theme #}
|
{# - Bootstrap theme #}
|
||||||
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.bootstrap|add_itop_version }}" rel="stylesheet" id="css_bootstrap_theme">
|
<link href="{{ app['combodo.absolute_url'] ~ app['combodo.portal.instance.conf'].properties.themes.bootstrap|add_itop_version }}" rel="stylesheet" id="css_bootstrap_theme">
|
||||||
{# - Portal adjustments for BS theme #}
|
{# - Portal adjustments for BS theme #}
|
||||||
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.portal|add_itop_version }}" rel="stylesheet" id="css_portal">
|
<link href="{{ app['combodo.absolute_url'] ~ app['combodo.portal.instance.conf'].properties.themes.portal|add_itop_version }}" rel="stylesheet" id="css_portal">
|
||||||
{# UI Extensions CSS, in an undefined order #}
|
{# UI Extensions CSS, in an undefined order #}
|
||||||
{% if app['ui_extensions_helper'].css_files is defined %}
|
{% if app['ui_extensions_helper'].css_files is defined %}
|
||||||
{% for css_file in app['ui_extensions_helper'].css_files %}
|
{% for css_file in app['ui_extensions_helper'].css_files %}
|
||||||
@@ -62,12 +64,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{# Custom CSS that is supposed to do adjustments to the portal #}
|
{# Custom CSS that is supposed to do adjustments to the portal #}
|
||||||
{% if app['combodo.portal.instance.conf'].properties.themes.custom is defined %}
|
{% if app['combodo.portal.instance.conf'].properties.themes.custom is defined %}
|
||||||
<link href="{{ app['combodo.portal.instance.conf'].properties.themes.custom|add_itop_version }}" rel="stylesheet">
|
<link href="{{ app['combodo.absolute_url'] ~ app['combodo.portal.instance.conf'].properties.themes.custom|add_itop_version }}" rel="stylesheet">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# Others CSS that will come after the theme/portal/custom, in an undefined order #}
|
{# Others CSS that will come after the theme/portal/custom, in an undefined order #}
|
||||||
{% if app['combodo.portal.instance.conf'].properties.themes.others is defined %}
|
{% if app['combodo.portal.instance.conf'].properties.themes.others is defined %}
|
||||||
{% for theme in app['combodo.portal.instance.conf'].properties.themes.others %}
|
{% for theme in app['combodo.portal.instance.conf'].properties.themes.others %}
|
||||||
<link href="{{ theme|add_itop_version }}" rel="stylesheet">
|
<link href="{{ app['combodo.absolute_url'] ~ theme|add_itop_version }}" rel="stylesheet">
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -230,11 +232,13 @@
|
|||||||
<li><a href="{{ aPortal.url }}" target="_blank"><span class="brick_icon {{ sIconClass }} fa-2x fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
|
<li><a href="{{ aPortal.url }}" target="_blank"><span class="brick_icon {{ sIconClass }} fa-2x fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
{% if bUserCanLogOff %}
|
||||||
{% if app['combodo.current_user.allowed_portals']|length > 1 %}
|
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||||
<li role="separator" class="divider"></li>
|
{% if app['combodo.current_user.allowed_portals']|length > 1 %}
|
||||||
{% endif %}
|
<li role="separator" class="divider"></li>
|
||||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-2x fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
{% endif %}
|
||||||
|
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-2x fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -269,11 +273,13 @@
|
|||||||
<li><a href="{{ aPortal.url }}" {% if app['combodo.portal.instance.conf'].properties.allowed_portals.opening_mode == 'tab' %}target="_blank"{% endif %} title="{{ aPortal.label|dict_s }}"><span class="brick_icon {{ sGlyphiconClass }} fa-lg fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
|
<li><a href="{{ aPortal.url }}" {% if app['combodo.portal.instance.conf'].properties.allowed_portals.opening_mode == 'tab' %}target="_blank"{% endif %} title="{{ aPortal.label|dict_s }}"><span class="brick_icon {{ sGlyphiconClass }} fa-lg fa-fw"></span>{{ aPortal.label|dict_s }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
{% if bUserCanLogOff %}
|
||||||
{% if app['combodo.current_user.allowed_portals']|length > 1 %}
|
{# We display the separator only if the user has more then 1 portal. Meaning default portal + console or several portal at least #}
|
||||||
<li role="separator" class="divider"></li>
|
{% if app['combodo.current_user.allowed_portals']|length > 1 %}
|
||||||
{% endif %}
|
<li role="separator" class="divider"></li>
|
||||||
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-lg fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
{% endif %}
|
||||||
|
<li><a href="{{ app['combodo.absolute_url'] }}pages/logoff.php"><span class="brick_icon fas fa-sign-out-alt fa-lg fa-fw"></span>{{ 'Brick:Portal:UserProfile:Navigation:Dropdown:Logout'|dict_s }}</a></li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -458,8 +464,8 @@
|
|||||||
sBody = '{{ 'Error:XHR:Fail'|dict_format(constant('ITOP_APPLICATION_SHORT'))|escape('js') }}';
|
sBody = '{{ 'Error:XHR:Fail'|dict_format(constant('ITOP_APPLICATION_SHORT'))|escape('js') }}';
|
||||||
}
|
}
|
||||||
var oModalElem = $('#modal-for-alert');
|
var oModalElem = $('#modal-for-alert');
|
||||||
oModalElem.find('.modal-content .modal-header .modal-title').html(sTitle);
|
oModalElem.find('.modal-content .modal-header .modal-title').text(sTitle);
|
||||||
oModalElem.find('.modal-content .modal-body .alert').addClass('alert-danger').html(sBody);
|
oModalElem.find('.modal-content .modal-body .alert').addClass('alert-danger').text(sBody);
|
||||||
oModalElem.modal('show');
|
oModalElem.modal('show');
|
||||||
};
|
};
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user