Compare commits

..

14 Commits

Author SHA1 Message Date
odain
fce1348d44 N°9104 - Be able to trigger cron remotely and asynchronously 2026-03-04 14:54:56 +01:00
odain
14d748aa97 rename cron scripts 2026-03-02 10:06:33 +01:00
odain
6f79e1842c ci : warum 2026-02-27 18:15:52 +01:00
odain
ecd4d77483 enable logs + ci debugging 2026-02-27 16:53:10 +01:00
odain
1a83149f14 ci test sdk: SaveItopConfFile method becomes protected 2026-02-27 16:28:43 +01:00
odain
828dbcf9b5 temp work 2026-02-27 09:40:08 +01:00
odain
e4b8f31af0 phpstan fix + add log 2026-02-26 08:46:06 +01:00
odain
393643b6f9 call cron asynchronously + tests 2026-02-25 17:28:15 +01:00
odain
259c82b88e temp 2026-02-24 17:04:03 +01:00
odain
0001f078c3 temp work 2026-02-24 17:04:02 +01:00
odain
a7ebf30746 be able to call cron locally/remotely via any authentication mode that implements iTokenLoginUIExtension (interface badly named) 2026-02-24 17:03:56 +01:00
odain
d92bdf7b89 new iTokenLoginUIExtension interface 2026-02-24 17:00:56 +01:00
odain
399f5bc449 cron endpoints behind login mechanism via auth_user and auth_pwd + deal with status_only different output 2026-02-24 17:00:56 +01:00
odain
d65412d61c be able to call cron asynchronously 2026-02-24 17:00:56 +01:00
1202 changed files with 10150 additions and 17559 deletions

View File

@@ -1,59 +0,0 @@
name: "Bug report"
description: "Report a bug that you identified in iTop, with the steps to reproduce it and the expected vs actual behavior. If you have an improvement proposition, please use the 'Enhancement suggestion' template instead."
body:
- type: markdown
attributes:
value: |
Please explain why you're creating this issue :
- Are you willing to create a PR for the bug fix ? If so, we'll indicate in the issue if we're interested in it.
- Then, please describe how to reproduce the issue.
- type: dropdown
id: willing_to_pr
attributes:
label: Are you willing to create (at a later stage) a PR for that?
options:
- Yes
- No
validations:
required: true
- type: input
id: itop_version
attributes:
label: iTop version
description: "Complete iTop version (e.g., 3.2.3)"
validations:
required: false
- type: input
id: php_version
attributes:
label: PHP version
description: "Complete PHP version (e.g., 8.4.20)"
validations:
required: false
- type: textarea
id: reproduction_steps
attributes:
label: Reproduction procedure
description: |
Please 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.
placeholder: |
1. First go there
2. Then do that
3. ...
4. Finally, see that... (what is expected and what is actually happening)
validations:
required: false
- type: upload
id: additional_info
attributes:
label: Additional information (if needed)
description: "Add/drag and drop screenshots, logs or any files that can be relevant for your issue."
validations:
required: false
accept: ".png, .jpg, .jpeg, .gif, .webp, .log, .txt, .json, .csv, .xml, .zip, .tar.gz"

View File

@@ -1,54 +0,0 @@
name: "Enhancement suggestion"
description: "Suggest an improvement to iTop, with a clear description of the expected behavior and the benefits it would bring. If you identified a bug and have an improvement proposition, please use the 'Bug report' template instead."
body:
- type: markdown
attributes:
value: |
Please explain why you're creating this issue :
- Are you willing to create a PR for this enhancement ? If so, we'll indicate in the issue if we're interested in it.
- Then, please describe what's your improvement proposition.
- type: dropdown
id: willing_to_pr
attributes:
label: Are you willing to create (at a later stage) a PR for that?
options:
- Yes
- No
validations:
required: true
- type: input
id: itop_version
attributes:
label: iTop version
description: "Complete iTop version (e.g., 3.2.3)"
validations:
required: false
- type: input
id: php_version
attributes:
label: PHP version
description: "Complete PHP version (e.g., 8.4.20)"
validations:
required: false
- type: textarea
id: enhancement_details
attributes:
label: Enhancement details
description: |
Please explain what you want to improve, and your proposition to make it better.
If it requires a custom datamodel, provide the minimal XML delta to reproduce it.
validations:
required: false
- type: upload
id: additional_info
attributes:
label: Additional information (if needed)
description: "Add/drag and drop screenshots, logs or any files that can be relevant for your issue."
validations:
required: false
accept: ".png, .jpg, .jpeg, .gif, .webp, .log, .txt, .json, .csv, .xml, .zip, .tar.gz"

View File

@@ -1,76 +1,83 @@
<!--
IMPORTANT: Before creating your PR, please create an issue first to know if Combodo is interested in your contribution (not needed for translations PR).
Since we may refuse a PR, it's preferable to create an issue first, to avoid spending time coding something that won't be accepted.
Once you've done it, and we confirmed we're interested in it, please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
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
| Question | Answer |
|---------------------------------------------------------------------------------|--------------------------------------|
| Related to a SourceForge thread / Another PR / A GitHub Issue / 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 many details as possible.
- 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)
## Reproduction procedure (bug)
<!--
Please explain step by step how to reproduce the issue on a standard iTop Community.
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) -->
3. First go there
4. Then do that
5. ...
6. Finally, see that... (what is expected and what is actually happening)
2. First go there
2. Then do that
3. ...
4. Finally, see that...
## Reproduction procedure (enhancement - if needed)
<!--
Please explain how we can reproduce the feature/behavior you want to improve, and what's your proposition to make it better.
Add screenshots if it's related to UI.
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
-->
## 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)
## 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
## 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 without digging in the code?
- [ ] 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
- ...
-->
- [ ] ...
- [ ] ...
- [ ] ...

3
.gitignore vendored
View File

@@ -58,9 +58,6 @@ tests/*/vendor/*
/tests/php-unit-tests/phpunit.xml
/tests/php-unit-tests/postbuild_integration.xml
# PHP CS Fixer: Cache file
/.php-cs-fixer.cache
# Jetbrains
/.idea/**

View File

@@ -4,33 +4,30 @@ You want to contribute to iTop? Many thanks to you! 🎉 👍
Here are some guidelines that will help us integrate your work!
## Contributions
### Subjects
You are welcome to create pull requests on any of those subjects:
* 🐛 bug fix
* 🌐 translation / i18n / l10n
* 🚸 enhancement
But before creating a PR, please [create a corresponding issue][itop-issues] for review.
We should review within two weeks, and get back to you to indicate if we're interested in your proposal or not.
If you don't create an issue, you won't know if we're interested in your contribution, and you may spend time coding something that won't be accepted.
If you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the issue.
If you want to implement a **new feature**, please [create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) for review.
If you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the ticket.
For all **security related subjects**, please see our [security policy](SECURITY.md).
All **datamodel modification** should be done in an extension. Beware that such change would
impact all existing customers, and could prevent them from upgrading!
Combodo has a long experience of datamodel changes: they are very disruptive!
All **datamodel modification** should be done in an extension. Beware that such change would
impact all existing customers, and could prevent them from
upgrading!
Combodo has a long experience of datamodel changes: they are very disruptive!
This is why we avoid them in iTop core, especially the changes on existing objects/fields.
If you have an idea you're sure would benefit to all of iTop users, you may
[create a corresponding issue][itop-issues] to submit it, but be warned that there are lots of good
If you have an idea you're sure would benefit to all of iTop users, you may
[create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) to submit it, but be warned that there are lots of good
reasons to refuse such changes.
### 📄 License and copyright
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file).
The iTop repository is divided in three parts: iTop (mainly PHP/JS/XML sources and dictionaries), images, and third-party libraries.
@@ -40,33 +37,48 @@ Anyhow, you are encouraged to signal your contribution by the mean of `@author`
If you want to use another license or keep the code ownership (copyright), you may [create an extension][wiki new ext].
[license.txt]: https://github.com/Combodo/iTop/blob/develop/license.txt
[itop-issues]: https://github.com/Combodo/iTop/issues
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
## 🔀 iTop branch model
When we first start with Git, we were using the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model. As
there was some confusions about branches to use for current developed release and previous maintained release, and also because we were
using just a very few of the GitFlow commands, we decided to add just a little modification to this branch model : since April 2020
we don't have a `master` branch anymore.
there was some confusions about branches to use for current developed release and previous maintained release, and also because we were
using just a very few of the GitFlow commands, we decided to add just a little modification to this branch model : since april 2020
we don't have a `master` branch anymore.
Here are the branches we use and their meaning :
Here are the branches we use and their meaning :
- `develop`: ongoing development version
- `release/*`: if present, that means we are working on a alpha/beta/rc version for shipping
- `support/*`: maintenance branches for older versions
For example, if no version is currently prepared for shipping we could have:
- `develop` containing future 3.3.0 version
- `support/3.2`: 3.2.x maintenance version
- `develop` containing future 3.1.0 version
- `support/3.0`: 3.0.x maintenance version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
And when 3.3.0 will be out:
In this example, when 3.1.0-beta is shipped that will become:
- `develop`: future 3.2.0 version
- `release/3.1.0`: 3.1.0-beta
- `support/3.0`: 3.0.x maintenance version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
And when 3.1.0 final will be out:
- `develop`: future 3.2.0 version
- `support/3.1`: 3.1.x maintenance version (will host developments for 3.1.1)
- `support/3.0`: 3.0.x maintenance version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
Also note that we have a "micro-version" concept : each of those versions have a very small amount of modifications. They are made from
`support/*` branches as well. For example 2.6.2-1 and 2.6.2-2 were made from the `support/2.6.2` branch.
- `develop`: future 3.4.0 version
- `support/3.3`: 3.3.x maintenance version (will host developments for 3.3.1)
- `support/3.2`: 3.2.x maintenance version
## Coding
@@ -80,11 +92,12 @@ A [dedicated page](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3A
2. Create a branch in this fork, based on the develop branch
3. Code !
Do create a dedicated branch for each modification you want to propose : if you don't, it will be very hard to merge back your work !
Do create a dedicated branch for each modification you want to propose : if you don't it will be very hard to merge back your work !
Most of the time you should base your developments on the develop branch.
Most of the time you should based your developments on the develop branch.
That may be different if you want to fix a bug, please use develop anyway and ask in your PR if rebase is possible.
### 🎨 PHP styleguide
Please follow [our guidelines](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Acoding_standards).
@@ -93,7 +106,7 @@ Please follow [our guidelines](https://www.itophub.io/wiki/page?id=latest%3Acust
Please create tests that covers as much as possible the code you're submitting.
Our tests are located in the `tests/` directory, containing a PHPUnit config file : `phpunit.xml.dist`.
Our tests are located in the `test/` directory, containing a PHPUnit config file : `phpunit.xml.dist`.
### Git Commit Messages
@@ -125,14 +138,14 @@ When your code is working, please:
* Rebase your branch on our repo last commit,
* Create a pull request. _Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)_.
* Pull request description: mind to add all the information useful to understand why you're suggesting this modification and anything necessary to dive into your work. Especially:
- Bugfixes: exact steps to reproduce the bug (given/when/then), description of the bug cause and what solution is implemented
- Enhancements: use cases, implementation details if needed
* Mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option ! (note that if you are working with an org fork, this
option [won't be available](https://github.com/orgs/community/discussions/5634))
- Bugfixes: exact steps to reproduce the bug (given/when/then), description of the bug cause and what solution is implemented
- Enhancements: use cases, implementation details if needed
* Mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option ! (note that if you are working with an org fork, this option [won't be available](https://github.com/orgs/community/discussions/5634))
## 🙏 We are thankful
We are thankful for all your contributions to the iTop universe! As a thank-you gift, we will send stickers to every iTop (& extensions) contributors!
We are thankful for all your contributions to the iTop universe! As a thank you gift, we will send stickers to every iTop (& extensions) contributors!
We have one sticker per contribution type. You might get multiple stickers with one contribution though :)
@@ -144,4 +157,8 @@ We have one sticker per contribution type. You might get multiple stickers with
* Graduated: Follow a Combodo's iTop training
* Ambassador: Outstanding community contributors
* Beta tester: Test and give feedback on beta releases
* Extension developer: Develop and publish an extension
* Extension developer: Develop and publish an extension
Here is the design of each stickers for year 2024:
![iTop stickers 2024](.doc/contributing-guide/2024.contributing-stickers-side-by-side.png)

View File

@@ -0,0 +1,23 @@
<?php
/**
* Login page extensibility
*
* @api
* @package UIExtensibilityAPI
* @since 3.x.x
*/
interface iTokenLoginUIExtension
{
/**
* @return array
* @api
*/
public function GetTokenInfo(): array;
/**
* @return array
* @api
*/
public function GetUserLogin(array $aTokenInfo): string;
}

