Compare commits

..

5 Commits

Author SHA1 Message Date
Anne-Cath
af29a00065 Fix rebase 2026-02-17 15:37:42 +01:00
Anne-Cath
4406afa089 Add tests 2026-02-17 12:18:39 +01:00
Anne-Cath
b547d2b0ea WIP 2026-02-17 12:18:13 +01:00
Anne-Cath
246de88a2d WIP 2026-02-17 12:17:24 +01:00
Anne-Cath
8679665c23 Rest on multi classes 2026-02-17 12:15:56 +01:00
1374 changed files with 18689 additions and 70231 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

View File

@@ -1,9 +0,0 @@
# Developers
## PHP Code Styles
We use `PHP CS Fixer` to ensure code formating consistency across PHP codebase.
You can find the configuration and instructions to run it [here](../tests/php-code-style/README.md).
## PHP Static Analysis
We use `PHPStan` to ensure code quality and to detect potential bugs in our PHP codebase.
You can find the configuration and instructions to run it [here](../tests/php-static-analysis/README.md).

View File

@@ -7,8 +7,7 @@
'git4': 'grey',
'git5': 'grey',
'git6': 'grey',
'git7': 'grey',
'git8': 'grey'
'git7': 'grey'
}, 'gitGraph': {'showBranches': true,'mainBranchName': 'develop','rotateCommitLabel': true}} }%%
gitGraph
commit id: "2016-07-06" tag: "2.3.0" type: HIGHLIGHT
@@ -87,25 +86,24 @@ gitGraph
commit id: "2024-01-17a" tag: "2.7.10"
checkout support/3.0
commit id: "2024-01-17b" tag: "3.0.4"
checkout support/2.7
commit id: "2024-09-28" tag: "2.7.11"
checkout support/3.1
commit id: "2024-09-27" tag: "3.1.2"
checkout support/3.2
commit id: "2024-06-25" tag: "3.2.0-beta1" type: REVERSE
commit id: "2024-08-07" tag: "3.2.0"
checkout support/2.7
commit id: "2025-02-07a" tag: "2.7.12"
commit id: "2024-09-13" tag: "3.2.0-2"
checkout support/3.1
commit id: "2025-02-07b" tag: "3.1.3"
checkout support/3.2
commit id: "2025-02-07c " tag: "3.2.1"
commit id: "2025-03-31 " tag: "3.2.1-1"
commit id: "2025-07-28 " tag: "3.2.2"
commit id: "2024-09-27" tag: "3.1.2"
checkout support/2.7
commit id: "2025-09-25" tag: "2.7.13"
commit id: "2024-09-28" tag: "2.7.11"
checkout support/2.7
commit id: "2025-02-25" tag: "2.7.12"
checkout support/3.1
commit id: "2025-02-25 " tag: "3.1.3"
checkout support/3.2
commit id: "2026-04-27 " tag: "3.2.3"
commit id: "2025-02-25 " tag: "3.2.1"
commit id: "2025-04-08" tag: "3.2.1-1"
commit id: "2025-08-19" tag: "3.2.2-1"
checkout support/2.7
commit id: "2025-10-07" tag: "2.7.13"
```
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).

View File

@@ -1,60 +0,0 @@
name: "Bug report"
description: "Report a bug that you identified in iTop"
type: bug
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"

View File

@@ -1,47 +0,0 @@
name: "Enhancement suggestion"
description: "Suggest an improvement to iTop"
type: feature
body:
- type: markdown
attributes:
value: |
Please explain why you're creating this issue :
- Please describe what's your improvement proposition.
- Then tell us if you're willing to create a PR for this enhancement ? If so, we'll indicate in the issue if we're interested in it.
- 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: input
id: itop_version
attributes:
label: iTop version (if appropriate)
description: "Complete iTop version (e.g., 3.2.3)"
validations:
required: false
- 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: 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"

View File

@@ -1,81 +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 thread / 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
| 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
- ...
-->
- [ ] ...
- [ ] ...
- [ ] ...

View File

@@ -26,23 +26,13 @@ jobs:
fi
- name: Add internal tag if member of the organization
- name: Add internal tag if member
if: env.is_member == 'true'
run: |
curl -X POST -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/labels \
-d '{"labels":["internal"]}'
- name: Set PR author as assignee if member of the organization
if: env.is_member == 'true'
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/assignees \
-d '{"assignees":["${{ github.event.pull_request.user.login }}"]}'
env:
is_member: ${{ env.is_member }}
@@ -50,4 +40,4 @@ jobs:
uses: actions/add-to-project@v1.0.2
with:
project-url: ${{ env.project_url }}
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}

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 :)
@@ -146,6 +159,6 @@ We have one sticker per contribution type. You might get multiple stickers with
* Beta tester: Test and give feedback on beta releases
* Extension developer: Develop and publish an extension
Here is the design of each sticker:
Here is the design of each stickers for year 2024:
![iTop stickers](.doc/contributing-guide/contributing-stickers-side-by-side.png)
![iTop stickers 2025](.doc/contributing-guide/2025.contributing-stickers-side-by-side.png)

View File

@@ -53,7 +53,7 @@ iTop also offers mass import tools to help you become even more efficient.
[4]: https://www.itophub.io/wiki/page?id=latest:install:requirements
[5]: https://www.itophub.io/wiki
[6]: https://store.itophub.io/en_US/
[7]: .doc/itop-version-history.md
[7]: itop-version-history.md
[10]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#configuration_management_cmdb
[11]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#ticketing
@@ -73,9 +73,6 @@ iTop development is sponsored, led, and supported by [Combodo][0].
[0]: https://www.combodo.com
## Developers
You can find information and instructions about our quality tools and how to run them [here](.doc/developers.md).
## Contributors

View File

@@ -16,5 +16,5 @@ require_once(APPROOT.'/application/audit.category.class.inc.php');
require_once(APPROOT.'/application/audit.domain.class.inc.php');
require_once(APPROOT.'/application/audit.rule.class.inc.php');
require_once(APPROOT.'/application/query.class.inc.php');
require_once(APPROOT.'/setup/moduleinstallation/moduleinstallation.class.inc.php');
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');

View File

@@ -97,33 +97,77 @@ class RestUtils
* @throws Exception
* @api
*/
public static function GetFieldList($sClass, $oData, $sParamName)
public static function GetFieldList($sClass, $oData, $sParamName, $bFailIfNotFound = true)
{
$sFields = self::GetOptionalParam($oData, $sParamName, '*');
$aShowFields = [];
if ($sFields == '*') {
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
$aShowFields[$sClass][] = $sAttCode;
}
} elseif ($sFields == '*+') {
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sRefClass) {
foreach (MetaModel::ListAttributeDefs($sRefClass) as $sAttCode => $oAttDef) {
$aShowFields[$sRefClass][] = $sAttCode;
}
}
} else {
foreach (explode(',', $sFields) as $sAttCode) {
$sAttCode = trim($sAttCode);
if (($sAttCode != 'id') && (!MetaModel::IsValidAttCode($sClass, $sAttCode))) {
throw new Exception("$sParamName: invalid attribute code '$sAttCode'");
}
$aShowFields[$sClass][] = $sAttCode;
}
}
return $aShowFields;
return match($sFields) {
'*' => self::GetFieldListForClass($sClass),
'*+' => self::GetFieldListForParentClass($sClass),
default => self::GetLimitedFieldListForClass($sClass, $sFields, $sParamName, $bFailIfNotFound),
};
}
public static function HasRequestedExtendedOutput(string $sFields): bool
{
return match($sFields) {
'*' => false,
'*+' => true,
default => substr_count($sFields, ':') > 1,
};
}
public static function HasRequestedAllOutputFields(string $sFields): bool
{
return match($sFields) {
'*', '*+' => true,
default => false,
};
}
protected static function GetFieldListForClass(string $sClass): array
{
return [$sClass => array_keys(MetaModel::ListAttributeDefs($sClass))];
}
protected static function GetFieldListForParentClass(string $sClass): array
{
$aFieldList = array();
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sRefClass) {
$aFieldList = array_merge($aFieldList, self::GetFieldListForClass($sRefClass));
}
return $aFieldList;
}
protected static function GetLimitedFieldListForSingleClass(string $sClass, string $sFields, string $sParamName, bool $bFailIfNotFound = true): array
{
$aFieldList = [$sClass => []];
foreach (explode(',', $sFields) as $sAttCode) {
$sAttCode = trim($sAttCode);
if (($sAttCode == 'id') || (MetaModel::IsValidAttCode($sClass, $sAttCode))) {
$aFieldList[$sClass][] = $sAttCode;
} else {
if ($bFailIfNotFound) {
throw new Exception("$sParamName: invalid attribute code '$sAttCode' for class '$sClass'");
}
}
}
return $aFieldList;
}
protected static function GetLimitedFieldListForClass(string $sClass, string $sFields, string $sParamName, bool $bFailIfNotFound = true): array
{
if (!str_contains($sFields, ':')) {
return self::GetLimitedFieldListForSingleClass($sClass, $sFields, $sParamName, $bFailIfNotFound);
}
$aFieldList = [];
$aFieldListParts = explode(';', $sFields);
foreach ($aFieldListParts as $sClassFields) {
list($sSubClass, $sSubClassFields) = explode(':', $sClassFields);
$aFieldList = array_merge($aFieldList, self::GetLimitedFieldListForSingleClass(trim($sSubClass), trim($sSubClassFields), $sParamName, $bFailIfNotFound));
}
return $aFieldList;
}
/**
* Read and interpret object search criteria from a Rest/Json structure
*

View File

@@ -819,7 +819,6 @@ HTML
foreach ($aNotificationClasses as $sNotifClass) {
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev WHERE Ev.object_id = :id AND Ev.object_class = :class");
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
$aNotifSearches[$sNotifClass]->AllowAllData();
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], []);
$iNotifsCount += $oNotifSet->Count();
}
@@ -833,7 +832,6 @@ HTML
'menu' => false,
'panel_title' => MetaModel::GetName($sNotifClass),
'panel_icon' => MetaModel::GetClassIcon($sNotifClass, false),
'display_unauthorized_objects' => true,
]);
}
}
@@ -1035,7 +1033,7 @@ HTML
// Add extra data for markup generation
// - Attribute code and AttributeDef. class
$val['attcode'] = $sAttCode;
$val['atttype'] = $oAttDef->GetType();
$val['atttype'] = $sAttDefClass;
$val['attlabel'] = $sAttLabel;
$val['attflags'] = ($bEditMode) ? $this->GetFormAttributeFlags($sAttCode) : OPT_ATT_READONLY;
@@ -4495,7 +4493,7 @@ HTML;
$oDivField = FieldUIBlockFactory::MakeLarge("");
// UIContentBlockUIBlockFactory::MakeStandard(null,["field_container field_large"]);
$oDivField->AddDataAttribute("attribute-type", $oAttDef->GetType());
$oDivField->AddDataAttribute("attribute-type", $sAttDefClass);
$oDivField->AddDataAttribute("attribute-label", $sAttMetaDataLabel);
$oDivField->AddDataAttribute("attribute-flag-hidden", false);
$oDivField->AddDataAttribute("attribute-flag-read-only", false);
@@ -5064,10 +5062,10 @@ EOF
} else {
if (count($aObjects) == 1) {
$oObj = $aObjects[0];
$sSubtitle = Dict::Format('UI:Delete:Confirm_Object', $oObj->GetHyperLink());
$sSubtitle = Dict::Format('UI:Delect:Confirm_Object', $oObj->GetHyperLink());
} else {
$sSubtitle = Dict::Format(
'UI:Delete:Confirm_Count_ObjectsOf_Class',
'UI:Delect:Confirm_Count_ObjectsOf_Class',
count($aObjects),
MetaModel::GetName($sClass)
);
@@ -5634,7 +5632,7 @@ JS
* @return void
* @since 3.1.0
*/
final public function AddAttributeFlags(string $sAttCode, int $iFlags, string $sTargetState = '', ?string $sReason = null): void
final public function AddAttributeFlags(string $sAttCode, int $iFlags, string $sTargetState = '', string $sReason = null): void
{
if (!isset($this->aAttributesFlags[$sTargetState])) {
$this->aAttributesFlags[$sTargetState] = [];
@@ -5657,7 +5655,7 @@ JS
* @return void
* @since 3.1.0
*/
final public function ForceAttributeFlags(string $sAttCode, int $iFlags, string $sTargetState = '', ?string $sReason = null): void
final public function ForceAttributeFlags(string $sAttCode, int $iFlags, string $sTargetState = '', string $sReason = null): void
{
if (!isset($this->aAttributesFlags[$sTargetState])) {
$this->aAttributesFlags[$sTargetState] = [];
@@ -5698,7 +5696,7 @@ JS
* @return void
* @since 3.1.0
*/
final public function AddInitialAttributeFlags(string $sAttCode, int $iFlags, ?string $sReason = null)
final public function AddInitialAttributeFlags(string $sAttCode, int $iFlags, string $sReason = null)
{
if (!isset($this->aInitialAttributesFlags)) {
$this->aInitialAttributesFlags = [];
@@ -5720,7 +5718,7 @@ JS
* @return void
* @since 3.1.0
*/
final public function ForceInitialAttributeFlags(string $sAttCode, int $iFlags, ?string $sReason = null)
final public function ForceInitialAttributeFlags(string $sAttCode, int $iFlags, string $sReason = null)
{
if (!isset($this->aInitialAttributesFlags)) {
$this->aInitialAttributesFlags = [];

View File

@@ -497,7 +497,7 @@ EOF
* @param array $aExtraParams
* @param bool $bCanEdit
*
* @return null|\Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout
* @return \Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = [], $bCanEdit = true)
{
@@ -1029,7 +1029,7 @@ EOF
var dashboard = $('.ibo-dashboard#$sDivId')
dashboard.block();
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
{ operation: 'toggle_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: $sReloadURL },
{ operation: 'toggle_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: '$sReloadURL' },
function(data) {
dashboard.html(data);
dashboard.unblock();

View File

@@ -726,10 +726,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);
}
@@ -1383,10 +1379,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

@@ -1,10 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ForgotPasswordApplicationException extends Exception
{
}

View File

@@ -1,10 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ForgotPasswordUserInputException extends Exception
{
}

View File

@@ -1387,7 +1387,7 @@ class DesignerIconSelectionField extends DesignerFormField
public function AddAllowedValue($aValue)
{
// Add a null value to the list of allowed values
$this->aAllowedValues = array_merge([$aValue], $this->aAllowedValues ?? [null]);
$this->aAllowedValues = array_merge([$aValue], $this->aAllowedValues);
}
public function EnableUpload($sIconUploadUrl)
{

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

@@ -243,12 +243,15 @@ class LoginTwigRenderer
public function GetDefaultVars()
{
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
$sDisplayIcon = Branding::GetLoginLogoAbsoluteUrl();
$aVars = [
'sAppRootUrl' => utils::GetAbsoluteUrlAppRoot(),
'aPluginFormData' => $this->GetPluginFormData(),
'sItopVersion' => ITOP_VERSION,
'sVersionShort' => $sVersionShort,
'sIconUrl' => $sIconUrl,
'sDisplayIcon' => $sDisplayIcon,
];

View File

@@ -221,15 +221,15 @@ class LoginWebPage extends NiceWebPage
if ($oUser != null) {
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token')) {
throw new ForgotPasswordUserInputException('External accounts do not allow password reset');
throw new Exception(Dict::S('UI:ResetPwd-Error-NotPossible'));
}
if (!$oUser->CanChangePassword()) {
throw new ForgotPasswordUserInputException('The account does not allow password reset');
throw new Exception(Dict::S('UI:ResetPwd-Error-FixedPwd'));
}
$sTo = $oUser->GetResetPasswordEmail(); // throws Exceptions if not allowed
if ($sTo == '') {
throw new ForgotPasswordUserInputException('Missing email address for this account');
throw new Exception(Dict::S('UI:ResetPwd-Error-NoEmail'));
}
// This token allows the user to change the password without knowing the previous one
@@ -255,21 +255,17 @@ class LoginWebPage extends NiceWebPage
case EMAIL_SEND_ERROR:
default:
throw new ForgotPasswordApplicationException('Failed to send the password reset email for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
IssueLog::Error('Failed to send the email with the NEW password for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
throw new Exception(Dict::S('UI:ResetPwd-Error-Send'));
}
}
} catch (ForgotPasswordApplicationException $e) {
IssueLog::Error('Failed to process the forgot password request for user "'.$sAuthUser.'" [reason='.get_class($e).']: '.$e->getMessage());
} catch (ForgotPasswordUserInputException $e) {
IssueLog::Info('Failed to process the forgot password request for user "'.$sAuthUser.'" [reason='.get_class($e).']: '.$e->getMessage());
} catch (\Throwable $e) {
IssueLog::Error('Unexpected error while processing the forgot password request for user "'.$sAuthUser.'": '.$e->getMessage());
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$oTwigContext->Render($this, 'forgotpwdsent.html.twig', $aVars);
} catch (Exception $e) {
$this->DisplayForgotPwdForm(true, $e->getMessage());
}
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$oTwigContext->Render($this, 'forgotpwdsent.html.twig', $aVars);
}
public function DisplayResetPwdForm($sErrorMessage = null)

View File

@@ -1423,21 +1423,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

@@ -34,7 +34,7 @@ interface iNewsroomProvider
* @param User $oUser The user for who to check if the provider is applicable.
* return bool
*/
public function IsApplicable(?User $oUser = null);
public function IsApplicable(User $oUser = null);
/**
* The human readable (localized) label for this provider
@@ -139,7 +139,7 @@ abstract class NewsroomProviderBase implements iNewsroomProvider
*/
abstract public function GetViewAllURL();
public function IsApplicable(?User $oUser = null)
public function IsApplicable(User $oUser = null)
{
return false;
}

View File

@@ -151,7 +151,7 @@ abstract class Query extends cmdbAbstractObject
* @return string|null
* @since 3.1.0
*/
abstract public function GetExportUrl(?array $aValues = null): ?string;
abstract public function GetExportUrl(array $aValues = null): ?string;
/**
* Update last export information.
@@ -227,7 +227,7 @@ class QueryOQL extends Query
}
/** @inheritdoc */
public function GetExportUrl(?array $aValues = null): ?string
public function GetExportUrl(array $aValues = null): ?string
{
try {
// retrieve attributes

View File

@@ -924,6 +924,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'])) {
@@ -931,14 +936,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;
}
@@ -970,9 +967,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)."'");
}
@@ -284,7 +276,9 @@ class utils
}
// Read and record the value for switching the archive mode
$iCurrent = self::ReadParam('with-archive', $iDefault);
Session::Set('archive_mode', $iCurrent);
if (Session::IsInitialized()) {
Session::Set('archive_mode', $iCurrent);
}
// Read and use the value for the current page (web services)
$iCurrent = self::ReadParam('with_archive', $iCurrent, true);
self::$bPageMode = ($iCurrent == 1);
@@ -396,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
*/
@@ -484,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,
@@ -497,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);
@@ -974,7 +958,7 @@ class utils
return self::$oConfig;
}
$sProductionEnvConfigPath = self::GetConfigFilePath(ITOP_DEFAULT_ENV);
$sProductionEnvConfigPath = self::GetConfigFilePath('production');
if (file_exists($sProductionEnvConfigPath)) {
$oProductionEnvDiskConfig = new Config($sProductionEnvConfigPath);
self::SetConfig($oProductionEnvDiskConfig);
@@ -1300,7 +1284,7 @@ class utils
* @throws \CoreException
* @throws \Exception
*/
public static function ExecITopScript(string $sScriptName, array $aArguments, ?string $sAuthUser = null, ?string $sAuthPwd = null)
public static function ExecITopScript(string $sScriptName, array $aArguments, string $sAuthUser = null, string $sAuthPwd = null)
{
$aDisabled = explode(', ', ini_get('disable_functions'));
if (in_array('exec', $aDisabled)) {
@@ -1390,7 +1374,7 @@ class utils
* @return string A path to a folder into which any module can store cache data
* The corresponding folder is created or cleaned upon code compilation
*/
public static function GetCachePath(?string $sEnvironment = null): string
public static function GetCachePath(string $sEnvironment = null): string
{
if (is_null($sEnvironment)) {
$sEnvironment = MetaModel::GetEnvironment();
@@ -1453,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;
@@ -2103,9 +2081,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 = [];
@@ -3144,76 +3120,4 @@ TXT
return $aTrace;
}
/**
* PHP unserialize encapsulation, allow throwing exception when not allowed object class is detected (for security hardening)
*
* @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
*/
public static function Unserialize(string $data, array $aOptions = ['allowed_classes' => false], bool $bThrowNotAllowedObjectClassException = true): mixed
{
$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);
}
}
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');
}
foreach (get_object_vars($data) as $property) {
self::AssertNoIncompleteClassDetected($property);
}
} elseif (is_array($data)) {
foreach ($data as $value) {
self::AssertNoIncompleteClassDetected($value);
}
}
}
/**
* Read memory limit from the php.ini file
*
* @return int Memory limit in bytes
*/
public static function GetMemoryLimit(): int
{
$sLimit = ini_get('memory_limit');
if ($sLimit == '-1') {
return 128 * 1048576;
}
switch (substr($sLimit, -1)) {
case 'M':
case 'm':
return (int)$sLimit * 1048576;
case 'K':
case 'k':
return (int)$sLimit * 1024;
case 'G':
case 'g':
return (int)$sLimit * 1073741824;
default:
return (int)$sLimit;
}
}
}

View File

@@ -4,7 +4,7 @@
"type": "project",
"license": "AGPL-3.0-only",
"require": {
"php": ">=8.2.0 <8.5.0",
"php": ">=8.1.0 <8.4.0",
"ext-ctype": "*",
"ext-dom": "*",
"ext-gd": "*",
@@ -12,50 +12,38 @@
"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": "dev-master",
"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",
"soundasleep/html2text": "~2.1",
"symfony/console": "~6.4.0",
"symfony/dotenv": "~6.4.0",
"symfony/form": "^6.4",
"symfony/framework-bundle": "~6.4.0",
"symfony/http-foundation": "~6.4.0",
"symfony/http-kernel": "~6.4.0",
"symfony/runtime": "~6.4.0",
"symfony/security-csrf": "~6.4.0",
"symfony/mailer": "^6.4",
"symfony/security-csrf": "^6.4",
"symfony/twig-bundle": "~6.4.0",
"symfony/validator" : "~6.4.0",
"symfony/var-dumper": "~6.4.0",
"symfony/validator" : "^6.4",
"symfony/yaml": "~6.4.0",
"symfony/mailer": "~6.4.0",
"tecnickcom/tcpdf": "^6.6.0",
"thenetworg/oauth2-azure": "^2.0",
"soundasleep/html2text": "~2.1"
"thenetworg/oauth2-azure": "^2.0"
},
"require-dev": {
"symfony/debug-bundle": "~6.4.0",
"symfony/stopwatch": "~6.4.0",
"symfony/web-profiler-bundle": "~6.4.0"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/Combodo/PHP-Parser"
},
{
"type": "vcs",
"url": "https://github.com/EsupPortail/phpCAS"
},
{
"type": "vcs",
"url": "https://github.com/combodo-itop-libs/scssphp"
}
],
"repositories": [{
"type": "vcs",
"url": "https://github.com/Combodo/PHP-Parser"
}],
"suggest": {
"ext-libsodium": "Required to use the AttributeEncryptedString.",
"ext-openssl": "Can be used as a polyfill if libsodium is not installed",
@@ -67,7 +55,7 @@
},
"config": {
"platform": {
"php": "8.2.0"
"php": "8.1.0"
},
"vendor-dir": "lib",
"preferred-install": {
@@ -75,10 +63,7 @@
},
"sort-packages": true,
"classmap-authoritative": true,
"platform-check": true,
"allow-plugins": {
"symfony/runtime": true
}
"platform-check": true
},
"autoload": {
"classmap": [

1011
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

@@ -24,7 +24,7 @@ MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
MetaModel::IncludeModule('application/audit.domain.class.inc.php');
MetaModel::IncludeModule('application/query.class.inc.php');
MetaModel::IncludeModule('setup/moduleinstallation/moduleinstallation.class.inc.php');
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
MetaModel::IncludeModule('core/event.class.inc.php');
MetaModel::IncludeModule('core/action.class.inc.php');

View File

@@ -199,7 +199,7 @@ class CellStatus_SearchIssue extends CellStatus_Issue
* @param null $sAllowedValues : used for additional message that provides allowed values $sAllowedValues for current class
* @param string|null $sAllowedValuesSearch : used to search all allowed values
*/
public function __construct($sSerializedSearch, $sReason, $sClass = null, $sAllowedValues = null, ?string $sAllowedValuesSearch = null)
public function __construct($sSerializedSearch, $sReason, $sClass = null, $sAllowedValues = null, string $sAllowedValuesSearch = null)
{
parent::__construct(null, null, $sReason);
$this->sSerializedSearch = $sSerializedSearch;
@@ -876,7 +876,7 @@ class BulkChange
return $aResults;
}
protected function CreateObject(&$aResult, $iRow, $aRowData, ?CMDBChange $oChange = null)
protected function CreateObject(&$aResult, $iRow, $aRowData, CMDBChange $oChange = null)
{
$oTargetObj = MetaModel::NewObject($this->m_sClass);
@@ -965,7 +965,7 @@ class BulkChange
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
protected function UpdateObject(&$aResult, $iRow, $oTargetObj, $aRowData, ?CMDBChange $oChange = null)
protected function UpdateObject(&$aResult, $iRow, $oTargetObj, $aRowData, CMDBChange $oChange = null)
{
$aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors);
@@ -1008,7 +1008,7 @@ class BulkChange
*
* @throws \BulkChangeException
*/
protected function UpdateMissingObject(&$aResult, $iRow, $oTargetObj, ?CMDBChange $oChange = null)
protected function UpdateMissingObject(&$aResult, $iRow, $oTargetObj, CMDBChange $oChange = null)
{
$aResult[$iRow] = $this->PrepareMissingObject($oTargetObj, $aErrors);
@@ -1043,7 +1043,7 @@ class BulkChange
}
}
public function Process(?CMDBChange $oChange = null)
public function Process(CMDBChange $oChange = null)
{
if ($oChange) {
CMDBObject::SetCurrentChange($oChange);

View File

@@ -20,6 +20,9 @@
*
*/
use Combodo\iTop\Config\Validator\iTopConfigAstValidator;
use Combodo\iTop\Config\Validator\iTopConfigSyntaxValidator;
define('ITOP_APPLICATION', 'iTop');
define('ITOP_APPLICATION_SHORT', 'iTop');
@@ -75,7 +78,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)
@@ -869,14 +871,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',
@@ -934,7 +928,7 @@ class Config
'type' => 'string',
'description' => 'Actions that are available as direct buttons next to the "Actions" menu',
// examples... not used
'default' => 'UI:Menu:Modify,UI:Menu:New,UI:Menu:impacts_down,UI:Menu:impacts_up',
'default' => 'UI:Menu:Modify,UI:Menu:New',
'value' => 'UI:Menu:Modify',
'source_of_value' => '',
'show_in_conf_sample' => true,
@@ -1179,8 +1173,8 @@ class Config
'tracking_level_linked_set_default' => [
'type' => 'integer',
'description' => 'Default tracking level if not explicitly set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)',
'default' => LINKSET_TRACKING_NONE,
'value' => LINKSET_TRACKING_NONE,
'default' => LINKSET_TRACKING_LIST,
'value' => LINKSET_TRACKING_LIST,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
@@ -1619,7 +1613,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,
@@ -1746,14 +1740,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.',
@@ -1762,14 +1748,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)',
@@ -1976,6 +1954,11 @@ class Config
*/
protected $m_sDefaultLanguage;
/**
* @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
@@ -2044,6 +2027,7 @@ class Config
$this->m_iFastReloadInterval = DEFAULT_FAST_RELOAD_INTERVAL;
$this->m_bSecureConnectionRequired = DEFAULT_SECURE_CONNECTION_REQUIRED;
$this->m_sDefaultLanguage = 'EN US';
$this->m_sExtAuthVariable = DEFAULT_EXT_AUTH_VARIABLE;
$this->m_aCharsets = [];
$this->m_bQueryCacheEnabled = DEFAULT_QUERY_CACHE_ENABLED;
$this->m_iPasswordHashAlgo = DEFAULT_HASH_ALGO;
@@ -2189,6 +2173,7 @@ class Config
$this->m_aModuleSettings = isset($MyModuleSettings) ? $MyModuleSettings : [];
$this->m_sDefaultLanguage = isset($MySettings['default_language']) ? trim($MySettings['default_language']) : 'EN US';
$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'] : [];
@@ -2350,73 +2335,9 @@ class Config
return explode('|', $this->m_aSettings['allowed_login_types']['value']);
}
/**
* @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()
@@ -2512,7 +2433,7 @@ class Config
public function SetExternalAuthenticationVariable($sExtAuthVariable)
{
$this->Set('ext_auth_variable', $sExtAuthVariable);
$this->m_sExtAuthVariable = $sExtAuthVariable;
}
public function SetEncryptionKey($sKey)
@@ -2566,6 +2487,7 @@ class Config
$aSettings['fast_reload_interval'] = $this->m_iFastReloadInterval;
$aSettings['secure_connection_required'] = $this->m_bSecureConnectionRequired;
$aSettings['default_language'] = $this->m_sDefaultLanguage;
$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;
@@ -2668,6 +2590,7 @@ class Config
// Old fashioned remaining values
$aOtherValues = [
'default_language' => $this->m_sDefaultLanguage,
'ext_auth_variable' => $this->m_sExtAuthVariable,
'encryption_key' => $this->m_sEncryptionKey,
'encryption_library' => $this->m_sEncryptionLibrary,
'csv_import_charsets' => $this->m_aCharsets,
@@ -2752,13 +2675,14 @@ class Config
*
* @param array $aParamValues
* @param ?string $sModulesDir
* @param bool $bPreserveModuleSettings
*
* @return void The current object is modified directly
*
* @throws \Exception
* @throws \CoreException
*/
public function UpdateFromParams($aParamValues, $sModulesDir = null)
public function UpdateFromParams($aParamValues, $sModulesDir = null, $bPreserveModuleSettings = false)
{
if (isset($aParamValues['application_path'])) {
$this->Set('app_root_url', $aParamValues['application_path']);
@@ -2806,10 +2730,7 @@ class Config
} else {
$aSelectedModules = null;
}
if (! is_null($sModulesDir)) {
$this->UpdateIncludes($sModulesDir, $aSelectedModules);
}
$this->UpdateIncludes($sModulesDir, $aSelectedModules);
if (isset($aParamValues['source_dir'])) {
$this->Set('source_dir', $aParamValues['source_dir']);
@@ -2827,13 +2748,17 @@ class Config
*
* @throws Exception
*/
public function UpdateIncludes(string $sModulesDir, $aSelectedModules = null)
public function UpdateIncludes($sModulesDir, $aSelectedModules = null)
{
if ($sModulesDir === null) {
return;
}
// Initialize the arrays below with default values for the application...
$oEmptyConfig = new Config('dummy_file', false); // Do NOT load any config file, just set the default values
$aAddOns = $oEmptyConfig->GetAddOns();
$aModules = ModuleDiscovery::GetModulesOrderedByDependencies([APPROOT.$sModulesDir]);
$aModules = ModuleDiscovery::GetAvailableModules([APPROOT.$sModulesDir]);
foreach ($aModules as $sModuleId => $aModuleInfo) {
list($sModuleName, $sModuleVersion) = ModuleDiscovery::GetModuleName($sModuleId);
if (is_null($aSelectedModules) || in_array($sModuleName, $aSelectedModules)) {

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

@@ -425,7 +425,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>
@@ -904,7 +904,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

@@ -2567,7 +2567,7 @@ abstract class DBObject implements iDisplay
*
* @see \RestUtils::FindObjectFromKey for the same check in the REST endpoint
*/
final public function CheckChangedExtKeysValues(?callable $oIsObjectLoadableCallback = null)
final public function CheckChangedExtKeysValues(callable $oIsObjectLoadableCallback = null)
{
if (is_null($oIsObjectLoadableCallback)) {
$oIsObjectLoadableCallback = function ($sClass, $sId) {
@@ -3727,7 +3727,7 @@ abstract class DBObject implements iDisplay
* @throws \MySQLException
* @throws \OQLException
*/
private function ActivateOnObjectUpdateTriggers(?DBObject $oObject, ?array $aAttributes = null): void
private function ActivateOnObjectUpdateTriggers(?DBObject $oObject, array $aAttributes = null): void
{
if (is_null($oObject)) {
return;
@@ -5108,8 +5108,8 @@ abstract class DBObject implements iDisplay
protected function GetReferencingObjectsForDeletion($bAllowAllData = false)
{
$aDependentObjects = [];
$aReferencingMe = MetaModel::EnumReferencingClasses(get_class($this));
foreach ($aReferencingMe as $sRemoteClass => $aExtKeys) {
$aRererencingMe = MetaModel::EnumReferencingClasses(get_class($this));
foreach ($aRererencingMe as $sRemoteClass => $aExtKeys) {
/** @var \AttributeExternalKey $oExtKeyAttDef */
foreach ($aExtKeys as $sExtKeyAttCode => $oExtKeyAttDef) {
// skip if external key doesn't require the deletion cascading

View File

@@ -1932,37 +1932,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

@@ -650,7 +650,7 @@ abstract class DBSearch
*
* @throws OQLException
*/
public static function FromOQL($sQuery, $aParams = null, ?ModelReflection $oMetaModel = null)
public static function FromOQL($sQuery, $aParams = null, ModelReflection $oMetaModel = null)
{
if (empty($sQuery)) {
return null;
@@ -1048,7 +1048,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) {
@@ -1510,33 +1524,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

@@ -59,7 +59,7 @@ class DBUnionSearch extends DBSearch
public function AllowAllData($bAllowAllData = true)
{
foreach ($this->aSearches as $oSearch) {
$oSearch->AllowAllData($bAllowAllData);
$oSearch->AllowAllData();
}
}
@@ -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

@@ -360,10 +360,10 @@ class DesignElement extends \DOMElement
* @param string $sTagName
* @param string|null $sDefault
*
* @return null|string
* @return string
* @throws \DOMFormatException
*/
public function GetChildText($sTagName, $sDefault = null): ?string
public function GetChildText($sTagName, $sDefault = null)
{
$sRet = $sDefault;
if ($oChild = $this->GetOptionalElement($sTagName)) {
@@ -453,7 +453,7 @@ class DesignElement extends \DOMElement
* @throws Exception
* @since 3.1.2 3.2.0 N°6974
*/
public static function _FindNode(DOMNode $oParent, DesignElement $oRefNode, ?string $sSearchId = null): ?DesignElement
public static function _FindNode(DOMNode $oParent, DesignElement $oRefNode, string $sSearchId = null): ?DesignElement
{
$oNodes = self::_FindNodes($oParent, $oRefNode, $sSearchId);
if ($oNodes instanceof DOMNodeList) {
@@ -477,7 +477,7 @@ class DesignElement extends \DOMElement
* @return \DOMNodeList|false|mixed
* @since 3.1.2 3.2.0 N°6974
*/
public static function _FindNodes(DOMNode $oParent, DesignElement $oRefNode, ?string $sSearchId = null)
public static function _FindNodes(DOMNode $oParent, DesignElement $oRefNode, string $sSearchId = null)
{
if ($oParent instanceof DOMDocument) {
$oDoc = $oParent->firstChild->ownerDocument;

View File

@@ -632,7 +632,7 @@ class DisplayableGroupNode extends DisplayableNode
$this->aObjects = [];
}
public function AddObject(?DBObject $oObj = null)
public function AddObject(DBObject $oObj = null)
{
if (is_object($oObj)) {
$sPrevClass = $this->GetObjectClass();

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" => "",
@@ -156,7 +156,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" => "",
@@ -178,7 +178,7 @@ class EventNotificationEmail extends EventNotification
// Display lists
MetaModel::Init_SetZListItems('details', ['date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_class', '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
@@ -192,7 +192,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" => "",
@@ -286,7 +286,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" => "",
@@ -321,7 +321,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" => "",
@@ -356,7 +356,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" => "",
@@ -394,7 +394,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

@@ -259,18 +259,13 @@ class InlineImage extends DBObject
* that refer to an InlineImage (detected via the attribute data-img-id="") so that
* the URL is consistent with the current URL of the application.
*
N°8681 * @param string|null $sHtml The HTML fragment to process
* @param string $sHtml The HTML fragment to process
*
* @return string The modified HTML
* @throws \Exception
*
* @since 3.3.0 N°8681 Add type hint for parameters and return value
*/
public static function FixUrls(string|null $sHtml): string
public static function FixUrls($sHtml)
{
// N°8681 - Ensure to have a string value
$sHtml = $sHtml ?? '';
$aNeedles = [];
$aReplacements = [];
// Find img tags with an attribute data-img-id
@@ -298,46 +293,6 @@ N°8681 * @param string|null $sHtml The HTML fragment to process
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

@@ -691,7 +691,7 @@ abstract class LogAPI
static::$m_oMockMetaModelConfig = $oMetaModelConfig;
}
public static function Exception(string $sMessage, throwable $oException, ?string $sChannel = null, array $aContext = []): void
public static function Exception(string $sMessage, throwable $oException, string $sChannel = null, array $aContext = []): void
{
$aErrorLogs = [];
$aErrorLogs[] = static::PrepareErrorLog($sMessage, $oException, $aContext);

View File

@@ -22,8 +22,6 @@ use Combodo\iTop\Application\EventRegister\ApplicationEvents;
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
use Combodo\iTop\Setup\ModuleDependency\Module;
use Combodo\iTop\Setup\ModuleDiscovery\ModuleFileReader;
require_once APPROOT.'core/modulehandler.class.inc.php';
require_once APPROOT.'core/querymodifier.class.inc.php';
@@ -130,7 +128,7 @@ abstract class MetaModel
/** @var array */
private static $m_aClassToFile = [];
/** @var string */
protected static $m_sEnvironment = ITOP_DEFAULT_ENV;
protected static $m_sEnvironment = 'production';
/**
* Objects currently created/updated.
@@ -464,43 +462,6 @@ abstract class MetaModel
return call_user_func([$sClass, 'GetClassDescription'], $sClass);
}
/**
* @param string $sClass
*
* @return string
* @throws \CoreException
*/
final public static function GetModuleName($sClass)
{
try {
$oReflectionClass = new ReflectionClass($sClass);
$sDir = realpath(dirname($oReflectionClass->getFileName()));
$sApproot = realpath(APPROOT);
while (($sDir !== $sApproot) && (str_contains($sDir, $sApproot))) {
$aFiles = glob("$sDir/module.*.php");
if (count($aFiles) > 1) {
return 'core';
}
if (count($aFiles) == 0) {
$sDir = realpath(dirname($sDir));
continue;
}
$sModuleFilePath = $aFiles[0];
$aModuleInfo = ModuleFileReader::GetInstance()->ReadModuleFileInformation($sModuleFilePath);
$sModuleId = $aModuleInfo[ModuleFileReader::MODULE_INFO_ID];
list($sModuleName, ) = ModuleDiscovery::GetModuleName($sModuleId);
return $sModuleName;
}
} catch (\Exception $e) {
throw new CoreException("Cannot find class module", ['class' => $sClass], '', $e);
}
return 'core';
}
/**
* @param string $sClass
*
@@ -5748,7 +5709,7 @@ abstract class MetaModel
* @throws \DictExceptionUnknownLanguage
* @throws \Exception
*/
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = ITOP_DEFAULT_ENV)
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = 'production')
{
// Startup on a new environment is not supported
static $bStarted = false;

View File

@@ -1656,7 +1656,7 @@ class PHP_ParserGenerator_Data
function emit_code($out, PHP_ParserGenerator_Rule $rp, &$lineno)
{
$linecnt = 0;
/* Generate code to do the reduce action */
if ($rp->code) {
$this->tplt_linedir($out, $rp->line, $this->filename);

View File

@@ -1084,7 +1084,7 @@ static public $yy_action = array(
function yy_find_shift_action($iLookAhead)
{
$stateno = $this->yystack[$this->yyidx]->stateno;
/* if ($this->yyidx < 0) return self::YY_NO_ACTION; */
if (!isset(self::$yy_shift_ofst[$stateno])) {
// no shift actions
@@ -1767,7 +1767,7 @@ throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine,
function yy_syntax_error($yymajor, $TOKEN)
{
#line 25 "..\oql-parser.y"
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
#line 1779 "..\oql-parser.php"
}
@@ -1806,7 +1806,7 @@ throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $
// $yyact; /* The parser action. */
// $yyendofinput; /* True if we are at the end of input */
$yyerrorhit = 0; /* True if yymajor has invoked an error */
/* (re)initialize the parser, if necessary */
if ($this->yyidx === null || $this->yyidx < 0) {
/* if ($yymajor == 0) return; // not sure why this was here... */
@@ -1819,7 +1819,7 @@ throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $
array_push($this->yystack, $x);
}
$yyendofinput = ($yymajor==0);
if (self::$yyTraceFILE) {
fprintf(
self::$yyTraceFILE,
@@ -1828,7 +1828,7 @@ throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $
self::$yyTokenName[$yymajor]
);
}
do {
$yyact = $this->yy_find_shift_action($yymajor);
if ($yymajor < self::YYERRORSYMBOL
@@ -2002,7 +2002,7 @@ class OQLParser extends OQLParserRaw
$this->m_sSourceQuery = $sQuery;
// no constructor - parent::__construct();
}
public function doParse($token, $value, $iCurrPosition = 0)
{
$this->m_iColPrev = $this->m_iCol;
@@ -2016,7 +2016,7 @@ class OQLParser extends OQLParserRaw
$this->doParse(0, 0);
return $this->my_result;
}
public function __destruct()
{
// Bug in the original destructor, causing an infinite loop !

View File

@@ -370,7 +370,7 @@ class ormCaseLog
/**
* Produces an HTML representation, aimed at being used within the iTop framework
*/
public function GetAsHTML(?WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
public function GetAsHTML(WebPage $oP = null, $bEditMode = false, $aTransfoHandler = null)
{
$bPrintableVersion = (utils::ReadParam('printable', '0') == '1');

View File

@@ -350,22 +350,20 @@ 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");
}
}
if (($sSecretField != null) && !hash_equals($oObj->Get($sSecretField), $sSecretValue)) {
if (($sSecretField != null) && ($oObj->Get($sSecretField) != $sSecretValue)) {
usleep(200);
throw new Exception("Invalid secret for class '$sClass' - the object does not exist or you are not allowed to view it");
}
/** @var \ormDocument $oDocument */

View File

@@ -93,7 +93,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
* @param DBObjectSet|null $oOriginalSet
* @throws Exception
*/
public function __construct($sHostClass, $sAttCode, ?DBObjectSet $oOriginalSet = null)
public function __construct($sHostClass, $sAttCode, DBObjectSet $oOriginalSet = null)
{
$this->sHostClass = $sHostClass;
$this->sAttCode = $sAttCode;
@@ -470,6 +470,17 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|| ($this->oOriginalSet->GetFilter()->ToOQL() == $oFellow->oOriginalSet->GetFilter()->ToOQL())) {
$bUpdateFromDelta = true;
}
} else {
//@since 3.2.2 N°2364 - API : remove old linkedset persistance
/* Goo pattern to use:
* $oCISet = $oTicket->Get(functioncis_list);
* $oCISet->AddItem(MetaModel::NewObject(lnkFunctionCIToTicket, array(ci_id=> 12345));
* $oCISet->RemoveItem(123456);
* $oTicket->Set(functionalcis_list, $oCISet);
*/
if (!ContextTag::Check(ContextTag::TAG_SETUP)) {
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('old pattern - please get previous value of the linked set, modify it and set it back to the host object');
}
}
if ($bUpdateFromDelta) {

View File

@@ -98,9 +98,9 @@ class ormPassword
$bResult = false;
$aInfo = password_get_info($this->m_sHashed);
if (is_null($aInfo["algo"]) || $aInfo["algo"] === 0) {
// - Unknown algorithm, assume it's a legacy password
//unknown, assume it's a legacy password
$sHashedPwd = $this->ComputeHash($sClearTextPassword);
$bResult = hash_equals($this->m_sHashed, $sHashedPwd);
$bResult = ($this->m_sHashed == $sHashedPwd);
} else {
$bResult = password_verify($sClearTextPassword, $this->m_sHashed);
}

View File

@@ -43,24 +43,24 @@ final class ormTagSet extends ormSet
/**
*
* @param array|null $aItems
* @param array $aTagCodes
*
* @throws \CoreException
* @throws \CoreUnexpectedValue when a code is invalid
*/
public function SetValues($aItems)
public function SetValues($aTagCodes)
{
if (is_null($aItems)) {
$aItems = [];
if (is_null($aTagCodes)) {
$aTagCodes = [];
}
if (!is_array($aItems)) {
throw new CoreUnexpectedValue("Wrong value {$aItems} for {$this->sClass}:{$this->sAttCode}");
if (!is_array($aTagCodes)) {
throw new CoreUnexpectedValue("Wrong value {$aTagCodes} for {$this->sClass}:{$this->sAttCode}");
}
$oTags = [];
$iCount = 0;
$bError = false;
foreach ($aItems as $sTagCode) {
foreach ($aTagCodes as $sTagCode) {
$iCount++;
if (($this->iLimit != 0) && ($iCount > $this->iLimit)) {
$bError = true;

View File

@@ -248,6 +248,45 @@ class RestResultWithObjects extends RestResult
}
}
/**
* @package RESTAPI
* @api
*/
class RestResultWithObjectSets extends RestResultWithObjects
{
private $current_object = null;
public function MakeNewObjectSet()
{
$arr = array();
$this->current_object = &$arr;
$this->objects[] = &$arr;
}
/**
* Report the given object
*
* @api
* @param string $sObjectAlias Name of the subobject, usually the OQL class alias
* @param int $iCode An error code (RestResult::OK is no issue has been found)
* @param string $sMessage Description of the error if any, an empty string otherwise
* @param DBObject $oObject The object being reported
* @param array|null $aFieldSpec An array of class => attribute codes (Cf. RestUtils::GetFieldList). List of the attributes to be reported.
* @param boolean $bExtendedOutput Output all of the link set attributes ?
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function AppendSubObject($sObjectAlias, $iCode, $sMessage, $oObject, $aFieldSpec = null, $bExtendedOutput = false)
{
$oObjRes = ObjectResult::FromDBObject($oObject, $aFieldSpec, $bExtendedOutput, $iCode, $sMessage);
$this->current_object[$sObjectAlias] = $oObjRes;
}
}
/**
* @package RESTAPI
* @api
@@ -500,15 +539,22 @@ class CoreServices implements iRestServiceProvider, iRestInputSanitizer
break;
case 'core/get':
$sClass = RestUtils::GetClass($aParams, 'class');
$sClassParam = RestUtils::GetMandatoryParam($aParams, 'class');
$key = RestUtils::GetMandatoryParam($aParams, 'key');
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
$bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
$sShowFields = RestUtils::GetOptionalParam($aParams, 'output_fields', '*');
$iLimit = (int)RestUtils::GetOptionalParam($aParams, 'limit', 0);
$iPage = (int)RestUtils::GetOptionalParam($aParams, 'page', 1);
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key, $iLimit, self::getOffsetFromLimitAndPage($iLimit, $iPage));
$sTargetClass = $oObjectSet->GetFilter()->GetClass();
// Validate the class(es)
$aClass = explode(',', $sClassParam);
foreach ($aClass as $sClass) {
if (!MetaModel::IsValidClass(trim($sClass))) {
throw new Exception("class '$sClass' is not valid");
}
}
$oObjectSet = RestUtils::GetObjectSetFromKey($sClassParam, $key, $iLimit, self::getOffsetFromLimitAndPage($iLimit, $iPage));
$sTargetClass = $oObjectSet->GetFilter()->GetClass();
if (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ) != UR_ALLOWED_YES) {
$oResult->code = RestResult::UNAUTHORIZED;
@@ -519,19 +565,67 @@ class CoreServices implements iRestServiceProvider, iRestInputSanitizer
} elseif ($iPage < 1) {
$oResult->code = RestResult::INVALID_PAGE;
$oResult->message = "The request page number is not valid. It must be an integer greater than 0";
} else {
if (!$bExtendedOutput && RestUtils::GetOptionalParam($aParams, 'output_fields', '*') != '*') {
$aFields = $aShowFields[$sClass];
//Id is not a valid attribute to optimize
if (in_array('id', $aFields)) {
unset($aFields[array_search('id', $aFields)]);
}
$aAttToLoad = [$oObjectSet->GetClassAlias() => $aFields];
$oObjectSet->OptimizeColumnLoad($aAttToLoad);
} elseif (count($oObjectSet->GetSelectedClasses()) > 1) {
$oResult = new RestResultWithObjectSets();
$aCache = [];
$aShowFields = [];
foreach ($oObjectSet->GetSelectedClasses() as $sSelectedClass) {
$aShowFields = array_merge( $aShowFields, RestUtils::GetFieldList($sSelectedClass, $aParams, 'output_fields', false));
}
while ($oObjects = $oObjectSet->FetchAssoc()) {
$oResult->MakeNewObjectSet();
foreach ($oObjects as $sAlias => $oObject) {
if (!$oObject) {
continue;
}
if (!array_key_exists($sAlias, $aCache)) {
$sClass = get_class($oObject);
$bExtendedOutput = RestUtils::HasRequestedExtendedOutput($sShowFields);
if (!RestUtils::HasRequestedAllOutputFields($sShowFields)) {
$aFields = $aShowFields[$sClass];
//Id is not a valid attribute to optimize
if ($aFields && in_array('id', $aFields)) {
unset($aFields[array_search('id', $aFields)]);
}
$aAttToLoad = [$sAlias => $aFields];
$oObjectSet->OptimizeColumnLoad($aAttToLoad);
}
$aCache[$sAlias] = [
'aShowFields' => $aShowFields,
'bExtendedOutput' => $bExtendedOutput,
];
} else {
$aShowFields = $aCache[$sAlias]['aShowFields'];
$bExtendedOutput = $aCache[$sAlias]['bExtendedOutput'];
}
$oResult->AppendSubObject($sAlias, 0, '', $oObject, $aShowFields, $bExtendedOutput);
}
}
$oResult->message = "Found: ".$oObjectSet->Count();
} else {
$aShowFields =[];
foreach ($aClass as $sSelectedClass) {
$sSelectedClass = trim($sSelectedClass);
$aShowFields = array_merge($aShowFields, RestUtils::GetFieldList($sSelectedClass, $aParams, 'output_fields', false));
}
if (!RestUtils::HasRequestedAllOutputFields($sShowFields) && count($aShowFields) == 1) {
$aFields = $aShowFields[$sClass];
//Id is not a valid attribute to optimize
if (in_array('id', $aFields)) {
unset($aFields[array_search('id', $aFields)]);
}
$aAttToLoad = [$oObjectSet->GetClassAlias() => $aFields];
$oObjectSet->OptimizeColumnLoad($aAttToLoad);
}
while ($oObject = $oObjectSet->Fetch()) {
$oResult->AddObject(0, '', $oObject, $aShowFields, $bExtendedOutput);
$oResult->AddObject(0, '', $oObject, $aShowFields, RestUtils::HasRequestedExtendedOutput($sShowFields));
}
$oResult->message = "Found: ".$oObjectSet->Count();
}

View File

@@ -1182,7 +1182,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;
}
@@ -1375,7 +1375,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

@@ -56,8 +56,6 @@ $ibo-shame--slider--is-round--border-radius: 20px !default;
$ibo-shame--slider--is-round--before--border-radius: 7px !default;
$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;
@@ -128,11 +126,3 @@ input:checked + .slider:before {
.slider.round:before {
border-radius: $ibo-shame--slider--is-round--before--border-radius;
}
/*
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

@@ -22,6 +22,4 @@
@import "medallion-with-blocklist";
@import "field-badge-within-datatable";
@import "jquery-blockui-within-dialog";
@import "jquery-blockui-within-datatable";
@import "badge-with-badge";
@import "extension-details-with-extension-details";
@import "jquery-blockui-within-datatable";

View File

@@ -1,10 +0,0 @@
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-badge--spacing-left--with-same-block: $ibo-spacing-200 !default;
.ibo-badge + .ibo-badge {
margin-left: $ibo-badge--spacing-left--with-same-block;
}

View File

@@ -1,11 +0,0 @@
/*
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-extension-details--margin-top: $ibo-spacing-300 !default;
.ibo-extension-details + .ibo-extension-details,
.ibo-extension-details--information--description .ibo-extension-details {
margin-top: $ibo-extension-details--margin-top;
}

View File

@@ -33,5 +33,4 @@
@import "field-badge";
@import "file-select";
@import "medallion-icon";
@import "toast";
@import "badge";
@import "toast";

View File

@@ -1,41 +0,0 @@
$ibo-badge--padding-x : $ibo-spacing-200 !default;
$ibo-badge--padding-y : $ibo-spacing-100 !default;
$ibo-badge--border-radius : $ibo-border-radius-400 !default;
$ibo-badge-colors: (
'primary': ($ibo-color-primary-100, $ibo-color-primary-900),
'secondary': ($ibo-color-secondary-100, $ibo-color-secondary-900),
'neutral': ($ibo-color-secondary-100, $ibo-color-secondary-900),
'information': ($ibo-color-information-100, $ibo-color-information-900),
'success': ($ibo-color-success-100, $ibo-color-success-900),
'failure': ($ibo-color-danger-100, $ibo-color-danger-900),
'warning': ($ibo-color-warning-100,$ibo-color-warning-900),
'danger': ($ibo-color-danger-100,$ibo-color-danger-900),
'grey' : ($ibo-color-grey-100, $ibo-color-grey-900),
'blue-grey': ($ibo-color-blue-grey-100, $ibo-color-blue-grey-900),
'blue': ($ibo-color-blue-100, $ibo-color-blue-900),
'cyan': ($ibo-color-cyan-100, $ibo-color-cyan-900),
'green': ($ibo-color-green-100, $ibo-color-green-900),
'orange' : ($ibo-color-orange-100, $ibo-color-orange-900),
'red': ($ibo-color-red-100, $ibo-color-red-900),
'pink': ($ibo-color-pink-100, $ibo-color-pink-900),
) !default;
.ibo-badge {
display: inline-block;
white-space: nowrap;
padding : $ibo-badge--padding-y $ibo-badge--padding-x;
border-radius : $ibo-badge--border-radius;
@extend %ibo-font-ral-med-50;
@each $sColor, $aColorValues in $ibo-badge-colors {
$bg-color: nth($aColorValues, 1);
$text-color: nth($aColorValues, 2);
&.ibo-is-#{$sColor} {
background-color: $bg-color;
color: $text-color;
}
}
}

View File

@@ -72,12 +72,9 @@ $ibo-panel--icon--spacing--as-medallion--is-sticking: $ibo-panel--icon--spacing-
$ibo-panel--icon--bottom--as-medallion--is-sticking: -12px !default;
$ibo-panel--icon--border--as-medallion--is-sticking: 1px $ibo-panel--base-border-style $ibo-panel--base-border-color !default;
$ibo-panel--icon-background--size--must-contain: contain !default; // deprecated, to be removed in favor of $ibo-panel--icon-img--size--must-contain
$ibo-panel--icon-background--size--must-cover: cover !default; // deprecated, to be removed in favor of $ibo-panel--icon-img--size--must-cover
$ibo-panel--icon-background--size--must-zoomout: 66.67% !default; // deprecated, to be removed in favor of $ibo-panel--icon-img--size--must-zoomout
$ibo-panel--icon-img--size--must-contain: $ibo-panel--icon-background--size--must-contain !default; // TODO remove when dealing with N°9317
$ibo-panel--icon-img--size--must-cover: $ibo-panel--icon-background--size--must-cover !default; // TODO remove when dealing with N°9317
$ibo-panel--icon-img--size--must-zoomout: $ibo-panel--icon-background--size--must-zoomout !default; // TODO remove when dealing with N°9317
$ibo-panel--icon-background--size--must-contain: contain !default;
$ibo-panel--icon-background--size--must-cover: cover !default;
$ibo-panel--icon-background--size--must-zoomout: 66.67% !default;
$ibo-panel--title--font-size--is-sticking: $ibo-font-size-150 !default;
$ibo-panel--title--color: $ibo-color-grey-900 !default;
@@ -182,29 +179,24 @@ $ibo-panel--is-selectable--body--after--font-size: $ibo-font-size-700 !default;
min-height: $ibo-panel--icon--size;
}
.ibo-panel--icon-img, .ibo-panel--icon-background { // second class is deprecated, remove it when dealing with N°9317
.ibo-panel--icon-background {
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;
background-size: $ibo-panel--icon-background--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-background--must-contain {
background-size: $ibo-panel--icon-background--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;
.ibo-panel--icon-background--must-cover {
background-size: $ibo-panel--icon-background--size--must-cover;
}
.ibo-panel--icon-img--must-zoomout, .ibo-panel--icon-background--must-zoomout { // second class is deprecated, remove it when dealing with N°9317
width: $ibo-panel--icon-img--size--must-zoomout;
height: $ibo-panel--icon-img--size--must-zoomout;
.ibo-panel--icon-background--must-zoomout {
background-size: $ibo-panel--icon-background--size--must-zoomout;
}
.ibo-panel--title {

View File

@@ -247,8 +247,7 @@ $ibo-quick-create--compartment--placeholder-hint--text-color: $ibo-color-grey-70
&:hover {
cursor: pointer;
color: $ibo-hyperlink-color--on-hover;
text-decoration: $ibo-hyperlink-text-decoration--on-hover;
@extend a;
}
.highlight {

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

@@ -11,12 +11,9 @@ $ibo-title--icon--size: 90px !default;
$ibo-title--icon--size-2: 80px !default;
$ibo-title--icon--size-3: 70px !default;
$ibo-title--icon-background--size--must-contain: contain !default; // deprecated, to be removed in favor of $ibo-title--icon-img--size--must-contain
$ibo-title--icon-background--size--must-cover: cover !default; // deprecated, to be removed in favor of $ibo-title--icon-img--size--must-cover
$ibo-title--icon-background--size--must-zoomout: 66.67% !default; // deprecated, to be removed in favor of $ibo-title--icon-img--size--must-zoomout
$ibo-title--icon-img--size--must-contain: $ibo-title--icon-background--size--must-contain !default; // TODO remove when dealing with N°9317
$ibo-title--icon-img--size--must-cover: $ibo-title--icon-background--size--must-cover !default; // TODO remove when dealing with N°9317
$ibo-title--icon-img--size--must-zoomout: $ibo-title--icon-background--size--must-zoomout !default; // TODO remove when dealing with N°9317
$ibo-title--icon-background--size--must-contain: contain !default;
$ibo-title--icon-background--size--must-cover: cover !default;
$ibo-title--icon-background--size--must-zoomout: 66.67% !default;
.ibo-title {
@@ -47,27 +44,24 @@ $ibo-title--icon-img--size--must-zoomout: $ibo-title--icon-background--size--mus
min-height: $ibo-title--icon--size-3;
}
.ibo-title--icon-img, .ibo-title--icon-background { // second class is deprecated, remove it when dealing with N°9317
.ibo-title--icon-background {
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;
background-position: center;
background-repeat: no-repeat;
background-size: $ibo-title--icon-background--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-background--must-contain {
background-size: $ibo-title--icon-background--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-background--must-cover {
background-size: $ibo-title--icon-background--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;
.ibo-title--icon-background--must-zoomout {
background-size: $ibo-title--icon-background--size--must-zoomout;
}
.ibo-title--for-object-details {

View File

@@ -203,9 +203,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

@@ -8,7 +8,6 @@ $ibo-toggler--wrapper--height: 20px !default;
$ibo-toggler--slider--border-radius: $ibo-border-radius-900 !default;
$ibo-toggler--slider--background-color: $ibo-color-secondary-600 !default;
$ibo-toggler--slider--disabled--background-color: $ibo-color-secondary-200 !default;
$ibo-toggler--slider--before--left: 3px !default;
$ibo-toggler--slider--before--bottom: 3px !default;
@@ -18,7 +17,6 @@ $ibo-toggler--slider--before--border-radius: $ibo-border-radius-full !default;
$ibo-toggler--slider--before--background-color: $ibo-color-grey-100 !default;
$ibo-toggler--slider--checked--background-color: $ibo-color-primary-600 !default;
$ibo-toggler--slider--checked-disabled--background-color: $ibo-color-primary-200 !default;
$ibo-toggler--slider--focus--box-shadow: 0 0 1px $ibo-color-primary-600 !default;
$ibo-toggler--label--margin-left: 4px !default;
@@ -63,13 +61,6 @@ $ibo-toggler--label--margin-left: 4px !default;
background-color: $ibo-toggler--slider--checked--background-color;
}
.ibo-toggler--wrapper input:disabled + .ibo-toggler--slider {
background-color: $ibo-toggler--slider--disabled--background-color;
}
.ibo-toggler--wrapper input:checked:disabled + .ibo-toggler--slider {
background-color: $ibo-toggler--slider--checked-disabled--background-color;
}
input:focus + .ibo-toggler--slider {
box-shadow: $ibo-toggler--slider--focus--box-shadow;
}

View File

@@ -15,4 +15,3 @@
@import "wizard-container/wizard-container";
@import "object/all";
@import "activity-panel/all";
@import "extension/all";

View File

@@ -1 +0,0 @@
@import "extension-details";

View File

@@ -1,65 +0,0 @@
$ibo-extension-details--information--metadata--padding: $ibo-spacing-200 !default;
$ibo-extension-details--information--metadata--delimiter: "-" !default;
$ibo-extension-details--information--metadata--color: $ibo-color-grey-700 !default;
$ibo-extension-details--actions--button--padding-y: 3px !default;
$ibo-extension-details--actions--button--padding-x: $ibo-button--padding-x !default;
.ibo-extension-details {
display: inline-flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
}
.ibo-extension-details--information {
flex-grow: 1;
display: flex;
flex-direction: column;
}
.ibo-extension-details--actions {
display: flex;
}
.ibo-extension-details--information--label {
@extend %ibo-font-ral-med-150;
}
.ibo-extension-details--information--metadata {
@extend %ibo-font-ral-med-100;
color: $ibo-extension-details--information--metadata--color;
}
.ibo-extension-details--information--description {
@extend %ibo-font-ral-med-100;
}
.ibo-extension-details--information--metadata span + span:before {
content: $ibo-extension-details--information--metadata--delimiter;
padding-left: $ibo-extension-details--information--metadata--padding;
padding-right: $ibo-extension-details--information--metadata--padding;
}
//ibo-extension-details can have other ibo-extension-details inside its ibo-extension-details--information--description in the setup. We need to only affect direct children
.ibo-extension-details:has(>.ibo-extension-details--actions input:is([type="checkbox"], [type="radio"]):checked){
&>.ibo-extension-details--information>.ibo-extension-details--information--label .ibo-badge.unchecked {
display: none;
}
}
//Merging the two lines below with :is([type="checkbox"], [type="radio"]) will generate a warning in scss compiler
.ibo-extension-details:has(>.ibo-extension-details--actions input[type="checkbox"]:not(:checked)),
.ibo-extension-details:has(>.ibo-extension-details--actions input[type="radio"]:not(:checked)) {
&>.ibo-extension-details--information>.ibo-extension-details--information--label .ibo-badge.checked {
display: none;
}
}
.ibo-extension-details--actions > button {
padding: $ibo-extension-details--actions--button--padding-y $ibo-extension-details--actions--button--padding-x;
}
.ibo-extension-details--actions:has(.toggler-install:not(:disabled)) .ibo-popover-menu--section a[data-resource-id="force_uninstall"] {
display: none;
}

View File

@@ -3,29 +3,9 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-datamodel-viewer--viewer-empty--width: max(20%, #{$ibo-size-700}) !default;
$ibo-datamodel-viewer--parent--spacer--padding-y: $ibo-spacing-0 !default;
$ibo-datamodel-viewer--parent--spacer--padding-x: $ibo-spacing-300 !default;
$ibo-datamodel-viewer--breadcrumb--margin: $ibo-spacing-200 0 !default;
$ibo-datamodel-viewer--classname--font-family: monospace !default;
$ibo-datamodel-viewer--classname--color: $ibo-color-blue-grey-600 !default;
$ibo-datamodel-viewer--tag--category--color: $ibo-color-blue-grey-800 !default;
$ibo-datamodel-viewer--tag--category--icon--color: $ibo-color-blue-grey-600 !default;
$ibo-datamodel-viewer--abstract-class-icon--margin-right: $ibo-spacing-300 !default;
$ibo-datamodel-viewer--abstract-class-icon--after--content: 'A' !default;
$ibo-datamodel-viewer--abstract-class-icon--after--width: 15px !default;
$ibo-datamodel-viewer--abstract-class-icon--after--height: 15px !default;
$ibo-datamodel-viewer--abstract-class-icon--after--line-height: 14px !default;
$ibo-datamodel-viewer--abstract-class-icon--after--border-radius: 50% !default;
$ibo-datamodel-viewer--abstract-class-icon--after--border: 1px solid $ibo-color-cyan-500 !default;
$ibo-datamodel-viewer--abstract-class-icon--after--color: $ibo-color-white-100 !default;
$ibo-datamodel-viewer--abstract-class-icon--after--background-color: $ibo-color-cyan-500 !default;
$ibo-datamodel-viewer--classes-list--selectize-input--background-color: $ibo-color-white-100 !default;
$ibo-datamodel-viewer--classes-list--selectize-input--color: $ibo-color-grey-800 !default;
$ibo-datamodel-viewer--classes-list--selectize-input--border-color: $ibo-color-grey-500 !default;
@@ -36,9 +16,8 @@ $ibo-datamodel-viewer--origin-cell--diameter: 8px !default;
$ibo-datamodel-viewer--origin-cell--border-radius: $ibo-border-radius-full !default;
$ibo-datamodel-viewer--classes-list--height: 100% !default;
$ibo-datamodel-viewer--classes-list--width: $ibo-size-700 !default;
$ibo-datamodel-viewer--classes-list--width: 350px !default;
$ibo-datamodel-viewer--classes-list--padding-left: $ibo-spacing-600 !default;
$ibo-datamodel-viewer--classes-list--padding-top: $ibo-spacing-300 !default;
$ibo-datamodel-viewer--lifecycle--code--color: $ibo-color-grey-700 !default;
$ibo-datamodel-viewer--lifecycle--stimuli--color: $ibo-color-blue-900 !default;
@@ -66,56 +45,6 @@ $ibo-datamodel-viewer--lifecycle-image--margin-bottom: $ibo-spacing-500 !default
.ibo-panel--subtitle{
@extend %ibo-font-ral-nor-150;
}
.ibo-datamodel-viewer--empty{
display: flex;
flex-direction: column;
align-items: center;
svg{
width: $ibo-datamodel-viewer--viewer-empty--width;
}
}
}
.ibo-datamodel-viewer--empty--text {
@extend %ibo-font-ral-med-400
}
.ibo-datamodel-viewer--breadcrumb{
@extend %ibo-font-ral-med-150;
margin: $ibo-datamodel-viewer--breadcrumb--margin;
.ibo-button{
text-transform: none; // unset uppercase
}
}
.ibo-datamodel-viewer--classname{
color: $ibo-datamodel-viewer--classname--color;
@extend %ibo-font-code-100;
}
.ibo-datamodel-viewer--tag--category{
color: $ibo-datamodel-viewer--tag--category--color;
.ibo-object-details--tag-icon{
color:$ibo-datamodel-viewer--tag--category--icon--color;
}
}
.ibo-datamodel-viewer--icon--abstract{
&:after{
content: $ibo-datamodel-viewer--abstract-class-icon--after--content;
display: inline-flex;
justify-content: center;
align-items: center;
width: $ibo-datamodel-viewer--abstract-class-icon--after--width;
height: $ibo-datamodel-viewer--abstract-class-icon--after--height;
line-height: $ibo-datamodel-viewer--abstract-class-icon--after--line-height;
border-radius: $ibo-datamodel-viewer--abstract-class-icon--after--border-radius;
border: $ibo-datamodel-viewer--abstract-class-icon--after--border;
color: $ibo-datamodel-viewer--abstract-class-icon--after--color;
background-color: $ibo-datamodel-viewer--abstract-class-icon--after--background-color;
@extend %ibo-font-ral-bol-50;
}
}
.ibo-datamodel-viewer--parent--spacer{
@@ -151,7 +80,6 @@ $ibo-datamodel-viewer--lifecycle-image--margin-bottom: $ibo-spacing-500 !default
height: $ibo-datamodel-viewer--classes-list--height;
width: $ibo-datamodel-viewer--classes-list--width;
padding-left: $ibo-datamodel-viewer--classes-list--padding-left;
padding-top: $ibo-datamodel-viewer--classes-list--padding-top;
overflow-y: scroll;
}

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.

View File

@@ -29,7 +29,7 @@ $ibo-vendors-selectize--element--active--background: $ibo-color-blue-100 !defaul
$ibo-vendors-selectize--element--active--color: $ibo-color-grey-900 !default;
$ibo-vendors-selectize--dropdown--background-color: $ibo-vendors-selectize-input--background-color !default;
$ibo-vendors-selectize--dropdown--color: $ibo-vendors-selectize-input--color !default;
$ibo-vendors-selectize--dropdown--color: $ibo-vendors-selectize-input--color!default;
$ibo-vendors-selectize--header--padding-x: 8px !default;
$ibo-vendors-selectize--header--padding-y: 5px !default;

View File

@@ -28,15 +28,6 @@ $ibo-vendors-ckeditor--ck-mentions--item--padding-x: $ibo-spacing-300 !default;
$ibo-vendors-ckeditor--ck-mentions--item--padding-y: $ibo-spacing-200 !default;
/* CSS3 variables */
:root {
--ck-content-font-family: inherit;
--ck-content-font-color: inherit;
--ck-content-font-size: inherit;
--ck-content-line-height: inherit;
--ck-content-word-break: inherit;
--ck-content-overflow-wrap: inherit;
}
.ck {
--ck-color-list-button-on-background: #{$ibo-vendors-ckeditor--ck-color-list-button-on-background};
--ck-color-list-button-on-background-focus: #{$ibo-vendors-ckeditor--ck-color-list-button-on-background-focus};

File diff suppressed because one or more lines are too long

View File

@@ -316,34 +316,29 @@ fieldset {
background-color: #F7FAFC;
padding: 10px;
.wiz-choice{
&:not(:checked) ~ label .checked{
&:checked ~ .description {
#itop-ticket-mgmt-simple-ticket-enhanced-portal:not(:checked),
#itop-ticket-mgmt-itil-enhanced-portal:not(:checked) {
~ .description::after {
content: "Legacy portal is no longer part of iTop, by leaving this option unchecked your portal users won't be able to access iTop anymore.";
display: block;
margin-top: 0.5em;
font-weight: bold;
color: $legacy-portal-removal-text-color;
}
}
}
&:not(:checked) ~ label .setup-extension-tag.checked{
display:none;
}
&:checked ~ label .unchecked{
&:checked ~ label .setup-extension-tag.unchecked{
display:none;
}
}
}
.ibo-extension-details:has(>.ibo-extension-details--actions>input:checked) {
.ibo-extension-details:has(#itop-ticket-mgmt-simple-ticket-enhanced-portal:not(:checked), #itop-ticket-mgmt-itil-enhanced-portal:not(:checked)) {
.ibo-extension-details--information--description::after {
content: "Legacy portal is no longer part of iTop, by leaving this option unchecked your portal users won't be able to access iTop anymore.";
display: block;
margin-top: 0.5em;
font-weight: bold;
color: $legacy-portal-removal-text-color;
}
}
}
.ibo-extension-details--information--metadata{
color: $ibo-color-grey-800;
}
.choice-disabled {
color: $ibo-color-grey-700;
}
body {
font-size: 1.17rem;
@@ -527,12 +522,10 @@ body {
}
}
.ibo-setup-summary-title, .ibo-setup-summary-title:visited, .ibo-setup-summary-title:hover {
font-size: $ibo-font-size-150;
color: inherit;
.ibo-setup-summary-title {
font-size: $ibo-font-size-150;
}
#ibo-setup-licenses--components-list {
background-color: $ibo-color-white-200;
padding: 12px;
@@ -612,7 +605,6 @@ body {
color:#a00000;
}
.setup-extension-tag {
display: inline-flex;
background-color: grey;
border-radius: 8px;
padding-left: 3px;
@@ -638,21 +630,6 @@ body {
}
}
.ibo-extension-details {
align-items: flex-start;
}
.ibo-extension-details--actions input{
margin:0.2em 0.5em;
width: 12px;
}
:not(.ibo-badge) ~ .ibo-badge{
margin-left:0.5em;
}
.ibo-extension-details--information--label i{
font-size : 0.9em;
margin-left:0.3em;
}
.setup--wizard-choice--label + .setup--wizard-choice--more-info {
margin-left: 0.5rem;
}
@@ -704,6 +681,9 @@ body {
overflow: auto;
text-align: center;
}
#installation_progress {
display: none;
}
#fresh_content{
border: 0;
min-height: 300px;

View File

@@ -97,19 +97,18 @@
$bDebug = isset($aServerParams['debug']) ? $aServerParams['debug'] : false;
}
if (array_key_exists(LDAP_OPT_DEBUG_LEVEL, $aOptions))
{
// Set debug level before trying to connect, so that debug info appear in the PHP error log if ldap_connect goes wrong
$bRet = ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, $aOptions[LDAP_OPT_DEBUG_LEVEL]);
$this->LogInfo($bDebug, "ldap_set_option('LDAP_OPT_DEBUG_LEVEL', '{$aOptions[LDAP_OPT_DEBUG_LEVEL]}') returned ".($bRet ? 'true' : 'false'));
}
$hDS = @ldap_connect($sURI);
if ($hDS === false)
{
$this->LogIssue($bDebug, "ldap_authentication: can not connect to the LDAP server '$sURI'. Check the configuration file config-itop.php.");
return false;
}
if (array_key_exists(LDAP_OPT_DEBUG_LEVEL, $aOptions))
{
// Set debug level before trying to connect, so that debug info appear in the PHP error log if ldap_connect goes wrong
$bRet = ldap_set_option($hDS, LDAP_OPT_DEBUG_LEVEL, $aOptions[LDAP_OPT_DEBUG_LEVEL]);
$this->LogInfo($bDebug, "ldap_set_option('LDAP_OPT_DEBUG_LEVEL', '{$aOptions[LDAP_OPT_DEBUG_LEVEL]}') returned ".($bRet ? 'true' : 'false'));
}
foreach($aOptions as $name => $value)
{
$bRet = ldap_set_option($hDS, $name, $value);

View File

@@ -2,7 +2,7 @@
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.3">
<module_parameters>
<parameters id="authent-local" _delta="define">
<password_validation.pattern>^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{12,}$</password_validation.pattern>
<password_validation.pattern>^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$</password_validation.pattern>
<password_validation.message type="hash"/>
</parameters>
</module_parameters>

View File

@@ -29,7 +29,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Heslo nemůže uživatel změnit.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Heslo bylo obnoveno',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Termín, kdy bylo heslo změneno',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Heslo musí obsahovat minimálně 12 znaků a musí obsahovat minimálně jedno velké písmeno, jedno malé písmeno, jedno číslo a speciální znak.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Heslo musí obsahovat minimálně 8 znaků a musí obsahovat minimálně jedno velké písmeno, jedno malé písmeno, jedno číslo a speciální znak.',
'UserLocal:password:expiration' => 'Níže uvedená pole vyžadují rozšíření',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Nastavení exspirace "Jednorázového hesla" nelze u vlastního účtu uživatele.',
]);

View File

@@ -28,7 +28,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
]);

View File

@@ -28,7 +28,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
'Class:UserLocal/Attribute:password_renewed_date' => 'Letzte Passworterneuerung',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Letztes Änderungsdatum',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Das Passwort muss mindestens 12 Zeichen lang sein und Großbuchstaben, Kleinbuchstaben, Zahlen und Sonderzeichen enthalten.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Das Passwort entspricht nicht dem in den Konfigurationsregeln hinterlegten RegEx-Ausdruck',
'UserLocal:password:expiration' => 'Die folgenden Felder benötigen eine '.ITOP_APPLICATION_SHORT.' Erweiterung',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Das setzen des Passwortablaufs auf "Einmalpasswort" ist für den eigenen Benutzer nicht erlaubt.',
]);

View File

@@ -55,7 +55,7 @@ Dict::Add('EN US', 'English', 'English', [
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.',
'UserLocal:password:expiration' => 'The fields below require an extension',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User',
]);

View File

@@ -55,7 +55,7 @@ Dict::Add('EN GB', 'British English', 'British English', [
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.',
'UserLocal:password:expiration' => 'The fields below require an extension',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User',
]);

View File

@@ -25,7 +25,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'El usuario no puede cambiar la contraseña.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Renovación de contraseña',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Cuando fue el último cambio de contraseña',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 12 caracteres e incluir mayúsculas, minúsculas, números y caracteres especiales.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 8 caracteres e incluír mayúsculas, minúsculas, números y caracteres especiales.',
'UserLocal:password:expiration' => 'El siguiente campo requiere una extensión',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Configurar expiración de contraseña para "ontraseña de un solo uso" no está permitido para su propio Usuario',
]);

View File

@@ -27,7 +27,7 @@ Dict::Add('FR FR', 'French', 'Français', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '',
'Class:UserLocal/Attribute:password_renewed_date' => 'Mot de passe changé le',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Dernière date à laquelle le mot de passe a été changé',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 12 caractères, avec minuscule, majuscule, nombre et caractère spécial.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Le mot de passe doit contenir au moins 8 caractères, avec minuscule, majuscule, nombre et caractère spécial.',
'UserLocal:password:expiration' => 'Les champs ci-dessous nécessitent une extension',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Impossible de mettre "Usage unique" comme validité du mot de passe pour son propre utilisateur.',
]);

View File

@@ -27,7 +27,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'A felhasználó nem változtathat jelszót.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Jelszó megújítás ideje',
'Class:UserLocal/Attribute:password_renewed_date+' => 'A jelszó legutóbbi módosításának időpontja',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A jelszónak legalább 12 karakterből kell állnia, és tartalmaznia kell nagybetűket, kisbetűket, numerikus és speciális karaktereket.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A jelszónak legalább 8 karakterből kell állnia, és tartalmaznia kell nagybetűket, kisbetűket, numerikus és speciális karaktereket.',
'UserLocal:password:expiration' => 'Az alábbi mezőkhöz egy bővítmény szükséges',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'A jelszó lejárati idejének beállítása "Egyszeri jelszóra" nem engedélyezett a saját Felhasználó számára.',
]);

View File

@@ -27,7 +27,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'La password non può essere cambiata dall\'utente.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Rinnovo della password',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Quando è stata cambiata l\'ultima volta la password',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La password deve essere di almeno 12 caratteri e includere lettere maiuscole, minuscole, numeri e caratteri speciali.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La password deve essere di almeno 8 caratteri e includere lettere maiuscole, minuscole, numeri e caratteri speciali.',
'UserLocal:password:expiration' => 'I campi sottostanti richiedono un\'estensione',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Impostare la scadenza della password su "Password monouso" non è consentito per il proprio utente',
]);

View File

@@ -28,7 +28,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
]);

View File

@@ -28,7 +28,7 @@ Dict::Add('NL NL', 'Dutch', 'Nederlands', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'De gebruiker kan dit wachtwoord niet veranderen.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Wachtwoord laatst aangepast',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Tijdstip waarop het wachtwoord het laatst aangepast werd.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Het wachtwoord bestaat uit minstens 12 tekens en bestaat uit een mix van minstens 1 hoofdletter, kleine letter, cijfer en speciaal teken.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Het wachtwoord bestaat uit minstens 8 tekens en bestaat uit een mix van minstens 1 hoofdletter, kleine letter, cijfer en speciaal teken.',
'UserLocal:password:expiration' => 'De velden hieronder vereisen een extensie.',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Je kan geen eenmalig wachtwoord instellen voor je eigen gebruiker.',
]);

View File

@@ -27,7 +27,7 @@ Dict::Add('PL PL', 'Polish', 'Polski', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Hasło nie może być zmienione przez użytkownika.',
'Class:UserLocal/Attribute:password_renewed_date' => 'Odnowienie hasła',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Kiedy ostatnio zmieniano hasło',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Hasło musi mieć co najmniej 12 znaków i zawierać duże, małe litery, cyfry i znaki specjalne.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Hasło musi mieć co najmniej 8 znaków i zawierać duże, małe litery, cyfry i znaki specjalne.',
'UserLocal:password:expiration' => 'Poniższe pola wymagają rozszerzenia',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Ustawienie wygaśnięcia hasła "Hasło jednorazowe" nie jest dozwolone dla własnego użytkownika',
]);

View File

@@ -28,7 +28,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Дата изменения пароля',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Когда пароль был изменен в последний раз',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Пароль должен содержать не менее 12 символов и включать прописные, строчные, числовые и специальные символы.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Пароль должен содержать не менее 8 символов и включать прописные, строчные, числовые и специальные символы.',
'UserLocal:password:expiration' => 'Поля требуют наличия доп. расширения',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
]);

View File

@@ -27,7 +27,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
]);

View File

@@ -28,7 +28,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 12 characters and include uppercase, lowercase, numeric and special characters.~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'UserLocal:password:expiration' => 'The fields below require an extension~~',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
]);

View File

@@ -51,7 +51,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', [
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => '用户不允许修改密码.',
'Class:UserLocal/Attribute:password_renewed_date' => '密码更新',
'Class:UserLocal/Attribute:password_renewed_date+' => '上次修改密码的时间',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => '密码必须至少12个字符, 包含大小写, 数字和特殊字符.',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => '密码必须至少8个字符, 包含大小写, 数字和特殊字符.',
'UserLocal:password:expiration' => '下面的区域需要插件扩展',
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => '不允许用户为自己设置 "一次性密码" 的失效期限',
]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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