View File

@@ -810,7 +810,6 @@ HTML
foreach ($aNotificationClasses as $sNotifClass) {
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN (:triggers) AND Ev.object_id = :id");
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
$aNotifSearches[$sNotifClass]->AllowAllData();
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], []);
$iNotifsCount += $oNotifSet->Count();
}
@@ -824,7 +823,6 @@ HTML
'menu' => false,
'panel_title' => MetaModel::GetName($sNotifClass),
'panel_icon' => MetaModel::GetClassIcon($sNotifClass, false),
'display_unauthorized_objects' => true,
]);
}
}

View File

@@ -724,10 +724,6 @@ class DisplayBlock
}
}
if (!$this->m_oFilter->IsAllDataAllowed() && ($aExtraParams['display_unauthorized_objects'] ?? false) === true) {
$this->m_oFilter->AllowAllData();
}
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams);
}
@@ -1385,10 +1381,7 @@ JS
// Check the classes that can be read (i.e authorized) by this user...
foreach ($aClasses as $sAlias => $sClassName) {
if (
(UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) !== UR_ALLOWED_NO)
|| ($aExtraParams['display_unauthorized_objects'] ?? false) === true
) {
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) != UR_ALLOWED_NO) {
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}

View File

@@ -9,7 +9,7 @@ use Combodo\iTop\Application\Helper\Session;
* @license http://opensource.org/licenses/AGPL-3.0
*/
class LoginBasic extends AbstractLoginFSMExtension
class LoginBasic extends AbstractLoginFSMExtension implements iTokenLoginUIExtension
{
/**
* Return the list of supported login modes for this plugin
@@ -120,4 +120,20 @@ class LoginBasic extends AbstractLoginFSMExtension
}
return [$sAuthUser, $sAuthPwd];
}
public function GetTokenInfo(): array
{
return $this->GetAuthUserAndPassword();
}
public function GetUserLogin(array $aTokenInfo): string
{
$sLogin = $aTokenInfo[0];
$sLoginMode = 'basic';
if (UserRights::CheckCredentials($sLogin, $aTokenInfo[1], $sLoginMode, 'internal')) {
return $sLogin;
}
throw new Exception("Cannot CheckCredentials user login ($sLogin) with ($sLoginMode) mode");
}
}

View File

@@ -75,10 +75,13 @@ class LoginExternal extends AbstractLoginFSMExtension
}
/**
* @return bool|mixed
* @return bool
*/
private function GetAuthUser()
{
return MetaModel::GetConfig()->GetExternalAuthenticationVariable();
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value
/** @var string $sAuthUser */
return $sAuthUser; // Retrieve the value
}
}

View File

@@ -12,7 +12,7 @@ use Combodo\iTop\Application\Helper\Session;
*
* @since 2.7.0
*/
class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension, iTokenLoginUIExtension
{
private $bForceFormOnError = false;
@@ -32,8 +32,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
protected function OnReadCredentials(&$iErrorCode)
{
if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'form') {
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
list($sAuthUser, $sAuthPwd) = $this->GetTokenInfo();
if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd)) {
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER)) {
// X-Combodo-Ajax is a special header automatically added to all ajax requests
@@ -65,8 +64,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'form') {
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
list($sAuthUser, $sAuthPwd) = $this->GetTokenInfo();
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
@@ -145,4 +143,23 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
return $oLoginContext;
}
public function GetTokenInfo(): array
{
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
return [$sAuthUser, $sAuthPwd];
}
public function GetUserLogin(array $aTokenInfo): string
{
$sLogin = $aTokenInfo[0];
$sLoginMode = 'form';
if (UserRights::CheckCredentials($sLogin, $aTokenInfo[1], $sLoginMode, 'internal')) {
return $sLogin;
}
throw new Exception("Cannot CheckCredentials user login ($sLogin) with ($sLoginMode) mode");
}
}

View File

@@ -9,7 +9,7 @@ use Combodo\iTop\Application\Helper\Session;
* @license http://opensource.org/licenses/AGPL-3.0
*/
class LoginURL extends AbstractLoginFSMExtension
class LoginURL extends AbstractLoginFSMExtension implements iTokenLoginUIExtension
{
/**
* @var bool
@@ -29,9 +29,8 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnModeDetection(&$iErrorCode)
{
if (!Session::IsSet('login_mode') && !$this->bErrorOccurred) {
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (!empty($sAuthUser) && !empty($sAuthPwd)) {
list($sAuthUser, $sAuthPwd) = $this->GetTokenInfo();
{
Session::Set('login_mode', 'url');
}
}
@@ -49,8 +48,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url') {
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
list($sAuthUser, $sAuthPwd) = $this->GetTokenInfo();
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
@@ -84,4 +82,22 @@ class LoginURL extends AbstractLoginFSMExtension
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
public function GetTokenInfo(): array
{
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
return [$sAuthUser, $sAuthPwd];
}
public function GetUserLogin(array $aTokenInfo): string
{
$sLogin = $aTokenInfo[0];
$sLoginMode = 'url';
if (UserRights::CheckCredentials($sLogin, $aTokenInfo[1], $sLoginMode, 'internal')) {
return $sLogin;
}
throw new Exception("Cannot CheckCredentials user login ($sLogin) with ($sLoginMode) mode");
}
}

View File

@@ -530,6 +530,23 @@ class LoginWebPage extends NiceWebPage
return $aPlugins;
}
public static function GetCurrentLoginPlugin(string $sCurrentLoginMode): iLoginExtension
{
/** @var iLoginExtension $oLoginExtensionInstance */
foreach (MetaModel::EnumPlugins('iLoginFSMExtension') as $oLoginExtensionInstance) {
$aLoginModes = $oLoginExtensionInstance->ListSupportedLoginModes();
$aLoginModes = (is_array($aLoginModes) ? $aLoginModes : []);
foreach ($aLoginModes as $sLoginMode) {
// Keep only the plugins for the current login mode + before + after
if ($sLoginMode == $sCurrentLoginMode) {
return $oLoginExtensionInstance;
}
}
}
throw new \Exception("should not happen");
}
/**
* Advance Login Finite State Machine to the next step
*

View File

@@ -1546,21 +1546,12 @@ class ShortcutMenuNode extends MenuNode
public function GetHyperlink($aExtraParams)
{
$sContext = $this->oShortcut->Get('context');
try {
$aContext = utils::Unserialize($sContext);
if (isset($aContext['menu'])) {
unset($aContext['menu']);
}
foreach ($aContext as $sArgName => $sArgValue) {
$aExtraParams[$sArgName] = $sArgValue;
}
} catch (Exception $e) {
IssueLog::Warning("User shortcut corrupted, delete the shortcut", LogChannels::CONSOLE, [
'shortcut_name' => $this->oShortcut->GetName(),
'root_cause' => $e->getMessage(),
]);
// delete the shortcut
$this->oShortcut->DBDelete();
$aContext = unserialize($sContext);
if (isset($aContext['menu'])) {
unset($aContext['menu']);
}
foreach ($aContext as $sArgName => $sArgValue) {
$aExtraParams[$sArgName] = $sArgValue;
}
return parent::GetHyperlink($aExtraParams);
}

View File

@@ -936,6 +936,11 @@ CSS;
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp, $aImportsPaths)
{
$aThemeParametersVariable = [];
if (array_key_exists('variables', $aThemeParameters)) {
if (is_array($aThemeParameters['variables'])) {
$aThemeParametersVariable = array_merge([], $aThemeParameters['variables']);
}
}
if (array_key_exists('variable_imports', $aThemeParameters)) {
if (is_array($aThemeParameters['variable_imports'])) {
@@ -943,14 +948,6 @@ CSS;
}
}
// Variables defined in theme XML have the priority over variables defined in XML imports files
// They're defined after so they overwrite previous parameters
if (array_key_exists('variables', $aThemeParameters)) {
if (is_array($aThemeParameters['variables'])) {
$aThemeParametersVariable = array_merge($aThemeParametersVariable, $aThemeParameters['variables']);
}
}
$aThemeParametersVariable['$version'] = $bSetupCompilationTimestamp;
return $aThemeParametersVariable;
}
@@ -982,9 +979,7 @@ CSS;
}
}
}
array_map(function ($sVariableValue) {
return ltrim($sVariableValue);
}, $aVariablesResults);
array_map(function ($sVariableValue) { return ltrim($sVariableValue); }, $aVariablesResults);
return $aVariablesResults;
}

View File

@@ -228,7 +228,7 @@ JS
<<<HTML
<form id="ObjectsAddForm_{$this->sInputid}">
<div id="SearchResultsToAdd_{$this->sInputid}">
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
</div>
<input type="hidden" id="count_{$this->sInputid}" value="0"/>
</form>

View File

@@ -27,9 +27,6 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
class UISearchFormForeignKeys
{
private $m_sRemoteClass;
private $m_iInputId;
public function __construct($sTargetClass, $iInputId = null)
{
$this->m_sRemoteClass = $sTargetClass;
@@ -43,7 +40,7 @@ class UISearchFormForeignKeys
*
* @throws \Exception
*/
public function ShowModalSearchForeignKeys($oPage)
public function ShowModalSearchForeignKeys($oPage, $sTitle)
{
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
@@ -63,17 +60,52 @@ class UISearchFormForeignKeys
]
));
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sCancel = Dict::S('UI:Button:Cancel');
$sAdd = Dict::S('UI:Button:Add');
$oPage->add(
<<<HTML
<form id="ObjectsAddForm_{$this->m_iInputId}">
<div id="SearchResultsToAdd_{$this->m_iInputId}" style="vertical-align:top;height:100%;overflow:auto;padding:0;border:0;">
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
</div>
<input type="hidden" id="count_{$this->m_iInputId}" value="0"/>
</form>
HTML
);
$oPage->add_ready_script(
<<<JS
$('#dlg_{$this->m_iInputId}').dialog({
width: $(window).width()*0.8,
height: $(window).height()*0.8,
autoOpen: false,
modal: true,
resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes,
buttons: [
{
text: Dict.S('UI:Button:Cancel'),
class: "cancel ibo-is-alternative ibo-is-neutral",
click: function() {
$('#dlg_{$this->m_iInputId}').dialog('close');
}
},
{
text: Dict.S('UI:Button:Add'),
id: 'btn_ok_{$this->m_iInputId}',
class: "ok ibo-is-regular ibo-is-primary",
click: function() {
oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);
}
},
],
});
$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});
$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);
$('#SearchFormToAdd_{$this->m_iInputId}').on('resize', oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);
JS
);
}
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
@@ -87,4 +119,31 @@ HTML
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
}
}
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
*
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
*
* @throws \Exception
*/
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
{
if ($sRemoteClass != '') {
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
$oFilter = new DBObjectSearch($sRemoteClass);
} else {
// No remote class specified use the one defined in the linkedset
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display(
$oP,
"ResultsToAdd_{$this->m_iInputId}",
['menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"]
);
}
}

View File

@@ -122,11 +122,6 @@ class utils
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
/**
* @var string For module codes (e.g. `itop-portal-base`, `combodo-webhook-integration`, `some-module-code-x.y`, ...)
* @since 3.2.3 3.3.0 N°8554
*/
public const ENUM_SANITIZATION_FILTER_MODULE_CODE = 'module_code';
/**
* @var string
* @since 2.7.10 3.0.0
@@ -186,9 +181,6 @@ class utils
protected static function LoadParamFile($sParamFile)
{
if (utils::RealPath($sParamFile, APPROOT) !== false) {
throw new Exception("File '".utils::HtmlEntities($sParamFile)."' should be outside iTop");
}
if (!file_exists($sParamFile)) {
throw new Exception("Could not find the parameter file: '".utils::HtmlEntities($sParamFile)."'");
}
@@ -398,7 +390,6 @@ class utils
* @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
* @since 3.2.1-1 N°8242 Allow value to be an array for every filter
* @since 3.2.3 3.3.0 N°8554 new case for ENUM_SANITIZATION_FILTER_MODULE_CODE
*
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
*/
@@ -486,7 +477,7 @@ class utils
);
break;
// For XML / HTML node selector
// For XML / HTML node id selector
case static::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR:
$retValue = filter_var(
$value,
@@ -499,15 +490,6 @@ class utils
$retValue = preg_replace('/[^a-zA-Z0-9_]/', '', $value);
break;
case static::ENUM_SANITIZATION_FILTER_MODULE_CODE:
// Module codes allow all alphabets letters, numbers, dash and dot characters
$retValue = filter_var(
$value,
FILTER_VALIDATE_REGEXP,
['options' => ['regexp' => '/^[\p{L}\d.-]+$/u']]
);
break;
// For URL
case static::ENUM_SANITIZATION_FILTER_URL:
$retValue = filter_var($value, FILTER_SANITIZE_URL);
@@ -1455,12 +1437,6 @@ class utils
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
/** @var \DBObjectSet $param */
// Check if the user has the right to read the objects of this list, otherwise do not propose any action (eg. configure this list, export, etc.)
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_READ, $param) !== UR_ALLOWED_YES) {
break;
}
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink(true);
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
@@ -2102,9 +2078,7 @@ SQL;
}
// Remove any remaining nulls (for positions that weren't referenced)
$aReplacements = array_filter($aReplacements, static function ($val) {
return $val !== null;
});
$aReplacements = array_filter($aReplacements, static function ($val) { return $val !== null; });
} else {
// For non-positional, we need to map each position
$aReplacements = [];
@@ -3254,48 +3228,41 @@ TXT
}
/**
* PHP unserialize encapsulation, allow throwing exception when not allowed object class is detected (for security hardening)
* @author Ain Tohvri <https://mstdn.social/@tekkie>
*
* @param string $data data to unserialize
* @param array $aOptions PHP @unserialise options
* @param bool $bThrowNotAllowedObjectClassException flag to throw exception
*
* @return mixed PHP @unserialise return
* @throws Exception
* @since ???
*/
public static function Unserialize(string $data, array $aOptions = ['allowed_classes' => false], bool $bThrowNotAllowedObjectClassException = true): mixed
public static function ReadTail($sFilename, $iLines = 1): array
{
$data = unserialize($data, $aOptions);
if ($bThrowNotAllowedObjectClassException) {
try {
self::AssertNoIncompleteClassDetected($data);
} catch (Exception $e) {
throw new CoreException('Unserialization failed because an incomplete class was detected.', [], '', $e);
}
$handle = fopen($sFilename, "r");
if (false === $handle) {
throw new \Exception("Cannot read file $sFilename");
}
return $data;
}
/**
* Assert that data provided doesn't contain any incomplete class.
*
* @throws Exception
*/
public static function AssertNoIncompleteClassDetected(mixed $data): void
{
if (is_object($data)) {
if ($data instanceof __PHP_Incomplete_Class) {
throw new Exception('__PHP_Incomplete_Class_Name object detected');
$iLineCounter = $iLines;
$iPos = -2;
$bBeginning = false;
$aLines = [];
while ($iLineCounter > 0) {
$sChar = " ";
while ($sChar != "\n") {
if (fseek($handle, $iPos, SEEK_END) == -1) {
$bBeginning = true;
break;
}
$sChar = fgetc($handle);
$iPos--;
}
foreach (get_object_vars($data) as $property) {
self::AssertNoIncompleteClassDetected($property);
$iLineCounter--;
if ($bBeginning) {
rewind($handle);
}
} elseif (is_array($data)) {
foreach ($data as $value) {
self::AssertNoIncompleteClassDetected($value);
$aLines[$iLines - $iLineCounter - 1] = fgets($handle);
if ($bBeginning) {
break;
}
}
fclose($handle);
return array_reverse($aLines);
}
}

View File

@@ -23,7 +23,7 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.2');
* @used-by utils::GetItopVersionWikiSyntax()
* @used-by iTopModulesPhpVersionIntegrationTest
*/
define('ITOP_CORE_VERSION', '3.2.3');
define('ITOP_CORE_VERSION', '3.2.1');
/**
* @var string

View File

@@ -4,7 +4,7 @@
"type": "project",
"license": "AGPL-3.0-only",
"require": {
"php": ">=8.1.0 <8.5.0",
"php": ">=8.1.0 <8.4.0",
"ext-ctype": "*",
"ext-dom": "*",
"ext-gd": "*",
@@ -12,14 +12,15 @@
"ext-json": "*",
"ext-mysqli": "*",
"ext-soap": "*",
"apereo/phpcas": "dev-master",
"apereo/phpcas": "~1.6.0",
"firebase/php-jwt": "^6.4.0",
"guzzlehttp/guzzle": "^7.5.1",
"league/oauth2-google": "^4.0.1",
"nikic/php-parser": "^4.14.0",
"pear/archive_tar": "~1.4.14",
"pelago/emogrifier": "^7.2.0",
"psr/log": "^3.0.0",
"scssphp/scssphp": "dev-combodo/1.x",
"scssphp/scssphp": "^1.12.1",
"symfony/console": "~6.4.0",
"symfony/dotenv": "~6.4.0",
"symfony/framework-bundle": "~6.4.0",
@@ -39,16 +40,6 @@
"symfony/stopwatch": "~6.4.0",
"symfony/web-profiler-bundle": "~6.4.0"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/EsupPortail/phpCAS"
},
{
"type": "vcs",
"url": "https://github.com/combodo-itop-libs/scssphp"
}
],
"suggest": {
"ext-libsodium": "Required to use the AttributeEncryptedString.",
"ext-openssl": "Can be used as a polyfill if libsodium is not installed",

858
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -234,11 +234,10 @@ abstract class Action extends cmdbAbstractObject
}
$oActionFilter = DBObjectSearch::FromOQL($sActionQueryOql, $aActionQueryParams);
$oActionFilter->AllowAllData();
$oSet = new DBObjectSet($oActionFilter, ['date' => false]);
$sPanelTitle = Dict::Format('Action:last_executions_tab_panel_title', $sActionQueryLimit);
$oExecutionsListBlock = DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle, 'display_unauthorized_objects' => true]);
$oExecutionsListBlock = DataTableUIBlockFactory::MakeForResult($oPage, 'action_executions_list', $oSet, ['panel_title' => $sPanelTitle]);
$oPage->AddUiBlock($oExecutionsListBlock);
}

View File

@@ -4344,9 +4344,7 @@ class AttributeText extends AttributeString
} else {
$sValue = self::RenderWikiHtml($sValue, true /* wiki only */);
$sImageHtml = UserRights::IsLoggedIn() ? InlineImage::FixUrls($sValue) : InlineImage::ReplaceInlineImagesWithBase64Representation($sValue);
return "<div class=\"HTML ibo-is-html-content\" $sStyle>".$sImageHtml.'</div>';
return "<div class=\"HTML ibo-is-html-content\" $sStyle>".InlineImage::FixUrls($sValue).'</div>';
}
}
@@ -4829,7 +4827,7 @@ class AttributeCaseLog extends AttributeLongText
}
if (strlen($sIndex) > 0) {
$aIndex = utils::Unserialize($sIndex, ['allowed_classes' => false], false);
$aIndex = unserialize($sIndex);
$value = new ormCaseLog($sLog, $aIndex);
} else {
$value = new ormCaseLog($sLog);
@@ -8990,10 +8988,7 @@ class AttributeStopWatch extends AttributeDefinition
switch ($sThresholdCode) {
case 'deadline':
if ($value) {
if (is_numeric($value)) {
if (!is_int($value)) {
$value = intval($value);
}
if (is_int($value)) {
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
$sRet = AttributeDeadline::FormatDeadline($sDate);
} else {

View File

@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
*
* @see ITOP_CORE_VERSION to get iTop core version
*/
define('ITOP_VERSION', '3.2.3-dev');
define('ITOP_VERSION', '3.2.0-dev');
define('ITOP_VERSION_NAME', 'Fullmoon');
define('ITOP_REVISION', 'svn');
@@ -75,7 +75,6 @@ define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
define('DEFAULT_HASH_ALGO', PASSWORD_DEFAULT);
/**
* Config
* configuration data (this class cannot not be localized, because it is responsible for loading the dictionaries)
@@ -870,14 +869,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'ext_auth_variable' => [
'type' => 'string',
'description' => 'External authentication expression (allowed: $_SERVER[\'key\'], $_COOKIE[\'key\'], $_REQUEST[\'key\'], getallheaders()[\'Header-Name\'])',
'default' => '$_SERVER[\'REMOTE_USER\']',
'value' => '$_SERVER[\'REMOTE_USER\']',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'login_debug' => [
'type' => 'bool',
'description' => 'Activate the login FSM debug',
@@ -1612,7 +1603,7 @@ class Config
'show_in_conf_sample' => false,
],
'search_manual_submit' => [
'type' => 'bool',
'type' => 'array',
'description' => 'Force manual submit of search all requests',
'default' => false,
'value' => true,
@@ -1739,14 +1730,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.disable_joined_classes_filter' => [
'type' => 'bool',
'description' => 'If true, scope filters aren\'t applied to joined classes or union classes not directly listed in the SELECT clause.',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.hide_administrators' => [
'type' => 'bool',
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
@@ -1755,14 +1738,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.disable_exec_forced_login_for_all_enpoints' => [
'type' => 'bool',
'description' => 'If true, when no delegated authentication module is defined, no login will be forced on modules exec endpoints',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'behind_reverse_proxy' => [
'type' => 'bool',
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',
@@ -1975,6 +1950,11 @@ class Config
*/
protected $m_sAllowedLoginTypes;
/**
* @var string Name of the PHP variable in which external authentication information is passed by the web server
*/
protected $m_sExtAuthVariable;
/**
* @var string Encryption key used for all attributes of type "encrypted string". Can be set to a random value
* unless you want to import a database from another iTop instance, in which case you must use
@@ -2047,6 +2027,7 @@ class Config
$this->m_bSecureConnectionRequired = DEFAULT_SECURE_CONNECTION_REQUIRED;
$this->m_sDefaultLanguage = 'EN US';
$this->m_sAllowedLoginTypes = DEFAULT_ALLOWED_LOGIN_TYPES;
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
$this->m_aCharsets = [];
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
$this->m_iPasswordHashAlgo = DEFAULT_HASH_ALGO;
@@ -2200,6 +2181,7 @@ class Config
$this->m_sDefaultLanguage = isset($MySettings['default_language']) ? trim($MySettings['default_language']) : 'EN US';
$this->m_sAllowedLoginTypes = isset($MySettings['allowed_login_types']) ? trim($MySettings['allowed_login_types']) : DEFAULT_ALLOWED_LOGIN_TYPES;
$this->m_sExtAuthVariable = isset($MySettings['ext_auth_variable']) ? trim($MySettings['ext_auth_variable']) : DEFAULT_EXT_AUTH_VARIABLE;
$this->m_sEncryptionKey = isset($MySettings['encryption_key']) ? trim($MySettings['encryption_key']) : $this->m_sEncryptionKey;
$this->m_sEncryptionLibrary = isset($MySettings['encryption_library']) ? trim($MySettings['encryption_library']) : $this->m_sEncryptionLibrary;
$this->m_aCharsets = isset($MySettings['csv_import_charsets']) ? $MySettings['csv_import_charsets'] : [];
@@ -2352,73 +2334,9 @@ class Config
return explode('|', $this->m_sAllowedLoginTypes);
}
/**
* @return bool|mixed
* @since 3.2.3 return the parsed value instead of an unsecured variable name
*/
public function GetExternalAuthenticationVariable()
{
$sExpression = $this->Get('ext_auth_variable');
$aParsed = $this->ParseExternalAuthVariableExpression($sExpression);
if ($aParsed === null) {
return false;
}
$sKey = $aParsed['key'];
switch ($aParsed['type']) {
case 'server':
return $_SERVER[$sKey] ?? false;
case 'cookie':
return $_COOKIE[$sKey] ?? false;
case 'request':
return $_REQUEST[$sKey] ?? false;
case 'header':
if (!function_exists('getallheaders')) {
return false;
}
$aHeaders = getallheaders();
if (!is_array($aHeaders)) {
return false;
}
return $aHeaders[$sKey] ?? false;
}
return false;
}
/**
* @param $sExpression
* @return array|null
*/
private function ParseExternalAuthVariableExpression($sExpression)
{
// If it's a configuration parameter it's probably already trimmed, but just in case
$sExpression = trim((string) $sExpression);
if ($sExpression === '') {
return null;
}
// Match $_SERVER/$_COOKIE/$_REQUEST['key'] with optional whitespace and single/double quotes.
if (preg_match('/^\$_(SERVER|COOKIE|REQUEST)\s*\[\s*(["\'])\s*([^"\']+)\2\s*\]\s*$/', $sExpression, $aMatches) === 1) {
$sContext = strtoupper($aMatches[1]);
$sKey = $aMatches[3];
return [
'type' => strtolower($sContext),
'key' => $sKey,
'normalized' => '$_'.$sContext.'[\''.$sKey.'\']',
];
}
// Match getallheaders()['Header-Name'] in a case-insensitive way.
if (preg_match('/^getallheaders\(\)\s*\[\s*(["\'])\s*([^"\']+)\1\s*\]\s*$/i', $sExpression, $aMatches) === 1) {
$sKey = $aMatches[2];
return [
'type' => 'header',
'key' => $sKey,
'normalized' => 'getallheaders()[\''.$sKey.'\']',
];
}
return null;
return $this->m_sExtAuthVariable;
}
public function GetCSVImportCharsets()
@@ -2514,7 +2432,7 @@ class Config
public function SetExternalAuthenticationVariable($sExtAuthVariable)
{
$this->Set('ext_auth_variable', $sExtAuthVariable);
$this->m_sExtAuthVariable = $sExtAuthVariable;
}
public function SetEncryptionKey($sKey)
@@ -2569,6 +2487,7 @@ class Config
$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
$aSettings['default_language'] = $this->m_sDefaultLanguage;
$aSettings['allowed_login_types'] = $this->m_sAllowedLoginTypes;
$aSettings['ext_auth_variable'] = $this->m_sExtAuthVariable;
$aSettings['encryption_key'] = $this->m_sEncryptionKey;
$aSettings['encryption_library'] = $this->m_sEncryptionLibrary;
$aSettings['csv_import_charsets'] = $this->m_aCharsets;
@@ -2673,6 +2592,7 @@ class Config
$aOtherValues = [
'default_language' => $this->m_sDefaultLanguage,
'allowed_login_types' => $this->m_sAllowedLoginTypes,
'ext_auth_variable' => $this->m_sExtAuthVariable,
'encryption_key' => $this->m_sEncryptionKey,
'encryption_library' => $this->m_sEncryptionLibrary,
'csv_import_charsets' => $this->m_aCharsets,

View File

@@ -5,7 +5,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\Helper\ExportHelper;
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
@@ -14,6 +13,7 @@ use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectUIBlockFactory
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
use Combodo\iTop\Application\Helper\ExportHelper;
use Combodo\iTop\Application\WebPage\Page;
use Combodo\iTop\Application\WebPage\WebPage;
@@ -55,8 +55,6 @@ class CSVBulkExport extends TabularBulkExport
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 0, true);
$this->aStatusInfo['ignore_excel_sanitization'] = (bool)utils::ReadParam('ignore_excel_sanitization', 0, true, utils::ENUM_SANITIZATION_FILTER_INTEGER);
$sDateFormatRadio = utils::ReadParam('csv_date_format_radio', '');
switch ($sDateFormatRadio) {
case 'default':
@@ -225,10 +223,6 @@ class CSVBulkExport extends TabularBulkExport
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
$oFieldSetDate->AddSubBlock($oRadioCustom);
$oFieldSetSecurity = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:Security'));
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oFieldSetSecurity));
$oFieldSetSecurity->AddSubBlock(ExportHelper::GetInputForSanitizeExcelExport());
$oP->add_ready_script(
<<<EOF
$('#form_part_csv_options').on('preview_updated', function() { FormatDatesInPreview('csv', 'csv'); });
@@ -270,13 +264,6 @@ EOF
default:
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
}
// If the option to ignore Excel sanitization is not set or explicitly set to false, apply sanitization
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
return ExportHelper::SanitizeField($sRet, $this->aStatusInfo['text_qualifier'] ?? '');
}
// The option to ignore Excel sanitization is explicitly set to true: return the raw value without sanitization
return $sRet;
}
@@ -350,12 +337,6 @@ EOF
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput, !$this->aStatusInfo['formatted_text']);
}
}
// If the option to ignore Excel sanitization is not set or absent, sanitize the field
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
$sField = ExportHelper::SanitizeField($sField, $this->aStatusInfo['text_qualifier']);
}
if ($this->aStatusInfo['charset'] != 'UTF-8') {
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
// and thus to convert field by field and not the whole row or file at once (see ticket N°991)

View File

@@ -409,7 +409,7 @@
</php_parent>
<parent>cmdbAbstractObject</parent>
<properties>
<category>core/cmdb,grant_by_profile,silo</category>
<category>core/cmdb,view_in_gui</category>
<abstract>false</abstract>
<key_type>autoincrement</key_type>
<db_table>priv_event_newsroom</db_table>
@@ -888,7 +888,7 @@
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>Event</parent>
<properties>
<category>core/cmdb,grant_by_profile,silo</category>
<category>core/cmdb,view_in_gui</category>
</properties>
<fields>
<field id="message" xsi:type="AttributeText"/>

View File

@@ -1925,37 +1925,4 @@ class DBObjectSearch extends DBSearch
{
return $this->GetCriteria()->ListParameters();
}
/**
* @inheritDoc
* @return DBObjectSearch
*/
protected function ApplyDataFilters(): DBObjectSearch
{
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
return $this;
}
$oSearch = $this;
$aClassesToFilter = $this->GetSelectedClasses();
// Opt-in for joined classes filtering, otherwise only filter the selected class(es)
if (MetaModel::GetConfig()->Get('security.disable_joined_classes_filter') === false) {
$aClassesToFilter = $this->GetJoinedClasses();
}
// Apply filter (this is similar to the one in DBSearch but the factorization could make it less readable)
foreach ($aClassesToFilter as $sClassAlias => $sClass) {
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false) {
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
}
if (is_object($oVisibleObjects)) {
$oVisibleObjects->AllowAllData();
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
$oSearch->SetDataFiltered();
}
}
return $oSearch;
}
}

View File

@@ -1122,7 +1122,21 @@ abstract class DBSearch
*/
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null)
{
$oSearch = $this->ApplyDataFilters();
$oSearch = $this;
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered()) {
foreach ($this->GetSelectedClasses() as $sClassAlias => $sClass) {
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false) {
// Make sure this is a valid search object, saying NO for all
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
}
if (is_object($oVisibleObjects)) {
$oVisibleObjects->AllowAllData();
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
$oSearch->SetDataFiltered();
}
}
}
if (is_array($aGroupByExpr)) {
foreach ($aGroupByExpr as $sAlias => $oGroupByExp) {
@@ -1594,33 +1608,4 @@ abstract class DBSearch
* @return array{\VariableExpression}
*/
abstract public function GetExpectedArguments(): array;
/**
* Apply data filters to the search, if needed
*
* @return DBSearch
* @throws CoreException
*/
protected function ApplyDataFilters(): DBSearch
{
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
return $this;
}
$oSearch = $this;
$aClassesToFilter = $this->GetSelectedClasses();
foreach ($aClassesToFilter as $sClassAlias => $sClass) {
$oVisibleObjects = UserRights::GetSelectFilter($sClass, $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false) {
$oVisibleObjects = DBObjectSearch::FromEmptySet($sClass);
}
if (is_object($oVisibleObjects)) {
$oVisibleObjects->AllowAllData();
$oSearch = $oSearch->Filter($sClassAlias, $oVisibleObjects);
$oSearch->SetDataFiltered();
}
}
return $oSearch;
}
}

View File

@@ -673,30 +673,4 @@ class DBUnionSearch extends DBSearch
return $aVariableCriteria;
}
/**
* @inheritDoc
* @return DBUnionSearch
*/
protected function ApplyDataFilters(): DBUnionSearch
{
if ($this->IsAllDataAllowed() || $this->IsDataFiltered()) {
return $this;
}
// Opt-in for joined classes filtering, otherwise fallback on DBSearch filtering
if (MetaModel::GetConfig()->Get('security.disable_joined_classes_filter') === true) {
return parent::ApplyDataFilters();
}
// Apply filters per sub-search
$aFilteredSearches = [];
foreach ($this->GetSearches() as $oSubSearch) {
// Recursively call ApplyDataFilters on sub-searches
$aFilteredSearches[] = $oSubSearch->ApplyDataFilters();
}
$oSearch = new DBUnionSearch($aFilteredSearches);
return $oSearch;
}
}

View File

@@ -26,7 +26,7 @@ class Event extends DBObject implements iDisplay
{
$aParams =
[
"category" => "core/cmdb,grant_by_profile,silo",
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -120,7 +120,7 @@ class EventNotification extends Event
{
$aParams =
[
"category" => "core/cmdb,grant_by_profile,silo",
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -154,7 +154,7 @@ class EventNotificationEmail extends EventNotification
{
$aParams =
[
"category" => "core/cmdb,grant_by_profile,silo",
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -176,7 +176,7 @@ class EventNotificationEmail extends EventNotification
// Display lists
MetaModel::Init_SetZListItems('details', ['date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body', 'attachments']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['date', 'message', 'to', 'cc', 'bcc', 'subject', 'attachments']); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', ['date', 'message', 'to', 'subject', 'attachments']); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
@@ -190,7 +190,7 @@ class EventIssue extends Event
{
$aParams =
[
"category" => "core/cmdb,grant_by_profile,silo",
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -284,7 +284,7 @@ class EventWebService extends Event
{
$aParams =
[
"category" => "core/cmdb,grant_by_profile,silo",
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -319,7 +319,7 @@ class EventRestService extends Event
{
$aParams =
[
"category" => "core/cmdb,grant_by_profile,silo",
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -354,7 +354,7 @@ class EventLoginUsage extends Event
{
$aParams =
[
"category" => "core/cmdb,grant_by_profile,silo",
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
@@ -392,7 +392,7 @@ class EventOnObject extends Event
{
$aParams =
[
"category" => "core/cmdb,grant_by_profile,silo",
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",

View File

@@ -5,13 +5,13 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\Helper\ExportHelper;
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
use Combodo\iTop\Application\Helper\ExportHelper;
use Combodo\iTop\Application\WebPage\Page;
use Combodo\iTop\Application\WebPage\WebPage;
@@ -63,8 +63,6 @@ class ExcelBulkExport extends TabularBulkExport
// Export from the command line (or scripted) => default format is SQL, as in previous versions of iTop, unless specified otherwise
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetSQLFormat(), true, 'raw_data');
}
$this->aStatusInfo['ignore_excel_sanitization'] = (bool)utils::ReadParam('ignore_excel_sanitization', 0, true, utils::ENUM_SANITIZATION_FILTER_INTEGER);
}
public function EnumFormParts()
@@ -123,10 +121,6 @@ class ExcelBulkExport extends TabularBulkExport
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
$oFieldSetDate->AddSubBlock($oRadioCustom);
$oFieldSetSecurity = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:Security'));
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oFieldSetSecurity));
$oFieldSetSecurity->AddSubBlock(ExportHelper::GetInputForSanitizeExcelExport());
$oP->add_ready_script(
<<<EOF
$('#form_part_xlsx_options').on('preview_updated', function() { FormatDatesInPreview('excel', 'xlsx'); });
@@ -222,12 +216,6 @@ EOF
}
}
}
// If the option to ignore Excel sanitization is not set or absent, sanitize the field
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
return ExportHelper::SanitizeField($sRet, '');
}
return $sRet;
}

View File

@@ -296,46 +296,6 @@ class InlineImage extends DBObject
return $sHtml;
}
/**
* Replace <img> tags with a data-img-id attribute by the actual image in base64 representation
* so that the image can be displayed even if the download URL is not accessible (e.g. in unauthenticated approval templates)
*
* @param string $sHtml The HTML fragment to process
*
* @return String The modified HTML
* @since 3.2.3
*/
public static function ReplaceInlineImagesWithBase64Representation(string $sHtml): String
{
return preg_replace_callback(
'/<img\s+[^>]*'.static::DOM_ATTR_ID.'="(\d+)"[^>]*>/i',
function ($matches) {
// Extract inline image ID from the tag
$id = $matches[1];
try {
// Retrieve inline image
$oInline = MetaModel::GetObject(InlineImage::class, $id, true, true);
$oOrmDocument = $oInline->Get('contents');
// Replace src image by the base64 representation
$sInlineImageAsBase64 = base64_encode($oOrmDocument->GetData());
$sDataUri = 'data:'.$oOrmDocument->GetMimeType().';base64,'.$sInlineImageAsBase64;
$sImage = preg_replace('/src=["\'][^"\']+["\']/', 'src="'.$sDataUri.'"', $matches[0]);
// Remove sensitive information (the image ID and secret) from the tag
$sImage = preg_replace('/'.static::DOM_ATTR_ID.'="\d+"\s+'.static::DOM_ATTR_SECRET.'="\w+"/', '', $sImage);
} catch (Exception $e) {
$sImage = '<img src="" alt="'.Dict::S('UI:MissingInlineImage').'">';
}
return $sImage;
},
$sHtml
);
}
/**
* Add an extra attribute data-img-id for images which are based on an actual InlineImage
* so that we can later reconstruct the full "src" URL when needed

View File

@@ -350,18 +350,15 @@ class ormDocument
if (!is_object($oObj)) {
// If access to the document is not granted, check if the access to the host object is allowed
$oObj = MetaModel::GetObject($sClass, $id, false, true);
$bHasHostRights = false;
if ($oObj instanceof Attachment) {
$sItemClass = $oObj->Get('item_class');
$sItemId = $oObj->Get('item_id');
$oHost = MetaModel::GetObject($sItemClass, $sItemId, false, false);
if (is_object($oHost)) {
$bHasHostRights = true;
if (!is_object($oHost)) {
$oObj = null;
}
}
// We could neither read the object nor get a host object matching our rights
if ($bHasHostRights !== true) {
if (!is_object($oObj)) {
throw new Exception("Invalid id ($id) for class '$sClass' - the object does not exist or you are not allowed to view it");
}
}

View File

@@ -1157,7 +1157,7 @@ class UserRights
return self::$m_oUser->GetKey();
} else {
// find the id out of the login string
$oUser = self::FindUser($sLogin, bAllowDisabledUsers: true);
$oUser = self::FindUser($sLogin);
if (is_null($oUser)) {
return null;
}
@@ -1350,7 +1350,7 @@ class UserRights
if (empty($sLogin)) {
$oUser = self::$m_oUser;
} else {
$oUser = self::FindUser($sLogin, bAllowDisabledUsers: true);
$oUser = self::FindUser($sLogin);
}
if (is_null($oUser)) {
return '';

View File

@@ -39,8 +39,6 @@
//
// .site-nav a { color:#BADA55!important; }
$ibo-blockquote--color: $ibo-body-text-color !default;
// N°2847 - Recolor svg illustrations with iTop's primary color
.ibo-svg-illustration--container > svg *[fill="#6c63ff"]{
fill: $ibo-svg-illustration--fill;
@@ -111,11 +109,3 @@ input:checked + .slider:before {
.slider.round:before {
border-radius: 7px;
}
/*
Bulma sets blockquote background color through a variable, it affects ckeditor and html display.
This rule is needed harmonize the blockquote text color in both contexts.
*/
.ibo-is-html-content blockquote {
color: $ibo-blockquote--color;
}

View File

@@ -4,4 +4,3 @@
*/
@import "bulk-modify";
@import "bulk-export";

View File

@@ -1,10 +0,0 @@
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
#form_part_csv_options:has(#ibo-sanitize-excel-export--input:checked), #form_part_xlsx_options:has(#ibo-sanitize-excel-export--input:checked){
#ibo-sanitize-excel-export--alert {
display: none;
}
}

View File

@@ -185,20 +185,16 @@ $ibo-panel--is-selectable--body--after--font-size: $ibo-font-size-700 !default;
.ibo-panel--icon-img, .ibo-panel--icon-background { // second class is deprecated, remove it when dealing with N°9317
width: 100%;
height: 100%;
object-position: center;
object-fit: $ibo-panel--icon-img--size--must-contain;
background-position: center;
background-repeat: no-repeat;
background-size: $ibo-panel--icon-img--size--must-contain;
}
.ibo-panel--icon-img--must-contain, .ibo-panel--icon-background--must-contain { // second class is deprecated, remove it when dealing with N°9317
object-fit: $ibo-panel--icon-img--size--must-contain;
background-size: $ibo-panel--icon-img--size--must-contain;
}
.ibo-panel--icon-img--must-cover, .ibo-panel--icon-background--must-cover { // second class is deprecated, remove it when dealing with N°9317
object-fit: $ibo-panel--icon-img--size--must-cover;
background-size: $ibo-panel--icon-img--size--must-cover;
}

View File

@@ -21,8 +21,6 @@ $ibo-search-form-panel--more-criteria--color: $ibo-color-blue-grey-800 !default;
$ibo-search-form-panel--more-criteria--background-color: $ibo-color-white-100 !default;
$ibo-search-form-panel--more-criteria--icon--color: $ibo-color-primary-600 !default;
$ibo-search-form-panel--more-criteria--border-color: $ibo-search-form-panel--criteria--border-color !default;
// calc is redundant but avoid SCSS min() from being used instead of CSS min()
$ibo-search-form-panel--criteria--max-height: calc(min(#{$ibo-size-750}, 50vh)) !default;
$ibo-search-form-panel--items--hover--color: $ibo-color-grey-200 !default;
@@ -280,10 +278,9 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
}
.sfc_form_group {
display: flex;
flex-direction: column;
margin-top: -1px;
z-index: -1;
display: block;
margin-top: -1px;
z-index: -1;
}
}
@@ -349,15 +346,11 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
display: none;
max-width: 450px;
width: max-content;
max-height: $ibo-search-form-panel--criteria--max-height;
max-height: 520px;
overflow-x: auto;
overflow-y: hidden;
.sfc_fg_operators {
display: flex;
flex-direction: column;
overflow: auto;
min-height: 0;
font-size: 12px;
.sfc_fg_operator {
@@ -394,9 +387,6 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
}
.sfc_opc_multichoices {
display: flex;
flex-direction: column;
height: 100%;
label > input {
vertical-align: text-top;
margin-left: $ibo-spacing-0;
@@ -408,6 +398,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
}
.sfc_opc_mc_items_wrapper {
max-height: 415px; /* Must be less than .sfc_form_group:max-height - .sfc_opc_mc_toggler:height - .sfc_opc_mc_filter:height */
overflow-y: auto;
margin: $ibo-spacing-0 -8px; /* Compensate .sfc_opc_multichoices side padding so the hover style can take the full with */
@@ -569,14 +560,8 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
&.search_form_criteria_enum {
.sfc_form_group {
.sfc_fg_operator_in {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
> label {
display: flex;
height: 100%;
min-height: 0;
display: inline-block;
width: 100%;
line-height: initial;
white-space: nowrap;

View File

@@ -51,23 +51,19 @@ $ibo-title--icon-img--size--must-zoomout: $ibo-title--icon-background--size--mus
width: 100%;
height: 100%;
object-position: center;
object-fit: $ibo-title--icon-img--size--must-contain;
background-size: $ibo-title--icon-img--size--must-contain;
}
.ibo-title--icon-img--must-contain, .ibo-title--icon-background--must-contain { // second class is deprecated, remove it when dealing with N°9317
object-fit: $ibo-title--icon-img--size--must-contain;
background-size: $ibo-title--icon-img--size--must-contain;
}
.ibo-title--icon-img--must-cover, .ibo-title--icon-background--must-cover { // second class is deprecated, remove it when dealing with N°9317
object-fit: $ibo-title--icon-img--size--must-cover;
background-size: $ibo-title--icon-img--size--must-cover;
}
.ibo-title--icon-img--must-zoomout, .ibo-title--icon-background--must-zoomout { // second class is deprecated, remove it when dealing with N°9317
width: $ibo-title--icon-img--size--must-zoomout;
height: $ibo-title--icon-img--size--must-zoomout;
background-size: $ibo-title--icon-img--size--must-zoomout;
}
.ibo-title--for-object-details {

View File

@@ -201,9 +201,8 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
}
// N°7982 Default selectize stylesheet override
// N°9468 Dropdown content needs to be a few pixel shorter than the dropdown itself to avoid double scrollbar
.selectize-dropdown-content{
max-height: calc(#{$ibo-input-select-selectize--dropdown--max-height} - 4px);
max-height: $ibo-input-select-selectize--dropdown--max-height;
}
.selectize-dropdown.ui-menu .ui-state-active {

View File

@@ -21,7 +21,6 @@ $text-strong: inherit !default;
* See https://bulma.io/documentation/elements/content/
*/
$content-block-margin-bottom: 0 !default;
$content-blockquote-background-color: $ibo-color-grey-200 !default;
/* Table: Reset style as much as possible to match rich text editor preview, which is the browser's default stylesheet.
* As there is no way to avoid bulma rules, we simply make them invalid by setting an invalid variable value, the rules will then be ignored by the browser.

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-cas/3.2.3',
'authent-cas/3.2.1',
[
// Identification
//

View File

@@ -27,7 +27,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-external/3.2.3',
'authent-external/3.2.1',
[
// Identification
//

View File

@@ -7,7 +7,7 @@ if (function_exists('ldap_connect')) {
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-ldap/3.2.3',
'authent-ldap/3.2.1',
[
// Identification
//

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-local/3.2.3',
'authent-local/3.2.1',
[
// Identification
//

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'combodo-backoffice-darkmoon-theme/3.2.3',
'combodo-backoffice-darkmoon-theme/3.2.1',
[
// Identification
//

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'combodo-backoffice-fullmoon-high-contrast-theme/3.2.3',
'combodo-backoffice-fullmoon-high-contrast-theme/3.2.1',
[
// Identification
//

File diff suppressed because one or more lines are too long

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'combodo-backoffice-fullmoon-protanopia-deuteranopia-theme/3.2.3',
'combodo-backoffice-fullmoon-protanopia-deuteranopia-theme/3.2.1',
[
// Identification
//

View File

@@ -68,47 +68,47 @@ $ibo-color-information-900: #0f172a !default;
$ibo-color-information-950: #020617 !default;
$ibo-lifecycle-new-state-primary-color: $ibo-color-information-600 !default;
$ibo-lifecycle-new-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-neutral-state-primary-color: $ibo-color-information-600 !default;
$ibo-lifecycle-neutral-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-waiting-state-primary-color: $ibo-color-yellow-700 !default;
$ibo-lifecycle-waiting-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-success-state-primary-color: $ibo-color-blue-700 !default;
$ibo-lifecycle-success-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-failure-state-primary-color: $ibo-color-orange-800 !default;
$ibo-lifecycle-failure-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-frozen-state-primary-color: $ibo-color-information-200 !default;
$ibo-lifecycle-frozen-state-secondary-color: $ibo-color-information-700 !default;
$ibo-lifecycle-new-state-primary-color: $ibo-color-information-600;
$ibo-lifecycle-new-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-neutral-state-primary-color: $ibo-color-information-600;
$ibo-lifecycle-neutral-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-waiting-state-primary-color: $ibo-color-yellow-700;
$ibo-lifecycle-waiting-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-success-state-primary-color: $ibo-color-blue-700;
$ibo-lifecycle-success-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-failure-state-primary-color: $ibo-color-orange-800;
$ibo-lifecycle-failure-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-frozen-state-primary-color: $ibo-color-information-200;
$ibo-lifecycle-frozen-state-secondary-color: $ibo-color-information-700;
$ibo-lifecycle-active-state-primary-color: $ibo-color-blue-700 !default;
$ibo-lifecycle-active-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-inactive-state-primary-color: $ibo-color-yellow-700 !default;
$ibo-lifecycle-inactive-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-active-state-primary-color: $ibo-color-blue-700;
$ibo-lifecycle-active-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-inactive-state-primary-color: $ibo-color-yellow-700;
$ibo-lifecycle-inactive-state-secondary-color: $ibo-color-white-100;
$ibo-caselog-highlight-color-1: $ibo-color-blue-700 !default;
$ibo-caselog-highlight-color-2: $ibo-color-yellow-700 !default;
$ibo-caselog-highlight-color-3: $ibo-color-information-600 !default;
$ibo-caselog-highlight-color-4: $ibo-color-yellow-500 !default;
$ibo-caselog-highlight-color-5: $ibo-color-blue-500 !default;
$ibo-caselog-highlight-color-6: $ibo-color-yellow-300 !default;
$ibo-caselog-highlight-color-7: $ibo-color-blue-300 !default;
$ibo-caselog-highlight-color-1: $ibo-color-blue-700;
$ibo-caselog-highlight-color-2: $ibo-color-yellow-700;
$ibo-caselog-highlight-color-3: $ibo-color-information-600;
$ibo-caselog-highlight-color-4: $ibo-color-yellow-500;
$ibo-caselog-highlight-color-5: $ibo-color-blue-500;
$ibo-caselog-highlight-color-6: $ibo-color-yellow-300;
$ibo-caselog-highlight-color-7: $ibo-color-blue-300;
$ibo-input-wrapper--is-error--border-color: $ibo-color-warning-700 !default;
$ibo-field-validation: $ibo-color-warning-800 !default;
$ibo-input-wrapper--is-error--border-color: $ibo-color-warning-700;
$ibo-field-validation: $ibo-color-warning-800;
$ibo-navigation-menu--visual-hint--background-color: $ibo-color-blue-400 !default;
$ibo-navigation-menu--visual-hint--background-color: $ibo-color-blue-400;
$ibo-wizard-container--background-color: $ibo-color-information-200 !default;
$ibo-wizard-container--border-color: $ibo-color-information-600 !default;
$ibo-wizard-container--background-color: $ibo-color-information-200;
$ibo-wizard-container--border-color: $ibo-color-information-600;
$ibo-navigation-menu--notifications--item--new-message-indicator--background-color: $ibo-color-white-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--border: solid 2px $ibo-color-grey-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--background-color: $ibo-color-danger-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--border: solid 2px $ibo-color-danger-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--background-color: $ibo-color-warning-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--border: solid 2px $ibo-color-warning-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--background-color: $ibo-color-success-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--border: solid 2px $ibo-color-success-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--background-color: $ibo-color-white-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--border: solid 2px $ibo-color-grey-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--background-color: $ibo-color-danger-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--border: solid 2px $ibo-color-danger-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--background-color: $ibo-color-warning-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--border: solid 2px $ibo-color-warning-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--background-color: $ibo-color-success-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--border: solid 2px $ibo-color-success-500;
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-blue-600 !default;
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-blue-600;

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'combodo-backoffice-fullmoon-tritanopia-theme/3.2.3',
'combodo-backoffice-fullmoon-tritanopia-theme/3.2.1',
[
// Identification
//

View File

@@ -32,47 +32,47 @@ $ibo-color-information-900: #0f172a !default;
$ibo-color-information-950: #020617 !default;
$ibo-lifecycle-new-state-primary-color: $ibo-color-information-600 !default;
$ibo-lifecycle-new-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-neutral-state-primary-color: $ibo-color-information-600 !default;
$ibo-lifecycle-neutral-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-waiting-state-primary-color: $ibo-color-red-200 !default;
$ibo-lifecycle-waiting-state-secondary-color: $ibo-color-red-800 !default;
$ibo-lifecycle-success-state-primary-color: $ibo-color-blue-700 !default;
$ibo-lifecycle-success-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-failure-state-primary-color: $ibo-color-red-800 !default;
$ibo-lifecycle-failure-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-frozen-state-primary-color: $ibo-color-information-200 !default;
$ibo-lifecycle-frozen-state-secondary-color: $ibo-color-information-700 !default;
$ibo-lifecycle-new-state-primary-color: $ibo-color-information-600;
$ibo-lifecycle-new-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-neutral-state-primary-color: $ibo-color-information-600;
$ibo-lifecycle-neutral-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-waiting-state-primary-color: $ibo-color-red-200;
$ibo-lifecycle-waiting-state-secondary-color: $ibo-color-red-800;
$ibo-lifecycle-success-state-primary-color: $ibo-color-blue-700;
$ibo-lifecycle-success-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-failure-state-primary-color: $ibo-color-red-800;
$ibo-lifecycle-failure-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-frozen-state-primary-color: $ibo-color-information-200;
$ibo-lifecycle-frozen-state-secondary-color: $ibo-color-information-700;
$ibo-lifecycle-active-state-primary-color: $ibo-color-blue-700 !default;
$ibo-lifecycle-active-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-inactive-state-primary-color: $ibo-color-red-700 !default;
$ibo-lifecycle-inactive-state-secondary-color: $ibo-color-white-100 !default;
$ibo-lifecycle-active-state-primary-color: $ibo-color-blue-700;
$ibo-lifecycle-active-state-secondary-color: $ibo-color-white-100;
$ibo-lifecycle-inactive-state-primary-color: $ibo-color-red-700;
$ibo-lifecycle-inactive-state-secondary-color: $ibo-color-white-100;
$ibo-caselog-highlight-color-1: $ibo-color-blue-700 !default;
$ibo-caselog-highlight-color-2: $ibo-color-red-700 !default;
$ibo-caselog-highlight-color-3: $ibo-color-information-600 !default;
$ibo-caselog-highlight-color-4: $ibo-color-red-500 !default;
$ibo-caselog-highlight-color-5: $ibo-color-blue-500 !default;
$ibo-caselog-highlight-color-6: $ibo-color-red-300 !default;
$ibo-caselog-highlight-color-7: $ibo-color-blue-300 !default;
$ibo-caselog-highlight-color-1: $ibo-color-blue-700;
$ibo-caselog-highlight-color-2: $ibo-color-red-700;
$ibo-caselog-highlight-color-3: $ibo-color-information-600;
$ibo-caselog-highlight-color-4: $ibo-color-red-500;
$ibo-caselog-highlight-color-5: $ibo-color-blue-500;
$ibo-caselog-highlight-color-6: $ibo-color-red-300;
$ibo-caselog-highlight-color-7: $ibo-color-blue-300;
$ibo-input-wrapper--is-error--border-color: $ibo-color-pink-700 !default;
$ibo-field-validation: $ibo-color-pink-800 !default;
$ibo-input-wrapper--is-error--border-color: $ibo-color-pink-700;
$ibo-field-validation: $ibo-color-pink-800;
$ibo-navigation-menu--visual-hint--background-color: $ibo-color-pink-600 !default;
$ibo-navigation-menu--visual-hint--background-color: $ibo-color-pink-600;
$ibo-wizard-container--background-color: $ibo-color-information-200 !default;
$ibo-wizard-container--border-color: $ibo-color-information-600 !default;
$ibo-wizard-container--background-color: $ibo-color-information-200;
$ibo-wizard-container--border-color: $ibo-color-information-600;
$ibo-navigation-menu--notifications--item--new-message-indicator--background-color: $ibo-color-white-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--border: solid 2px $ibo-color-grey-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--background-color: $ibo-color-pink-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--border: solid 2px $ibo-color-pink-600 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--background-color: $ibo-color-warning-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--border: solid 2px $ibo-color-warning-400 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--background-color: $ibo-color-success-100 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--border: solid 2px $ibo-color-success-500 !default;
$ibo-navigation-menu--notifications--item--new-message-indicator--background-color: $ibo-color-white-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--border: solid 2px $ibo-color-grey-500;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--background-color: $ibo-color-pink-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--border: solid 2px $ibo-color-pink-600;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--background-color: $ibo-color-warning-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-2--border: solid 2px $ibo-color-warning-400;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--background-color: $ibo-color-success-100;
$ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-3--border: solid 2px $ibo-color-success-500;
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-pink-500 !default;
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-pink-500;

View File

@@ -10,87 +10,22 @@
*/
use Combodo\iTop\Core\MetaModel\HierarchicalKey;
use Combodo\iTop\DBTools\Enum\BinExitCode;
use Combodo\iTop\DBTools\Exception\AuthenticationException;
// env-xxx folders
if (file_exists(__DIR__.'/../../../approot.inc.php')) {
require_once __DIR__.'/../../../approot.inc.php';
}
// datamodel/2.x and data/xxx-modules folders
elseif (file_exists(__DIR__.'/../../../../approot.inc.php')) {
require_once __DIR__.'/../../../../approot.inc.php';
}
require_once('../../../approot.inc.php');
require_once APPROOT.'application/startup.inc.php';
// Prepare output page
$sPageTitle = "Database maintenance tools - Report";
$bIsModeCLI = utils::IsModeCLI();
if ($bIsModeCLI) {
$oP = new CLIPage($sPageTitle);
SetupUtils::CheckPhpAndExtensionsForCli($oP, BinExitCode::FATAL->value);
} else {
$oP = new WebPage($sPageTitle);
}
// Authentication logic
try {
utils::UseParamFile();
if ($bIsModeCLI) {
$sAuthUser = utils::ReadParam('auth_user', null, true, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
$sAuthPwd = utils::ReadParam('auth_pwd', null, true, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
if (utils::IsNullOrEmptyString($sAuthUser) || utils::IsNullOrEmptyString($sAuthPwd)) {
throw new AuthenticationException("Access credentials not provided, usage: php rebuildhk.php --auth_user=<login> --auth_pwd=<password> [--param_file=<file_path>]");
}
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
UserRights::Login($sAuthUser);
} else {
throw new AuthenticationException("Access wrong credentials ('$sAuthUser')");
}
} else {
// Check user rights and prompt if needed
LoginWebPage::DoLoginEx(null, true);
foreach (MetaModel::GetClasses() as $sClass) {
if (!MetaModel::HasTable($sClass)) {
continue;
}
if (!UserRights::IsAdministrator()) {
throw new AuthenticationException("Access restricted to administrators");
}
} catch (AuthenticationException $oException) {
$oP->p($oException->getMessage());
$oP->output();
exit(BinExitCode::ERROR->value);
} catch (Exception $oException) {
$oP->p("Error: ".$oException->GetMessage());
$oP->output();
exit(BinExitCode::FATAL->value);
}
// Business logic
try {
foreach (MetaModel::GetClasses() as $sClass) {
if (!MetaModel::HasTable($sClass)) {
continue;
}
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
// Check (once) all the attributes that are hierarchical keys
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey()) {
$oP->p("Rebuild hierarchical key $sAttCode from $sClass.");
HierarchicalKey::Rebuild($sClass, $sAttCode, $oAttDef);
}
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
// Check (once) all the attributes that are hierarchical keys
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey()) {
echo "Rebuild hierarchical key $sAttCode from $sClass.\n";
HierarchicalKey::Rebuild($sClass, $sAttCode, $oAttDef);
}
}
$oP->p("Done");
$oP->output();
} catch (AuthenticationException $oException) {
$oP->p($oException->getMessage());
$oP->output();
exit(BinExitCode::ERROR->value);
} catch (Exception $oException) {
$oP->p("Error: ".$oException->GetMessage());
$oP->output();
exit(BinExitCode::FATAL->value);
}
echo "Done\n";

View File

@@ -5,93 +5,22 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\DBTools\Enum\BinExitCode;
use Combodo\iTop\DBTools\Exception\AuthenticationException;
use Combodo\iTop\DBTools\Service\DBAnalyzerUtils;
// env-xxx folders
if (file_exists(__DIR__.'/../../../approot.inc.php')) {
require_once __DIR__.'/../../../approot.inc.php';
}
// datamodel/2.x and data/xxx-modules folders
elseif (file_exists(__DIR__.'/../../../../approot.inc.php')) {
require_once __DIR__.'/../../../../approot.inc.php';
require_once('../../../approot.inc.php');
require_once(APPROOT.'application/startup.inc.php');
require_once('../db_analyzer.class.inc.php');
require_once('../src/Service/DBAnalyzerUtils.php');
$oDBAnalyzer = new DatabaseAnalyzer(0);
$aResults = $oDBAnalyzer->CheckIntegrity([]);
if (empty($aResults)) {
echo "Database OK\n";
exit(0);
}
require_once APPROOT.'application/startup.inc.php';
require_once APPROOT.'application/loginwebpage.class.inc.php';
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults);
require_once __DIR__.'/../db_analyzer.class.inc.php';
// Prepare output page
$sPageTitle = "Database maintenance tools - Report";
$bIsModeCLI = utils::IsModeCLI();
if ($bIsModeCLI) {
$oP = new CLIPage($sPageTitle);
SetupUtils::CheckPhpAndExtensionsForCli($oP, BinExitCode::FATAL->value);
} else {
$oP = new WebPage($sPageTitle);
}
// Authentication logic
try {
utils::UseParamFile();
if ($bIsModeCLI) {
$sAuthUser = utils::ReadParam('auth_user', null, true, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
$sAuthPwd = utils::ReadParam('auth_pwd', null, true, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
if (utils::IsNullOrEmptyString($sAuthUser) || utils::IsNullOrEmptyString($sAuthPwd)) {
throw new AuthenticationException("Access credentials not provided, usage: php report.php --auth_user=<login> --auth_pwd=<password> [--param_file=<file_path>]");
}
if (UserRights::CheckCredentials($sAuthUser, $sAuthPwd)) {
UserRights::Login($sAuthUser);
} else {
throw new AuthenticationException("Access wrong credentials ('$sAuthUser')");
}
} else {
// Check user rights and prompt if needed
LoginWebPage::DoLoginEx(null, true);
}
if (!UserRights::IsAdministrator()) {
throw new AuthenticationException("Access restricted to administrators");
}
} catch (AuthenticationException $oException) {
$sExceptionMessage = $oP instanceof WebPage ? utils::EscapeHtml($oException->getMessage()) : $oException->getMessage();
$oP->p($sExceptionMessage);
$oP->output();
exit(BinExitCode::ERROR->value);
} catch (Exception $oException) {
$sExceptionMessage = $oP instanceof WebPage ? utils::EscapeHtml($oException->getMessage()) : $oException->getMessage();
$oP->p("Error: ".$sExceptionMessage);
$oP->output();
exit(BinExitCode::FATAL->value);
}
// Business logic
try {
$oDBAnalyzer = new DatabaseAnalyzer(0);
$aResults = $oDBAnalyzer->CheckIntegrity([]);
if (empty($aResults)) {
$oP->p("Database OK");
$oP->output();
exit(BinExitCode::SUCCESS->value);
}
$sReportFile = DBAnalyzerUtils::GenerateReport($aResults);
$oP->p("Report generated: {$sReportFile}.log");
$oP->output();
} catch (AuthenticationException $oException) {
$sExceptionMessage = $oP instanceof WebPage ? utils::EscapeHtml($oException->getMessage()) : $oException->getMessage();
$oP->p($sExceptionMessage);
$oP->output();
exit(BinExitCode::ERROR->value);
} catch (Exception $oException) {
$sExceptionMessage = $oP instanceof WebPage ? utils::EscapeHtml($oException->getMessage()) : $oException->getMessage();
$oP->p("Error: ".$sExceptionMessage);
$oP->output();
exit(BinExitCode::FATAL->value);
}
echo "Report generated: {$sReportFile}.log\n";

View File

@@ -1,9 +0,0 @@
{
"name": "combodo/combodo-db-tools",
"license": "AGPL-3.0-only",
"autoload": {
"psr-4": {
"Combodo\\iTop\\DBTools\\": "src/"
}
}
}

View File

@@ -1,18 +0,0 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "38292b9b3a56c6c8776285a17c58034e",
"packages": [],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {},
"platform-dev": {},
"plugin-api-version": "2.6.0"
}

View File

@@ -25,7 +25,7 @@
/** @noinspection PhpUnhandledExceptionInspection */
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'combodo-db-tools/3.2.3',
'combodo-db-tools/3.2.1',
[
// Identification
//
@@ -43,7 +43,6 @@ SetupWebPage::AddModule(
// Components
//
'datamodel' => [
'vendor/autoload.php',
'src/Service/DBToolsUtils.php',
'src/Service/DBAnalyzerUtils.php',
],

View File

@@ -1,18 +0,0 @@
<?php
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\DBTools\Enum;
/**
* Enum for the exit codes of the bin scripts
*/
enum BinExitCode: int
{
case SUCCESS = 0;
case ERROR = -1;
case FATAL = -2;
}

View File

@@ -1,12 +0,0 @@
<?php
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\DBTools\Exception;
class AuthenticationException extends \Exception
{
}

View File

@@ -1,22 +0,0 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, $err);
} elseif (!headers_sent()) {
echo $err;
}
}
throw new RuntimeException($err);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit38292b9b3a56c6c8776285a17c58034e::getLoader();

View File

@@ -1,579 +0,0 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var \Closure(string):void */
private static $includeFile;
/** @var string|null */
private $vendorDir;
// PSR-4
/**
* @var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array<string, list<string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* List of PSR-0 prefixes
*
* Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
*
* @var array<string, array<string, list<string>>>
*/
private $prefixesPsr0 = array();
/**
* @var list<string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var array<string, bool>
*/
private $missingClasses = array();
/** @var string|null */
private $apcuPrefix;
/**
* @var array<string, self>
*/
private static $registeredLoaders = array();
/**
* @param string|null $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
self::initializeIncludeClosure();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array<string, list<string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return list<string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return list<string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return array<string, string> Array of classname => path
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array<string, string> $classMap Class to filename map
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
$paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
$paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
$paths = (array) $paths;
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
$paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
$paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
$paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param list<string>|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param list<string>|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
$includeFile = self::$includeFile;
$includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders keyed by their corresponding vendor directories.
*
* @return array<string, self>
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
/**
* @return void
*/
private static function initializeIncludeClosure()
{
if (self::$includeFile !== null) {
return;
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
*/
self::$includeFile = \Closure::bind(static function($file) {
include $file;
}, null, null);
}
}

View File

@@ -1,378 +0,0 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool
*/
private static $installedIsLocalDir;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints((string) $constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
// so we have to assume it does not, and that may result in duplicate data being returned when listing
// all installed packages for example
self::$installedIsLocalDir = false;
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
$copiedLocalDir = false;
if (self::$canGetVendors) {
$selfDir = strtr(__DIR__, '\\', '/');
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
$vendorDir = strtr($vendorDir, '\\', '/');
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require $vendorDir.'/composer/installed.php';
self::$installedByVendor[$vendorDir] = $required;
$installed[] = $required;
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
self::$installed = $required;
self::$installedIsLocalDir = true;
}
}
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
$copiedLocalDir = true;
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;
} else {
self::$installed = array();
}
}
if (self::$installed !== array() && !$copiedLocalDir) {
$installed[] = self::$installed;
}
return $installed;
}
}

View File

@@ -1,15 +0,0 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Combodo\\iTop\\DBTools\\Enum\\BinExitCode' => $baseDir . '/src/Enum/BinExitCode.php',
'Combodo\\iTop\\DBTools\\Exception\\AuthenticationException' => $baseDir . '/src/Exception/AuthenticationException.php',
'Combodo\\iTop\\DBTools\\Service\\BinUtils' => $baseDir . '/src/Service/BinUtils.php',
'Combodo\\iTop\\DBTools\\Service\\DBAnalyzerUtils' => $baseDir . '/src/Service/DBAnalyzerUtils.php',
'Combodo\\iTop\\DBTools\\Service\\DBToolsUtils' => $baseDir . '/src/Service/DBToolsUtils.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
);

View File

@@ -1,9 +0,0 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);

View File

@@ -1,10 +0,0 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Combodo\\iTop\\DBTools\\' => array($baseDir . '/src'),
);

View File

@@ -1,37 +0,0 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit38292b9b3a56c6c8776285a17c58034e
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit38292b9b3a56c6c8776285a17c58034e', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit38292b9b3a56c6c8776285a17c58034e', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit38292b9b3a56c6c8776285a17c58034e::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
return $loader;
}
}

View File

@@ -1,41 +0,0 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInit38292b9b3a56c6c8776285a17c58034e
{
public static $prefixLengthsPsr4 = array (
'C' =>
array (
'Combodo\\iTop\\DBTools\\' => 21,
),
);
public static $prefixDirsPsr4 = array (
'Combodo\\iTop\\DBTools\\' =>
array (
0 => __DIR__ . '/../..' . '/src',
),
);
public static $classMap = array (
'Combodo\\iTop\\DBTools\\Enum\\BinExitCode' => __DIR__ . '/../..' . '/src/Enum/BinExitCode.php',
'Combodo\\iTop\\DBTools\\Exception\\AuthenticationException' => __DIR__ . '/../..' . '/src/Exception/AuthenticationException.php',
'Combodo\\iTop\\DBTools\\Service\\BinUtils' => __DIR__ . '/../..' . '/src/Service/BinUtils.php',
'Combodo\\iTop\\DBTools\\Service\\DBAnalyzerUtils' => __DIR__ . '/../..' . '/src/Service/DBAnalyzerUtils.php',
'Combodo\\iTop\\DBTools\\Service\\DBToolsUtils' => __DIR__ . '/../..' . '/src/Service/DBToolsUtils.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit38292b9b3a56c6c8776285a17c58034e::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit38292b9b3a56c6c8776285a17c58034e::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit38292b9b3a56c6c8776285a17c58034e::$classMap;
}, null, ClassLoader::class);
}
}

View File

@@ -1,5 +0,0 @@
{
"packages": [],
"dev": true,
"dev-package-names": []
}

View File

@@ -1,23 +0,0 @@
<?php return array(
'root' => array(
'name' => 'combodo/combodo-db-tools',
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => null,
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'combodo/combodo-db-tools' => array(
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => null,
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
),
);

View File

@@ -19,7 +19,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-attachments/3.2.3',
'itop-attachments/3.2.1',
[
// Identification
//

View File

@@ -199,15 +199,15 @@ function RaiseAlarm($sMessage)
//////////
// Main
try {
utils::UseParamFile();
} catch (Exception $e) {
echo "Error: ".$e->GetMessage()."\n";
exit;
}
if (utils::IsModeCLI()) {
try {
utils::UseParamFile();
} catch (Exception $e) {
echo 'Error: '.$e->GetMessage()."\n";
exit;
}
$oP = new CLIPage('Check backup utility');
SetupUtils::CheckPhpAndExtensionsForCli($oP);
SetupUtils::CheckPhpAndExtensionsForCli(new CLIPage('Check backup utility'));
echo date('Y-m-d H:i:s')." - running check-backup utility\n";
try {

View File

@@ -88,15 +88,16 @@ if (utils::IsModeCLI()) {
$oP = new CLIPage(GetOperationName());
SetupUtils::CheckPhpAndExtensionsForCli($oP);
try {
utils::UseParamFile();
} catch (Exception $e) {
ExitError($oP, $e->GetMessage());
}
} else {
$oP = new WebPage(GetOperationName());
}
try {
utils::UseParamFile();
} catch (Exception $e) {
ExitError($oP, $e->GetMessage());
}
ExecuteMainOperation($oP);
$oP->output();

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-backup/3.2.3',
'itop-backup/3.2.1',
[
// Identification
//
@@ -41,11 +41,6 @@ SetupWebPage::AddModule(
'doc.manual_setup' => '',
'doc.more_information' => '',
// Security
'delegated_authentication_endpoints' => [
'ajax.backup.php',
],
// Default settings
//
'settings' => [

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-cmdb-services/3.2.3',
'itop-bridge-cmdb-services/3.2.1',
[
// Identification
//

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-cmdb-ticket/3.2.3',
'itop-bridge-cmdb-ticket/3.2.1',
[
// Identification
//

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-datacenter-mgmt-services/3.2.3',
'itop-bridge-datacenter-mgmt-services/3.2.1',
[
// Identification
//

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-endusers-devices-services/3.2.3',
'itop-bridge-endusers-devices-services/3.2.1',
[
// Identification
//

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-storage-mgmt-services/3.2.3',
'itop-bridge-storage-mgmt-services/3.2.1',
[
// Identification
//

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-virtualization-mgmt-services/3.2.3',
'itop-bridge-virtualization-mgmt-services/3.2.1',
[
// Identification
//

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-bridge-virtualization-storage/3.2.3',
'itop-bridge-virtualization-storage/3.2.1',
[
// Identification
//

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-change-mgmt-itil/3.2.3',
'itop-change-mgmt-itil/3.2.1',
[
// Identification
//

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-change-mgmt/3.2.3',
'itop-change-mgmt/3.2.1',
[
// Identification
//

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-config-mgmt/3.2.3',
'itop-config-mgmt/3.2.1',
[
// Identification
//

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-config/3.2.3',
'itop-config/3.2.1',
[
// Identification
//

View File

@@ -25,7 +25,7 @@
/** @noinspection PhpUnhandledExceptionInspection */
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-core-update/3.2.3',
'itop-core-update/3.2.1',
[
// Identification
//

View File

@@ -19,7 +19,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-datacenter-mgmt/3.2.3',
'itop-datacenter-mgmt/3.2.1',
[
// Identification
//

View File

@@ -26,7 +26,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-endusers-devices/3.2.3',
'itop-endusers-devices/3.2.1',
[
// Identification
//

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-faq-light/3.2.3',
'itop-faq-light/3.2.1',
[
// Identification
//

View File

@@ -25,7 +25,7 @@
/** @noinspection PhpUnhandledExceptionInspection */
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-files-information/3.2.3',
'itop-files-information/3.2.1',
[
// Identification
//

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-full-itil/3.2.3',
'itop-full-itil/3.2.1',
[
// Identification
//

View File

@@ -242,8 +242,8 @@ try {
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
}
// First step: prepare the datamodel, if it fails, roll-back
$aSelectedExtensionCodes = utils::ReadParam('extension_codes', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', [], false, utils::ENUM_SANITIZATION_FILTER_MODULE_CODE);
$aSelectedExtensionCodes = utils::ReadParam('extension_codes', []);
$aSelectedExtensionDirs = utils::ReadParam('extension_dirs', []);
$oRuntimeEnv = new HubRunTimeEnvironment('production', false); // use a temp environment: production-build
$oRuntimeEnv->MoveSelectedExtensions(APPROOT.'/data/downloaded-extensions/', $aSelectedExtensionDirs);
@@ -279,24 +279,11 @@ try {
$oRuntimeEnv = new RunTimeEnvironment('production', true);
try {
SetupLog::Info('Move to production starts...');
$sAuthent = utils::ReadParam('authent', '', false, 'raw_data');
if (!file_exists(APPROOT.'data/hub/compile_authent') || $sAuthent !== file_get_contents(APPROOT.'data/hub/compile_authent')) {
throw new SecurityException(Dict::S('iTopHub:FailAuthent'));
}
} catch (Exception $e) {
if (file_exists(APPROOT.'data/hub/compile_authent')) {
unlink(APPROOT.'data/hub/compile_authent');
}
// Note: at this point, the dictionnary is not necessarily loaded
SetupLog::Error(get_class($e).': '.Dict::S('iTopHub:ConfigurationSafelyReverted')."\n".$e->getMessage());
SetupLog::Error('Debug trace: '.$e->getTraceAsString());
ReportError($e->getMessage(), $e->getCode());
break;
}
try {
SetupLog::Info('Move to production starts...');
unlink(APPROOT.'data/hub/compile_authent');
// Load the "production" config file to clone & update it
$oConfig = new Config(APPCONF.'production/'.ITOP_CONFIG_FILE);

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-hub-connector/3.2.3',
'itop-hub-connector/3.2.1',
[
// Identification
//
@@ -37,10 +37,6 @@ SetupWebPage::AddModule(
// add your sample data XML files here,
],
'delegated_authentication_endpoints' => [
'ajax.php',
],
// Documentation
//
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-incident-mgmt-itil/3.2.3',
'itop-incident-mgmt-itil/3.2.1',
[
// Identification
//

View File

@@ -2,7 +2,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-knownerror-mgmt/3.2.3',
'itop-knownerror-mgmt/3.2.1',
[
// Identification
//

View File

@@ -6,7 +6,7 @@
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-oauth-client/3.2.3',
'itop-oauth-client/3.2.1',
[
// Identification
//

View File

@@ -21,7 +21,7 @@
/** @noinspection PhpUnhandledExceptionInspection */
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-portal-base/3.2.3',
'itop-portal-base/3.2.1',
[
// Identification
'label' => 'Portal Development Library',

View File

@@ -1386,27 +1386,19 @@ class ObjectController extends BrickController
if ($oField instanceof DateTimeField) {
$oField->SetDateTimePickerWidgetParent($sDateTimePickerWidgetParent);
}
// View data
$sValue = $oAttDef->GetAsHTML($oNewLink->Get($sAttCode));
$aObjectData['attributes']['lnk__'.$sAttCode] = [
'object_class' => $sLinkClass,
'object_id' => $oNewLink->GetKey(),
'prefix' => 'lnk__',
'attribute_code' => $sAttCode,
'attribute_type' => get_class($oAttDef),
'value_html' => $sValue,
];
// If the field has a renderer we adjust view data
$sFieldRendererClass = BsLinkedSetFieldRenderer::GetFieldRendererClass($oField);
$sValue = $oAttDef->GetAsHTML($oNewLink->Get($sAttCode));
if ($sFieldRendererClass !== null) {
$oFieldRenderer = new $sFieldRendererClass($oField);
$oFieldOutput = $oFieldRenderer->Render();
$aObjectData['attributes']['lnk__'.$sAttCode]['value_html'] = $oFieldOutput->GetHtml();
$aObjectData['attributes']['lnk__'.$sAttCode]['css_inline'] = $oFieldOutput->GetCss();
$aObjectData['attributes']['lnk__'.$sAttCode]['js_inline'] = $oFieldOutput->GetJs();
$sValue = $oFieldOutput->GetHtml();
}
$aObjectData['attributes']['lnk__'.$sAttCode] = [
'att_code' => $sAttCode,
'value' => $sValue,
'css_inline' => $oFieldOutput->GetCss(),
'js_inline' => $oFieldOutput->GetJs(),
];
}
$aData['items'][] = $aObjectData;

Some files were not shown because too many files have changed in this diff Show More