mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-26 05:44:12 +01:00
Compare commits
10 Commits
3.1.2
...
feature/62
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3d880bf2d | ||
|
|
a6a3a02bb4 | ||
|
|
7b36b5f10f | ||
|
|
99a4260287 | ||
|
|
f5967f200a | ||
|
|
2c3b8c0bcd | ||
|
|
beaa50c860 | ||
|
|
dc12eb874c | ||
|
|
248ae03b72 | ||
|
|
a27de39593 |
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 MiB |
@@ -62,12 +62,6 @@ gitGraph
|
||||
commit id: "2022-12-28" tag: "2.7.8"
|
||||
checkout support/3.0
|
||||
commit id: "2023-04-12" tag: "3.0.3"
|
||||
checkout develop
|
||||
commit id: "2023-06-19" tag: "3.1.0-beta" type: REVERSE
|
||||
commit id: "2023-07-26" tag: "3.1.0-1" type: HIGHLIGHT
|
||||
branch support/3.1 order: 840
|
||||
checkout support/3.1
|
||||
commit id: "2023-08-09" tag: "3.1.0-2"
|
||||
```
|
||||
|
||||
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).
|
||||
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).
|
||||
83
.github/pull_request_template.md
vendored
83
.github/pull_request_template.md
vendored
@@ -1,83 +0,0 @@
|
||||
<!--
|
||||
|
||||
IMPORTANT: Please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
|
||||
|
||||
Any PRs not following the guidelines or with missing information will not be considered.
|
||||
|
||||
-->
|
||||
|
||||
## Base information
|
||||
| Question | Answer
|
||||
|---------------------------------------------------------------|--------
|
||||
| Related to a SourceForge thead / Another PR / Combodo ticket? | <!-- Put the URL -->
|
||||
| Type of change? | Bug fix / Enhancement / Translations
|
||||
|
||||
|
||||
## Symptom (bug) / Objective (enhancement)
|
||||
<!--
|
||||
If it's a bug
|
||||
- Explain the symptom in details
|
||||
- If possible put error messages, logs or screenshots (you can paste image directly in this editor).
|
||||
|
||||
If it's an enhancement
|
||||
- Describe what is blocking you, what is the objective with as much details as possible.
|
||||
- Add screenshots if it's related to UI.
|
||||
-->
|
||||
|
||||
|
||||
## Reproduction procedure (bug)
|
||||
<!--
|
||||
Remove this section only if it's NOT a bug.
|
||||
|
||||
Otherwise, explain step by step how to reproduce the issue on a standard iTop Community.
|
||||
|
||||
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
|
||||
-->
|
||||
|
||||
1. On iTop x.y.z <!-- Put complete iTop version (eg. 3.1.0-2) -->
|
||||
2. With PHP x.y.z <!-- Put complete PHP version (eg. 8.1.24) -->
|
||||
2. First go there
|
||||
2. Then do that
|
||||
3. ...
|
||||
4. Finally, see that...
|
||||
|
||||
|
||||
## Cause (bug)
|
||||
<!--
|
||||
Remove this section only if it's NOT a bug.
|
||||
|
||||
Otherwise, explain what is the cause of the issue (where in the code and why)
|
||||
-->
|
||||
|
||||
|
||||
## Proposed solution (bug and enhancement)
|
||||
<!--
|
||||
Explain in details how you are proposing to solve this:
|
||||
- What did you do in the code and why
|
||||
- If you changed something in the UI, put before / after screenshots (you can paste image directly in this editor)
|
||||
-->
|
||||
|
||||
|
||||
## Checklist before requesting a review
|
||||
<!--
|
||||
Don't remove these lines, check them once done.
|
||||
-->
|
||||
- [ ] I have performed a self-review of my code
|
||||
- [ ] I have tested all changes I made on an iTop instance
|
||||
- [ ] I have added a unit test, otherwise I have explained why I couldn't
|
||||
- [ ] Is the PR clear and detailed enough so anyone can understand digging in the code?
|
||||
|
||||
## Checklist of things to do before PR is ready to merge
|
||||
<!--
|
||||
Things that needs to be done in the PR before it can be considered as ready to be merged
|
||||
|
||||
Examples:
|
||||
- Changes requested in the review
|
||||
- Unit test to add
|
||||
- Dictionary entries to translate
|
||||
- ...
|
||||
-->
|
||||
|
||||
- [ ] ...
|
||||
- [ ] ...
|
||||
- [ ] ...
|
||||
16
.github/workflows/action.yml
vendored
16
.github/workflows/action.yml
vendored
@@ -1,16 +0,0 @@
|
||||
name: Add PRs to Combodo PRs Dashboard
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add PR to Combodo Project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v1.0.2
|
||||
with:
|
||||
project-url: https://github.com/orgs/Combodo/projects/5
|
||||
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -37,9 +37,7 @@ tests/*/vendor/*
|
||||
|
||||
# iTop extensions
|
||||
/extensions/**
|
||||
!/extensions/.htaccess
|
||||
!/extensions/readme.txt
|
||||
!/extensions/web.config
|
||||
|
||||
# all logs but listing prevention
|
||||
/log/**
|
||||
@@ -47,10 +45,8 @@ tests/*/vendor/*
|
||||
!/log/index.php
|
||||
!/log/web.config
|
||||
|
||||
# PHPUnit: Cache file, local XML working copies
|
||||
# PHPUnit cache file
|
||||
/tests/php-unit-tests/.phpunit.result.cache
|
||||
/tests/php-unit-tests/phpunit.xml
|
||||
/tests/php-unit-tests/postbuild_integration.xml
|
||||
|
||||
|
||||
# Jetbrains
|
||||
|
||||
@@ -27,7 +27,7 @@ $iTopFolder = __DIR__."/../../../";
|
||||
require_once("$iTopFolder/approot.inc.php");
|
||||
require_once(APPROOT."/application/utils.inc.php");
|
||||
|
||||
if (PHP_SAPI !== 'cli')
|
||||
if (php_sapi_name() !== 'cli')
|
||||
{
|
||||
throw new \Exception('This script can only run from CLI');
|
||||
}
|
||||
@@ -48,4 +48,4 @@ if (!file_exists($sCssFile))
|
||||
{
|
||||
fwrite(STDERR, "Failed to compile $sCssFile, exiting.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ $iTopFolder = __DIR__ . "/../../" ;
|
||||
require_once ("$iTopFolder/approot.inc.php");
|
||||
require_once (APPROOT."/setup/setuputils.class.inc.php");
|
||||
|
||||
if (PHP_SAPI !== 'cli')
|
||||
if (php_sapi_name() !== 'cli')
|
||||
{
|
||||
throw new \Exception('This script can only run from CLI');
|
||||
}
|
||||
@@ -70,4 +70,4 @@ if (false === empty($aMissing)) {
|
||||
echo "Some new tests dirs exists !\n"
|
||||
.' They must be declared either in the allowed or denied list in '.iTopComposer::class." (see N°2651).\n"
|
||||
.' List of dirs:'."\n".var_export($aMissing, true);
|
||||
}
|
||||
}
|
||||
@@ -19,24 +19,17 @@
|
||||
* The target license file path is in `$xmlFilePath`
|
||||
*/
|
||||
|
||||
$iTopFolder = __DIR__."/../../";
|
||||
$xmlFilePath = $iTopFolder."setup/licenses/community-licenses.xml";
|
||||
$iTopFolder = __DIR__ . "/../../" ;
|
||||
$xmlFilePath = $iTopFolder . "setup/licenses/community-licenses.xml";
|
||||
|
||||
$jqExec = shell_exec("jq -V"); // a param is mandatory otherwise the script will freeze
|
||||
if ((null === $jqExec) || (false === $jqExec)) {
|
||||
echo "/!\ JQ is required but cannot be launched :( \n";
|
||||
echo "Check this script PHPDoc block for instructions\n";
|
||||
die(-1);
|
||||
}
|
||||
|
||||
|
||||
function get_scope($product_node) {
|
||||
function get_scope($product_node)
|
||||
{
|
||||
$scope = $product_node->getAttribute("scope");
|
||||
|
||||
if ($scope === "") { //put iTop first
|
||||
if ($scope === "")
|
||||
{ //put iTop first
|
||||
return "aaaaaaaaa";
|
||||
}
|
||||
|
||||
return $scope;
|
||||
}
|
||||
|
||||
|
||||
@@ -161,4 +161,4 @@ We have one sticker per contribution type. You might get multiple stickers with
|
||||
|
||||
Here is the design of each stickers for year 2022:
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -107,8 +107,7 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Rudner, Björn (a.k.a [@rudnerbjoern](https://github.com/rudnerbjoern))
|
||||
- Seki, Shoji
|
||||
- Shilov, Vladimir
|
||||
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya-stukalov))
|
||||
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
|
||||
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
|
||||
- Tulio, Marco
|
||||
- Turrubiates, Miguel
|
||||
|
||||
|
||||
@@ -228,7 +228,6 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"is_link" => true, /** @since 3.1.0 N°6482 */
|
||||
'uniqueness_rules' => array(
|
||||
'no_duplicate' => array(
|
||||
'attributes' => array(
|
||||
@@ -446,12 +445,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
UR_ACTION_BULK_DELETE => 'bd',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array $aUsersProfilesList Cache of users' profiles. Hash array of user ID => [profile ID => profile friendlyname, profile ID => profile friendlyname, ...]
|
||||
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6887
|
||||
*/
|
||||
private $aUsersProfilesList = [];
|
||||
|
||||
// Installation: create the very first user
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
@@ -508,7 +501,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
}
|
||||
|
||||
protected $m_aUserOrgs = array(); // userid -> array of orgid
|
||||
protected $m_aAdministrators = null; // [user id]
|
||||
|
||||
// Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read)
|
||||
protected $m_aObjectActionGrants = array();
|
||||
@@ -565,7 +557,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
|
||||
// Cache
|
||||
$this->m_aObjectActionGrants = array();
|
||||
$this->m_aAdministrators = null;
|
||||
}
|
||||
|
||||
public function LoadCache()
|
||||
@@ -708,10 +699,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
*/
|
||||
private function GetAdministrators()
|
||||
{
|
||||
if ($this->m_aAdministrators === null)
|
||||
static $aAdministrators = null;
|
||||
|
||||
if ($aAdministrators === null)
|
||||
{
|
||||
// Find all administrators
|
||||
$this->m_aAdministrators = array();
|
||||
$aAdministrators = array();
|
||||
$oAdministratorsFilter = new DBObjectSearch('User');
|
||||
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
|
||||
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
|
||||
@@ -724,10 +717,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
$oSet->OptimizeColumnLoad(array('User' => array('login')));
|
||||
while($oUser = $oSet->Fetch())
|
||||
{
|
||||
$this->m_aAdministrators[] = $oUser->GetKey();
|
||||
$aAdministrators[] = $oUser->GetKey();
|
||||
}
|
||||
}
|
||||
return $this->m_aAdministrators;
|
||||
return $aAdministrators;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -764,12 +757,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||
|
||||
$bStatus = null;
|
||||
// Cache user's profiles
|
||||
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
|
||||
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
|
||||
}
|
||||
// Call the API of UserRights because it caches the list for us
|
||||
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
|
||||
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
|
||||
{
|
||||
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
|
||||
if (!is_null($bGrant))
|
||||
@@ -895,16 +884,11 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
// Note: this code is VERY close to the code of IsActionAllowed()
|
||||
$iUser = $oUser->GetKey();
|
||||
|
||||
// Cache user's profiles
|
||||
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
|
||||
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
|
||||
}
|
||||
|
||||
// Note: The object set is ignored because it was interesting to optimize for huge data sets
|
||||
// and acceptable to consider only the root class of the object set
|
||||
$bStatus = null;
|
||||
// Call the API of UserRights because it caches the list for us
|
||||
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
|
||||
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
|
||||
{
|
||||
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
|
||||
if (!is_null($bGrant))
|
||||
@@ -933,9 +917,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @return string|null Find out which attribute is corresponding the dimension 'owner org'
|
||||
* returns null if no such attribute has been found (no filtering should occur)
|
||||
* Find out which attribute is corresponding the the dimension 'owner org'
|
||||
* returns null if no such attribute has been found (no filtering should occur)
|
||||
*/
|
||||
public static function GetOwnerOrganizationAttCode($sClass)
|
||||
{
|
||||
|
||||
@@ -334,7 +334,6 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"is_link" => true, /** @since 3.1.0 N°6482 */
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
@@ -580,10 +579,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
/**
|
||||
* Read and cache organizations allowed to the given user
|
||||
*
|
||||
* @param User $oUser
|
||||
* @param string $sClass (not used here but can be used in overloads)
|
||||
* @param $oUser
|
||||
* @param $sClass (not used here but can be used in overloads)
|
||||
*
|
||||
* @return array keys of the User allowed org
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
|
||||
@@ -277,7 +277,6 @@ class URP_UserProfile extends UserRightsBaseClass
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"is_link" => true, /** @since 3.1.0 N°6482 */
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -238,7 +238,7 @@ class ApplicationContext
|
||||
{
|
||||
$aContextInputBlocks = [];
|
||||
foreach ($this->aValues as $sName => $sValue) {
|
||||
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", $sValue);
|
||||
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", utils::EscapeHtml($sValue));
|
||||
}
|
||||
return $aContextInputBlocks;
|
||||
}
|
||||
|
||||
@@ -335,6 +335,7 @@ abstract class AbstractPreferencesExtension implements iPreferencesExtension
|
||||
* A recommended pattern is to cache data by the mean of static members.
|
||||
*
|
||||
* @api
|
||||
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
|
||||
* @package UIExtensibilityAPI
|
||||
*/
|
||||
interface iApplicationUIExtension
|
||||
@@ -486,6 +487,7 @@ interface iApplicationUIExtension
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
* @deprecated
|
||||
*/
|
||||
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
|
||||
{
|
||||
@@ -558,7 +560,6 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
|
||||
* or through the GUI.
|
||||
*
|
||||
* @api
|
||||
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method. More details on each method PHPDoc.
|
||||
* @package ORMExtensibilityAPI
|
||||
*/
|
||||
interface iApplicationObjectExtension
|
||||
@@ -573,7 +574,6 @@ interface iApplicationObjectExtension
|
||||
* Otherwise, the answer is definitively "yes, the object has changed".
|
||||
*
|
||||
* @api
|
||||
* @deprecated 3.1.0 N°4756 No alternative available, this API was unstable and is abandoned
|
||||
* @param \cmdbAbstractObject $oObject The target object
|
||||
*
|
||||
* @return boolean True if something has changed for the target object
|
||||
@@ -587,7 +587,6 @@ interface iApplicationObjectExtension
|
||||
* Anyhow, this API can be called in other contexts such as the CSV import tool.
|
||||
*
|
||||
* @api
|
||||
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_WRITE event instead
|
||||
* @param \cmdbAbstractObject $oObject The target object
|
||||
*
|
||||
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
|
||||
@@ -602,7 +601,6 @@ interface iApplicationObjectExtension
|
||||
* Please not that it is not possible to cascade deletion by this mean: only stopper issues can be handled.
|
||||
*
|
||||
* @api
|
||||
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_DELETE event instead
|
||||
* @param \cmdbAbstractObject $oObject The target object
|
||||
*
|
||||
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
|
||||
@@ -619,7 +617,6 @@ interface iApplicationObjectExtension
|
||||
* * {@see DBObject::Get()} : for a given attribute the new value that was persisted
|
||||
*
|
||||
* @api
|
||||
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
|
||||
* @param \cmdbAbstractObject $oObject The target object
|
||||
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
|
||||
* once for all the changes made within the current page
|
||||
@@ -636,7 +633,6 @@ interface iApplicationObjectExtension
|
||||
* The method is called right <b>after</b> the object has been written to the database.
|
||||
*
|
||||
* @api
|
||||
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
|
||||
* @param \cmdbAbstractObject $oObject The target object
|
||||
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
|
||||
* once for all the changes made within the current page
|
||||
@@ -651,7 +647,6 @@ interface iApplicationObjectExtension
|
||||
* The method is called right <b>before</b> the object will be deleted from the database.
|
||||
*
|
||||
* @api
|
||||
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_DELETE event instead
|
||||
* @param \cmdbAbstractObject $oObject The target object
|
||||
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
|
||||
* once for all the changes made within the current page
|
||||
@@ -665,7 +660,6 @@ interface iApplicationObjectExtension
|
||||
* Extend this class instead of iApplicationObjectExtension if you don't need to overload all methods
|
||||
*
|
||||
* @api
|
||||
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
|
||||
* @package ORMExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
*/
|
||||
@@ -2015,8 +2009,6 @@ class RestUtils
|
||||
*
|
||||
* @return DBObject The object found
|
||||
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
||||
*
|
||||
* @see DBObject::CheckChangedExtKeysValues() generic method to check that we can access the linked object isn't used in that use case because values can be literal, OQL, friendlyname
|
||||
*/
|
||||
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
|
||||
{
|
||||
@@ -2103,16 +2095,8 @@ class RestUtils
|
||||
elseif (is_string($key))
|
||||
{
|
||||
// OQL
|
||||
try {
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
} catch (Exception $e) {
|
||||
throw new CoreOqlException('Query failed to execute', [
|
||||
'query' => $key,
|
||||
'exception_class' => get_class($e),
|
||||
'exception_message' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Wrong format for key");
|
||||
@@ -2264,25 +2248,34 @@ interface iModuleExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* KPI logging extensibility point
|
||||
* Interface to manipulate ormCaseLog objects after initialization/edition
|
||||
*
|
||||
* KPI Logger extension
|
||||
* @api
|
||||
* @since 3.1.0 N°6275
|
||||
*/
|
||||
interface iKPILoggerExtension
|
||||
interface iOrmCaseLogExtension
|
||||
{
|
||||
/**
|
||||
* Init the statistics collected
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function InitStats();
|
||||
/**
|
||||
* Rebuild API to be able manipulate ormCaseLog after its initialization/modifications
|
||||
* Examples of use: fix ormcase log broken index/shrink huge histories/....
|
||||
* @param string $sLog: ormcaselog description
|
||||
* @param array|null $aIndex: ormcaselog index
|
||||
*
|
||||
* @return bool: indicate whether current ormCaseLog fields were touched
|
||||
*/
|
||||
public function Rebuild(&$sLog, &$aIndex) : bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new KPI to the stats
|
||||
*
|
||||
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKpiLogData
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function LogOperation($oKpiLogData);
|
||||
}
|
||||
/**
|
||||
* Inherit from iOrmCaseLogExtension to manipulate ormCaseLog after its initialization/modifications
|
||||
*
|
||||
* @api
|
||||
* @since 3.1.0 N°6275
|
||||
*/
|
||||
abstract class AbstractOrmCaseLogExtension implements iOrmCaseLogExtension
|
||||
{
|
||||
public function Rebuild(&$sLog, &$aIndex) : bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,15 +34,15 @@ class AuditCategory extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditcategory",
|
||||
"db_key_field" => "id",
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditcategory",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
@@ -74,23 +74,15 @@ class AuditCategory extends cmdbAbstractObject
|
||||
public function GetReportColor($iTotal, $iErrors)
|
||||
{
|
||||
$sResult = 'red';
|
||||
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) ) {
|
||||
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) )
|
||||
{
|
||||
$sResult = 'green';
|
||||
} else if (($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100)) {
|
||||
}
|
||||
else if ( ($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100) )
|
||||
{
|
||||
$sResult = 'orange';
|
||||
}
|
||||
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
public static function GetShortcutActions($sFinalClass)
|
||||
{
|
||||
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
|
||||
if (!in_array('UI:Menu:RunAudit', $aShortcutActions)) {
|
||||
$aShortcutActions[] = 'UI:Menu:RunAudit';
|
||||
}
|
||||
|
||||
return $aShortcutActions;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -35,42 +35,30 @@ class AuditDomain extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"complementary_name_attcode" => array('description'),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditdomain",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-album.svg'),
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditdomain",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-album.svg'),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("description" => "Short name for this category", "allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeImage("icon", array("is_null_allowed" => true, "depends_on" => array(), "display_max_width" => 96, "display_max_height" => 96, "storage_max_width" => 256, "storage_max_height" => 256, "default_image" => null, "always_load_in_tables" => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("categories_list",
|
||||
array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeImage("icon", array("is_null_allowed"=>true, "depends_on"=>array(), "display_max_width"=>96, "display_max_height"=>96, "storage_max_width"=>256, "storage_max_height"=>256, "default_image"=>null, "always_load_in_tables"=>false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("categories_list", array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'icon', 'categories_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description',)); // Attributes to be displayed for a list
|
||||
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
|
||||
}
|
||||
|
||||
public static function GetShortcutActions($sFinalClass)
|
||||
{
|
||||
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
|
||||
if (!in_array('UI:Menu:RunAudit', $aShortcutActions)) {
|
||||
$aShortcutActions[] = 'UI:Menu:RunAudit';
|
||||
}
|
||||
|
||||
return $aShortcutActions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,26 +74,15 @@ class lnkAuditCategoryToAuditDomain extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('category_id', 'domain_id'),
|
||||
"db_table" => "priv_link_audit_category_domain",
|
||||
"db_key_field" => "id",
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('category_id', 'domain_id'),
|
||||
"db_table" => "priv_link_audit_category_domain",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"is_link" => true,
|
||||
'uniqueness_rules' => array(
|
||||
'no_duplicate' => array(
|
||||
'attributes' => array(
|
||||
0 => 'category_id',
|
||||
1 => 'domain_id',
|
||||
),
|
||||
'filter' => '',
|
||||
'disabled' => false,
|
||||
'is_blocking' => true,
|
||||
),
|
||||
),
|
||||
"is_link" => true,
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("targetclass" => "AuditCategory", "jointype" => '', "allowed_values" => null, "sql" => "category_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
|
||||
|
||||
@@ -35,23 +35,23 @@ class AuditRule extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditrule",
|
||||
"db_key_field" => "id",
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditrule",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit.svg'),
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit.svg'),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("query", array("allowed_values" => null, "sql" => "query", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", array("allowed_values" => new ValueSetEnum('true,false'), "sql" => "valid_flag", "default_value" => "true", "is_null_allowed" => false, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("allowed_values" => null, "sql" => "category_id", "targetclass" => "AuditCategory", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("query", array("allowed_values"=>null, "sql"=>"query", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", array("allowed_values"=>new ValueSetEnum('true,false'), "sql"=>"valid_flag", "default_value"=>"true", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("allowed_values"=>null, "sql"=>"category_id", "targetclass"=>"AuditCategory", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values"=>null, "extkey_attcode"=> 'category_id', "target_attcode"=>"name")));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('category_id', 'name', 'description', 'query', 'valid_flag')); // Attributes to be displayed for the complete details
|
||||
@@ -60,16 +60,5 @@ class AuditRule extends cmdbAbstractObject
|
||||
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'category_id')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
|
||||
public static function GetShortcutActions($sFinalClass)
|
||||
{
|
||||
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
|
||||
if (!in_array('UI:Menu:RunAudit', $aShortcutActions)) {
|
||||
$aShortcutActions[] = 'UI:Menu:RunAudit';
|
||||
}
|
||||
|
||||
return $aShortcutActions;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -47,7 +47,6 @@ use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
|
||||
use Combodo\iTop\Service\Links\LinkSetDataTransformer;
|
||||
use Combodo\iTop\Service\Links\LinkSetModel;
|
||||
use Combodo\iTop\Service\TemporaryObjects\TemporaryObjectHelper;
|
||||
|
||||
|
||||
define('OBJECT_PROPERTIES_TAB', 'ObjectProperties');
|
||||
@@ -134,8 +133,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
/** @var string */
|
||||
public const ENUM_INPUT_TYPE_TAGSET = 'tagset';
|
||||
/** @var string */
|
||||
public const ENUM_INPUT_TYPE_TAGSET_LINKEDSET = 'tagset_linkedset';
|
||||
/** @var string */
|
||||
public const ENUM_INPUT_TYPE_RADIO = 'radio';
|
||||
/** @var string */
|
||||
public const ENUM_INPUT_TYPE_CHECKBOX = 'checkbox';
|
||||
@@ -190,6 +187,9 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
/** @var array initial attributes flags cache [attcode]['flags'] */
|
||||
protected $aInitialAttributesFlags;
|
||||
|
||||
protected $iUpdateLoopCount;
|
||||
|
||||
const MAX_UPDATE_LOOP_COUNT = 10;
|
||||
|
||||
/**
|
||||
* @var array First level classname, second level id, value number of calls done
|
||||
@@ -227,6 +227,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$this->sDisplayMode = static::DEFAULT_DISPLAY_MODE;
|
||||
$this->bAllowWrite = false;
|
||||
$this->bAllowDelete = false;
|
||||
$this->iUpdateLoopCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -746,13 +747,7 @@ HTML
|
||||
$oPage->SetCurrentTab($sTabCode, $oAttDef->GetLabel().$sCount, $sTabDescription);
|
||||
|
||||
$aArgs = array('this' => $this);
|
||||
|
||||
$sEditWhen = $oAttDef->GetEditWhen();
|
||||
// Calculate if edit_when allows to edit based on current $bEditMode
|
||||
$bIsEditableBasedOnEditWhen = ($sEditWhen === LINKSET_EDITWHEN_ALWAYS) ||
|
||||
($bEditMode ? $sEditWhen === LINKSET_EDITWHEN_ON_HOST_EDITION : $sEditWhen === LINKSET_EDITWHEN_ON_HOST_DISPLAY);
|
||||
|
||||
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)) || !$bIsEditableBasedOnEditWhen;
|
||||
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
|
||||
if ($bEditMode && (!$bReadOnly)) {
|
||||
$sInputId = $this->m_iFormId.'_'.$sAttCode;
|
||||
$sDisplayValue = ''; // not used
|
||||
@@ -762,9 +757,9 @@ HTML
|
||||
$oPage->add($sHTMLValue);
|
||||
} else {
|
||||
if ($oAttDef->IsIndirect()) {
|
||||
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
|
||||
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
|
||||
} else {
|
||||
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
|
||||
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
|
||||
}
|
||||
$oPage->AddUiBlock($oBlockLinkSetViewTable);
|
||||
}
|
||||
@@ -1174,7 +1169,7 @@ HTML
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param \CMDBObjectSet $oSet
|
||||
* @param array $aExtraParams See possible values in {@see DataTableUIBlockFactory::RenderDataTable()}
|
||||
* @param array $aExtraParams
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \CoreException
|
||||
@@ -2361,7 +2356,6 @@ EOF
|
||||
|
||||
case 'LinkedSet':
|
||||
if ($oAttDef->GetDisplayStyle() === LINKSET_DISPLAY_STYLE_PROPERTY) {
|
||||
$sInputType = self::ENUM_INPUT_TYPE_TAGSET_LINKEDSET;
|
||||
if (array_key_exists('bulk_context', $aArgs)) {
|
||||
$oTagSetBlock = LinkSetUIBlockFactory::MakeForBulkLinkSet($iId, $oAttDef, $value, $sWizardHelperJsVarName, $aArgs['bulk_context']);
|
||||
} else {
|
||||
@@ -2828,33 +2822,33 @@ JS
|
||||
}
|
||||
}
|
||||
// Custom operation for the form ?
|
||||
if (isset($aExtraParams['custom_operation'])) {
|
||||
$sOperation = $aExtraParams['custom_operation'];
|
||||
} else {
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
$sOperation = 'apply_modify';
|
||||
} else {
|
||||
$sOperation = 'apply_new';
|
||||
}
|
||||
}
|
||||
if (isset($aExtraParams['custom_operation'])) {
|
||||
$sOperation = $aExtraParams['custom_operation'];
|
||||
} else {
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
$sOperation = 'apply_modify';
|
||||
} else {
|
||||
$sOperation = 'apply_new';
|
||||
}
|
||||
}
|
||||
|
||||
$oContentBlock = new UIContentBlock();
|
||||
$oPage->AddUiBlock($oContentBlock);
|
||||
$oContentBlock = new UIContentBlock();
|
||||
$oPage->AddUiBlock($oContentBlock);
|
||||
|
||||
$oForm = new Form("form_{$this->m_iFormId}");
|
||||
$oForm->SetAction($sFormAction);
|
||||
$sOnSubmitForm = "let bOnSubmitForm = OnSubmit('form_{$this->m_iFormId}');";
|
||||
if (isset($aExtraParams['js_handlers']['form_on_submit'])) {
|
||||
$oForm->SetOnSubmitJsCode($sOnSubmitForm . $aExtraParams['js_handlers']['form_on_submit']);
|
||||
} else {
|
||||
$oForm->SetOnSubmitJsCode($sOnSubmitForm . "return bOnSubmitForm;");
|
||||
}
|
||||
$oContentBlock->AddSubBlock($oForm);
|
||||
$oForm = new Form("form_{$this->m_iFormId}");
|
||||
$oForm->SetAction($sFormAction);
|
||||
$sOnSubmitForm = "let bOnSubmitForm = OnSubmit('form_{$this->m_iFormId}');";
|
||||
if (isset($aExtraParams['js_handlers']['form_on_submit'])) {
|
||||
$oForm->SetOnSubmitJsCode($sOnSubmitForm.$aExtraParams['js_handlers']['form_on_submit']);
|
||||
} else {
|
||||
$oForm->SetOnSubmitJsCode($sOnSubmitForm."return bOnSubmitForm;");
|
||||
}
|
||||
$oContentBlock->AddSubBlock($oForm);
|
||||
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
// The object already exists in the database, it's a modification
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
|
||||
}
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
// The object already exists in the database, it's a modification
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
|
||||
}
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sOperation));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass));
|
||||
|
||||
@@ -2863,11 +2857,6 @@ JS
|
||||
$oPage->SetTransactionId($iTransactionId);
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
|
||||
|
||||
// Add temporary object watchdog (only on root form)
|
||||
if (!utils::IsXmlHttpRequest()) {
|
||||
$oPage->add_ready_script(TemporaryObjectHelper::GetWatchDogJS($iTransactionId));
|
||||
}
|
||||
|
||||
// TODO 3.0.0: Is this (the if condition, not the code inside) still necessary?
|
||||
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container']) {
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
@@ -2878,34 +2867,34 @@ JS
|
||||
}
|
||||
}
|
||||
|
||||
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
|
||||
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
|
||||
|
||||
$oCancelButton = ButtonUIBlockFactory::MakeForCancel();
|
||||
$oCancelButton->AddCSSClasses(['action', 'cancel']);
|
||||
$oToolbarButtons->AddSubBlock($oCancelButton);
|
||||
$oApplyButton = ButtonUIBlockFactory::MakeForPrimaryAction($sApplyButton, null, null, true);
|
||||
$oApplyButton->AddCSSClass('action');
|
||||
$oToolbarButtons->AddSubBlock($oApplyButton);
|
||||
$bAreTransitionsHidden = isset($aExtraParams['hide_transitions']) && $aExtraParams['hide_transitions'] === true;
|
||||
$aTransitions = $this->EnumTransitions();
|
||||
if (!isset($aExtraParams['custom_operation']) && !$bAreTransitionsHidden && count($aTransitions)) {
|
||||
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
|
||||
$oSetToCheckRights = DBObjectSet::FromObject($this);
|
||||
$oCancelButton = ButtonUIBlockFactory::MakeForCancel();
|
||||
$oCancelButton->AddCSSClasses(['action', 'cancel']);
|
||||
$oToolbarButtons->AddSubBlock($oCancelButton);
|
||||
$oApplyButton = ButtonUIBlockFactory::MakeForPrimaryAction($sApplyButton, null, null, true);
|
||||
$oApplyButton->AddCSSClass('action');
|
||||
$oToolbarButtons->AddSubBlock($oApplyButton);
|
||||
$bAreTransitionsHidden = isset($aExtraParams['hide_transitions']) && $aExtraParams['hide_transitions'] === true;
|
||||
$aTransitions = $this->EnumTransitions();
|
||||
if (!isset($aExtraParams['custom_operation']) && !$bAreTransitionsHidden && count($aTransitions)) {
|
||||
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
|
||||
$oSetToCheckRights = DBObjectSet::FromObject($this);
|
||||
|
||||
$oTransitionPopoverMenu = new PopoverMenu();
|
||||
$sTPMSectionId = 'transitions';
|
||||
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
|
||||
$aStimuli = Metamodel::EnumStimuli($sClass);
|
||||
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
|
||||
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
|
||||
switch ($iActionAllowed) {
|
||||
case UR_ALLOWED_YES:
|
||||
// Button to be displayed on its own on large screens
|
||||
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
|
||||
$oButton->AddCSSClass('action');
|
||||
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
|
||||
$oToolbarButtons->AddSubBlock($oButton);
|
||||
$oTransitionPopoverMenu = new PopoverMenu();
|
||||
$sTPMSectionId = 'transitions';
|
||||
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
|
||||
$aStimuli = Metamodel::EnumStimuli($sClass);
|
||||
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
|
||||
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
|
||||
switch ($iActionAllowed) {
|
||||
case UR_ALLOWED_YES:
|
||||
// Button to be displayed on its own on large screens
|
||||
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
|
||||
$oButton->AddCSSClass('action');
|
||||
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
|
||||
$oToolbarButtons->AddSubBlock($oButton);
|
||||
|
||||
// Button to be displayed in a grouped button on smaller screens
|
||||
$oTPMPopupMenuItem = new JSPopupMenuItem('next_action--'.$oButton->GetId(), $oButton->GetLabel(), "$(`#{$oButton->GetId()}`).trigger(`click`);");
|
||||
@@ -2960,7 +2949,8 @@ JS
|
||||
}
|
||||
|
||||
// Prepare blocker protection to avoid loosing data
|
||||
$sBlockerId = $sClass.':'.$iKey; // Important: This must have the synthax format as in js/layouts/activity-panel/activity-panel.js
|
||||
$sBlockerId = uniqid($sClass.':'.$iKey.':', true);
|
||||
$sConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
||||
$sJSToken = json_encode($sOwnershipToken);
|
||||
$oPage->add_ready_script(<<<JS
|
||||
// Try to release concurrent lock when leaving the page
|
||||
@@ -3017,7 +3007,7 @@ JS
|
||||
$oToolbarButtons->AddCSSClass('ibo-toolbar-top');
|
||||
$oObjectDetails->AddToolbarBlock($oToolbarButtons);
|
||||
// Allow form title customization
|
||||
if (array_key_exists('form_title', $aExtraParams) && $aExtraParams['form_title'] !== null) {
|
||||
if (array_key_exists('form_title', $aExtraParams)) {
|
||||
$oObjectDetails->SetTitle($aExtraParams['form_title']);
|
||||
}
|
||||
}
|
||||
@@ -3060,21 +3050,16 @@ JS
|
||||
|
||||
$oPage->SetCurrentTab('');
|
||||
|
||||
// Static fields values for wizard helper serialization
|
||||
$aWizardHelperStaticValues = [];
|
||||
|
||||
// Add as hidden inputs values that we want displayed if they're readonly
|
||||
if(isset($aExtraParams['forceFieldsSubmission'])){
|
||||
$aExtraFlags = $aExtraParams['fieldsFlags'] ?? [];
|
||||
foreach ($aExtraParams['forceFieldsSubmission'] as $sAttCode) {
|
||||
if(FormHelper::GetAttributeFlagsForObject($this, $sAttCode, $aExtraFlags) & OPT_ATT_READONLY) {
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('attr_'.$sPrefix.$sAttCode, $this->Get($sAttCode)));
|
||||
$aWizardHelperStaticValues[$sAttCode] = $this->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
$sWizardHelperStaticValues = json_encode($aWizardHelperStaticValues);
|
||||
|
||||
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
|
||||
foreach ($aExtraParams as $sName => $value) {
|
||||
@@ -3101,10 +3086,7 @@ JS
|
||||
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||
$sLifecycleStateForWizardHelper = '';
|
||||
if (MetaModel::HasLifecycle($sClass)) {
|
||||
$sLifecycleStateForWizardHelper = $this->GetState();
|
||||
}
|
||||
$sState = $this->GetState();
|
||||
$sSessionStorageKey = $sClass.'_'.$iKey;
|
||||
$sTempId = utils::GetUploadTempId($iTransactionId);
|
||||
$oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId));
|
||||
@@ -3114,10 +3096,9 @@ JS
|
||||
sessionStorage.removeItem('$sSessionStorageKey');
|
||||
|
||||
// Create the object once at the beginning of the page...
|
||||
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sLifecycleStateForWizardHelper');
|
||||
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sState');
|
||||
oWizardHelper$sPrefix.SetFieldsMap($sJsonFieldsMap);
|
||||
oWizardHelper$sPrefix.SetFieldsCount($iFieldsCount);
|
||||
oWizardHelper$sPrefix.SetStaticValues($sWizardHelperStaticValues);
|
||||
EOF
|
||||
);
|
||||
$oPage->add_ready_script(
|
||||
@@ -3409,15 +3390,22 @@ EOF
|
||||
];
|
||||
|
||||
// The list of candidate fields is made of the ordered list of "details" attributes + other attributes
|
||||
// First attributes from the "details" zlist as they were sorted...
|
||||
$aList = $this->FlattenZList(MetaModel::GetZListItems($sClass, 'details'));
|
||||
|
||||
// ... then append forgotten attributes
|
||||
$aAttributes = array();
|
||||
foreach ($this->FlattenZList(MetaModel::GetZListItems($sClass, 'details')) as $sAttCode) {
|
||||
$aAttributes[$sAttCode] = true;
|
||||
}
|
||||
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) {
|
||||
if (!in_array($sAttCode, $aList)) {
|
||||
$aList[] = $sAttCode;
|
||||
if (!array_key_exists($sAttCode, $aAttributes)) {
|
||||
$aAttributes[$sAttCode] = true;
|
||||
}
|
||||
}
|
||||
// Order the fields based on their dependencies, set the fields for which there is only one possible value
|
||||
// and perform this in the order of dependencies to avoid dead-ends
|
||||
$aDeps = array();
|
||||
foreach ($aAttributes as $sAttCode => $trash) {
|
||||
$aDeps[$sAttCode] = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode);
|
||||
}
|
||||
$aList = $this->OrderDependentFields($aDeps);
|
||||
|
||||
$bExistFieldToDisplay = false;
|
||||
foreach ($aList as $sAttCode) {
|
||||
@@ -3712,7 +3700,7 @@ HTML;
|
||||
if ($oAttDef->GetEditClass() == 'Document') {
|
||||
/** @var \ormDocument $oDocument */
|
||||
$oDocument = $this->Get($sAttCode);
|
||||
if (is_object($oDocument) && !$oDocument->IsEmpty()) {
|
||||
if (!$oDocument->IsEmpty()) {
|
||||
$sFieldAsHtml = $this->GetAsHTML($sAttCode);
|
||||
|
||||
$sDisplayLabel = Dict::S('UI:OpenDocumentInNewWindow_');
|
||||
@@ -4530,12 +4518,16 @@ HTML;
|
||||
*/
|
||||
public function DBInsertNoReload()
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
try {
|
||||
$res = parent::DBInsertNoReload();
|
||||
|
||||
$this->SetWarningsAsSessionMessages('create');
|
||||
|
||||
// Invoke extensions after insertion (the object must exist, have an id, etc.)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
|
||||
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
|
||||
}
|
||||
} finally {
|
||||
if (static::IsCrudStackEmpty()) {
|
||||
// Avoid signaling the current object that links were modified
|
||||
@@ -4543,25 +4535,9 @@ HTML;
|
||||
static::FireEventDbLinksChangedForAllObjects();
|
||||
}
|
||||
}
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function PostInsertActions(): void
|
||||
{
|
||||
parent::PostInsertActions();
|
||||
|
||||
// Invoke extensions after insertion (the object must exist, have an id, etc.)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* Attaches InlineImages to the current object
|
||||
@@ -4571,23 +4547,15 @@ HTML;
|
||||
InlineImage::FinalizeInlineImages($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.1 3.2.0 N°6966 We will have only one DBClone method in the future
|
||||
*/
|
||||
protected function DBCloneTracked_Internal($newKey = null)
|
||||
{
|
||||
/** @var cmdbAbstractObject $oNewObj */
|
||||
$oNewObj = MetaModel::GetObject(get_class($this), parent::DBCloneTracked_Internal($newKey));
|
||||
$oNewObj = parent::DBCloneTracked_Internal($newKey);
|
||||
|
||||
// Invoke extensions after insertion (the object must exist, have an id, etc.)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
|
||||
}
|
||||
|
||||
return $oNewObj;
|
||||
@@ -4595,42 +4563,57 @@ HTML;
|
||||
|
||||
public function DBUpdate()
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
$res = 0;
|
||||
|
||||
try {
|
||||
if (count($this->ListChanges()) === 0) {
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
return $this->GetKey();
|
||||
}
|
||||
$res = parent::DBUpdate();
|
||||
|
||||
$this->SetWarningsAsSessionMessages('update');
|
||||
|
||||
// Protection against reentrance (e.g. cascading the update of ticket logs)
|
||||
// Note: This is based on the fix made on r 3190 in DBObject::DBUpdate()
|
||||
if (!MetaModel::StartReentranceProtection($this)) {
|
||||
$sClass = get_class($this);
|
||||
$sKey = $this->GetKey();
|
||||
IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Rejected (reentrance)", LogChannels::DM_CRUD);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
try {
|
||||
// Invoke extensions after the update (could be before)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
|
||||
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
MetaModel::StopReentranceProtection($this);
|
||||
}
|
||||
|
||||
$aChanges = $this->ListChanges();
|
||||
if (count($aChanges) != 0) {
|
||||
$this->iUpdateLoopCount++;
|
||||
if ($this->iUpdateLoopCount > self::MAX_UPDATE_LOOP_COUNT) {
|
||||
$sClass = get_class($this);
|
||||
$sKey = $this->GetKey();
|
||||
$aPlugins = [];
|
||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
|
||||
$aPlugins[] = get_class($oExtensionInstance);
|
||||
}
|
||||
$sPlugins = implode(', ', $aPlugins);
|
||||
IssueLog::Error("CRUD: DBUpdate $sClass::$sKey Update loop detected plugins: $sPlugins", LogChannels::DM_CRUD);
|
||||
} else {
|
||||
return $this->DBUpdate();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (static::IsCrudStackEmpty()) {
|
||||
static::FireEventDbLinksChangedForAllObjects();
|
||||
}
|
||||
}
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function PostUpdateActions(array $aChanges): void
|
||||
{
|
||||
parent::PostUpdateActions($aChanges);
|
||||
|
||||
// Invoke extensions after the update (could be before)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBUpdate()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sMessageIdPrefix
|
||||
*
|
||||
@@ -4650,56 +4633,26 @@ HTML;
|
||||
|
||||
public function DBDelete(&$oDeletionPlan = null)
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
try {
|
||||
parent::DBDelete($oDeletionPlan);
|
||||
} finally {
|
||||
if (static::IsCrudStackEmpty()) {
|
||||
// Avoid signaling the current object that links were modified
|
||||
static::RemoveObjectAwaitingEventDbLinksChanged(get_class($this), $this->GetKey());
|
||||
$this->LogCRUDDebug(__METHOD__, var_export(self::$aObjectsAwaitingEventDbLinksChanged, true));
|
||||
static::FireEventDbLinksChangedForAllObjects();
|
||||
}
|
||||
}
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
|
||||
return $oDeletionPlan;
|
||||
}
|
||||
|
||||
final protected function PreDeleteActions(): void
|
||||
{
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBDelete()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
|
||||
}
|
||||
|
||||
parent::PreDeleteActions();
|
||||
}
|
||||
|
||||
final protected function PostDeleteActions(): void
|
||||
{
|
||||
parent::PostDeleteActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.1 3.2.0 N°6967 We will have only one DBDelete method in the future
|
||||
*/
|
||||
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
|
||||
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
|
||||
{
|
||||
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBDelete()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
|
||||
}
|
||||
|
||||
return parent::DBDeleteTracked_Internal($oDeletionPlan);
|
||||
@@ -4717,16 +4670,9 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$bIsModified = $oExtensionInstance->OnIsModified($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
|
||||
if ($bIsModified) {
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> true");
|
||||
if ($oExtensionInstance->OnIsModified($this))
|
||||
{
|
||||
return true;
|
||||
} else {
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> false");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4778,11 +4724,7 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToWrite()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
|
||||
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
|
||||
{
|
||||
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
|
||||
@@ -4830,11 +4772,7 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToDelete()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
|
||||
if (is_array($aNewIssues) && count($aNewIssues) > 0)
|
||||
{
|
||||
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
|
||||
@@ -5388,7 +5326,7 @@ EOF
|
||||
$aErrors = $oObj->UpdateObjectFromPostedForm('');
|
||||
$bResult = (count($aErrors) == 0);
|
||||
if ($bResult) {
|
||||
[$bResult, $aErrors] = $oObj->CheckToWrite();
|
||||
list($bResult, $aErrors) = $oObj->CheckToWrite();
|
||||
}
|
||||
if ($bPreview) {
|
||||
$sStatus = $bResult ? Dict::S('UI:BulkModifyStatusOk') : Dict::S('UI:BulkModifyStatusError');
|
||||
@@ -5405,11 +5343,6 @@ EOF
|
||||
'errors' => '<p>'.($bResult ? '' : implode('</p><p>', $aErrorsToDisplay)).'</p>',
|
||||
);
|
||||
if ($bResult && (!$bPreview)) {
|
||||
// doing the check will load multiple times same objects :/
|
||||
// but it shouldn't cost too much on execution time
|
||||
// user can mitigate by selecting less extkeys/lnk to set and/or less objects to update 🤷♂️
|
||||
$oObj->CheckChangedExtKeysValues();
|
||||
|
||||
$oObj->DBUpdate();
|
||||
}
|
||||
}
|
||||
@@ -5938,28 +5871,39 @@ JS
|
||||
*/
|
||||
final protected function FireEventCheckToWrite(): void
|
||||
{
|
||||
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE, ['is_new' => $this->IsNew()]);
|
||||
}
|
||||
|
||||
final protected function FireEventBeforeWrite()
|
||||
{
|
||||
$this->FireEvent(EVENT_DB_BEFORE_WRITE, ['is_new' => $this->IsNew()]);
|
||||
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 3.1.0
|
||||
*/
|
||||
final protected function FireEventCreateDone(): void
|
||||
{
|
||||
$this->NotifyAttachedObjectsOnLinkClassModification();
|
||||
$this->FireEventDbLinksChangedForCurrentObject();
|
||||
$this->FireEvent(EVENT_DB_CREATE_DONE);
|
||||
}
|
||||
|
||||
/////////////
|
||||
/// UPDATE
|
||||
///
|
||||
|
||||
/**
|
||||
* @param array $aChanges
|
||||
* @param bool $bIsNew
|
||||
*
|
||||
* @return void
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @since 3.1.0
|
||||
*/
|
||||
final protected function FireEventAfterWrite(array $aChanges, bool $bIsNew): void
|
||||
final protected function FireEventUpdateDone(array $aChanges): void
|
||||
{
|
||||
$this->NotifyAttachedObjectsOnLinkClassModification();
|
||||
$this->RemoveObjectAwaitingEventDbLinksChanged(get_class($this), $this->GetKey());
|
||||
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges]);
|
||||
$this->FireEventDbLinksChangedForCurrentObject();
|
||||
$this->FireEvent(EVENT_DB_UPDATE_DONE, ['changes' => $aChanges]);
|
||||
}
|
||||
|
||||
//////////////
|
||||
@@ -5978,82 +5922,56 @@ JS
|
||||
$this->FireEvent(EVENT_DB_CHECK_TO_DELETE, ['deletion_plan' => $oDeletionPlan]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \CoreException
|
||||
* @since 3.1.2
|
||||
*/
|
||||
final protected function FireEventAboutToDelete(): void
|
||||
{
|
||||
$this->FireEvent(EVENT_DB_ABOUT_TO_DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 3.1.0
|
||||
*/
|
||||
final protected function FireEventAfterDelete(): void
|
||||
final protected function FireEventDeleteDone(): void
|
||||
{
|
||||
$this->NotifyAttachedObjectsOnLinkClassModification();
|
||||
$this->FireEvent(EVENT_DB_AFTER_DELETE);
|
||||
$this->FireEventDbLinksChangedForCurrentObject();
|
||||
$this->FireEvent(EVENT_DB_DELETE_DONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Possibility for linked classes to be notified of current class modification
|
||||
*
|
||||
* If the passed object is an instance of a link class, then will register each remote object for modification using {@see static::RegisterObjectAwaitingEventDbLinksChanged()}
|
||||
* If an external key was modified, register also the previous object that was linked previously.
|
||||
*
|
||||
* @uses static::RegisterObjectAwaitingEventDbLinksChanged()
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*
|
||||
* @throws ArchivedObjectException
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*
|
||||
* @since 3.1.0 N°5906 method creation
|
||||
* @since 3.1.1 3.2.0 N°6228 now just notify attributes having `with_php_computation`
|
||||
* @since 3.1.0 N°5906
|
||||
*/
|
||||
final protected function NotifyAttachedObjectsOnLinkClassModification(): void
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
if (false === MetaModel::IsLinkClass($sClass)) {
|
||||
return;
|
||||
}
|
||||
// previous values in case of link change
|
||||
$aPreviousValues = $this->ListPreviousValuesForUpdatedAttributes();
|
||||
$sClass = get_class($this);
|
||||
$aClassExtKeyAttCodes = MetaModel::GetAttributesList($sClass, [AttributeExternalKey::class]);
|
||||
foreach ($aClassExtKeyAttCodes as $sExternalKeyAttCode) {
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sExternalKeyAttCode);
|
||||
|
||||
if (false === $this->DoesTargetObjectHavePhpComputation($oAttDef)) {
|
||||
continue;
|
||||
$aLnkClassExternalKeys = MetaModel::GetAttributesList($sClass, [AttributeExternalKey::class]);
|
||||
foreach ($aLnkClassExternalKeys as $sExternalKeyAttCode) {
|
||||
/** @var \AttributeExternalKey $oExternalKeyAttDef */
|
||||
$oExternalKeyAttDef = MetaModel::GetAttributeDef($sClass, $sExternalKeyAttCode);
|
||||
$sRemoteClassName = $oExternalKeyAttDef->GetTargetClass();
|
||||
|
||||
$sRemoteObjectId = $this->Get($sExternalKeyAttCode);
|
||||
if ($sRemoteObjectId > 0) {
|
||||
self::RegisterObjectAwaitingEventDbLinksChanged($sRemoteClassName, $sRemoteObjectId);
|
||||
}
|
||||
|
||||
$sTargetObjectId = $this->Get($sExternalKeyAttCode);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
if ($sTargetObjectId > 0) {
|
||||
$this->LogCRUDDebug(__METHOD__, "Add $sTargetClass:$sTargetObjectId for DBLINKS_CHANGED");
|
||||
self::RegisterObjectAwaitingEventDbLinksChanged($sTargetClass, $sTargetObjectId);
|
||||
}
|
||||
|
||||
$sPreviousTargetObjectId = $aPreviousValues[$sExternalKeyAttCode] ?? 0;
|
||||
if ($sPreviousTargetObjectId > 0) {
|
||||
$this->LogCRUDDebug(__METHOD__, "Add $sTargetClass:$sPreviousTargetObjectId for DBLINKS_CHANGED");
|
||||
self::RegisterObjectAwaitingEventDbLinksChanged($sTargetClass, $sPreviousTargetObjectId);
|
||||
$sPreviousRemoteObjectId = $aPreviousValues[$sExternalKeyAttCode] ?? 0;
|
||||
if ($sPreviousRemoteObjectId > 0) {
|
||||
self::RegisterObjectAwaitingEventDbLinksChanged($sRemoteClassName, $sPreviousRemoteObjectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function DoesTargetObjectHavePhpComputation(AttributeExternalKey $oAttDef): bool
|
||||
{
|
||||
/** @var AttributeLinkedSet $oAttDefMirrorLink */
|
||||
$oAttDefMirrorLink = $oAttDef->GetMirrorLinkAttribute();
|
||||
if (is_null($oAttDefMirrorLink) || false === $oAttDefMirrorLink->HasPHPComputation()){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register one object for later EVENT_DB_LINKS_CHANGED event.
|
||||
*
|
||||
@@ -6071,6 +5989,26 @@ JS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the EVENT_DB_LINKS_CHANGED event if current object is registered
|
||||
*
|
||||
* @return void
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 3.1.0 N°5906
|
||||
*/
|
||||
final protected function FireEventDbLinksChangedForCurrentObject(): void
|
||||
{
|
||||
if (true === static::IsEventDBLinksChangedBlocked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sClass = get_class($this);
|
||||
$sId = $this->GetKey();
|
||||
self::FireEventDbLinksChangedForClassId($sClass, $sId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the EVENT_DB_LINKS_CHANGED event if given object is registered, and unregister it
|
||||
*
|
||||
@@ -6099,23 +6037,16 @@ JS
|
||||
// - we have a EVENT_DB_LINKS_CHANGED listener on Ticket that will update impacted items, so it will create new lnkApplicationSolutionToFunctionalCI
|
||||
// We want to avoid launching the listener twice, first here, and secondly after saving the Ticket in the listener
|
||||
// By disabling the event to be fired, we can remove the current object from the attribute !
|
||||
$oObject = MetaModel::GetObject($sClass, $sId, false);
|
||||
self::FireEventDbLinksChangedForObject($oObject);
|
||||
self::RemoveObjectAwaitingEventDbLinksChanged($sClass, $sId);
|
||||
}
|
||||
|
||||
private static function FireEventDbLinksChangedForObject(DBObject $oObject)
|
||||
{
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$oObject = MetaModel::GetObject($sClass, $sId, true);
|
||||
self::SetEventDBLinksChangedBlocked(true);
|
||||
// N°6408 The object can have been deleted
|
||||
if (!is_null($oObject)) {
|
||||
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
|
||||
|
||||
// Update the object if needed
|
||||
if (count($oObject->ListChanges()) !== 0) {
|
||||
$oObject->DBUpdate();
|
||||
}
|
||||
MetaModel::StartReentranceProtection($oObject);
|
||||
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
|
||||
MetaModel::StopReentranceProtection($oObject);
|
||||
if ($oObject->IsModified()) {
|
||||
$oObject->DBUpdate();
|
||||
}
|
||||
self::RemoveObjectAwaitingEventDbLinksChanged($sClass, $sId);
|
||||
cmdbAbstractObject::SetEventDBLinksChangedBlocked(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -918,11 +918,6 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
$bCustomized = false;
|
||||
|
||||
$sDashboardFileSanitized = utils::RealPath(APPROOT.$sDashboardFile, APPROOT);
|
||||
if (false === $sDashboardFileSanitized) {
|
||||
throw new SecurityException('Invalid dashboard file !');
|
||||
}
|
||||
|
||||
// Search for an eventual user defined dashboard
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
@@ -934,7 +929,7 @@ class RuntimeDashboard extends Dashboard
|
||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||
$bCustomized = true;
|
||||
} else {
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
}
|
||||
|
||||
|
||||
@@ -942,7 +937,7 @@ class RuntimeDashboard extends Dashboard
|
||||
$oDashboard = new RuntimeDashboard($sDashBoardId);
|
||||
$oDashboard->FromXml($sDashboardDefinition);
|
||||
$oDashboard->SetCustomFlag($bCustomized);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFile);
|
||||
} else {
|
||||
$oDashboard = null;
|
||||
}
|
||||
@@ -1141,7 +1136,7 @@ JS
|
||||
$oToolbar->AddSubBlock($oActionButton);
|
||||
|
||||
$aActions = array();
|
||||
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
|
||||
$sFile = addslashes($this->sDefinitionFile);
|
||||
$sJSExtraParams = json_encode($aExtraParams);
|
||||
if ($this->HasCustomDashboard()) {
|
||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:EditCustom'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
|
||||
@@ -1264,12 +1259,12 @@ EOF
|
||||
$sOkButtonLabel = Dict::S('UI:Button:Save');
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
|
||||
$sId = utils::HtmlEntities($this->sId);
|
||||
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass);
|
||||
$sId = addslashes($this->sId);
|
||||
$sLayoutClass = addslashes($this->sLayoutClass);
|
||||
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
|
||||
$sAutoReloadSec = (string) $this->iAutoReloadSec;
|
||||
$sTitle = utils::HtmlEntities($this->sTitle);
|
||||
$sFile = utils::HtmlEntities($this->GetDefinitionFile());
|
||||
$sTitle = addslashes($this->sTitle);
|
||||
$sFile = addslashes($this->GetDefinitionFile());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
|
||||
|
||||
@@ -667,7 +667,7 @@ class DashletUnknown extends Dashlet
|
||||
*/
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
$oField = new DesignerXMLField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
|
||||
$oField = new DesignerLongTextField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
|
||||
@@ -869,7 +869,7 @@ class DashletPlainText extends Dashlet
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
$sText = $this->aProperties['text'];
|
||||
$sText = utils::EscapeHtml(Dict::S($sText));
|
||||
$sText = utils::EscapeHtml($sText);
|
||||
$sText = str_replace(array("\r\n", "\n", "\r"), "<br/>", $sText);
|
||||
|
||||
$sId = 'plaintext_'.($bEditMode ? 'edit_' : '').$this->sId;
|
||||
|
||||
@@ -186,33 +186,8 @@
|
||||
</menu>
|
||||
</menus>
|
||||
<events>
|
||||
<event id="EVENT_DB_BEFORE_WRITE" _delta="define">
|
||||
<name>Before create or update</name>
|
||||
<description><![CDATA[An object is about to be written into the database.
|
||||
The object can be modified.]]></description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<replaces>DBObject::OnInsert</replaces>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object inserted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="is_new">
|
||||
<description>Creation flag</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_CHECK_TO_WRITE" _delta="define">
|
||||
<name>Check to write</name>
|
||||
<description><![CDATA[Check an object before it is written into the database (no change possible).
|
||||
Call $this->AddCheckIssue() to signal an issue.]]></description>
|
||||
<description>Check an object before it is written into the database (no change possible). Call DBObject::AddCheckIssue() to signal an issue</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -222,20 +197,14 @@ Call $this->AddCheckIssue() to signal an issue.]]></description>
|
||||
<description>The object to check</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="is_new">
|
||||
<description>Creation flag</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_AFTER_WRITE" _delta="define">
|
||||
<name>After create or update</name>
|
||||
<description><![CDATA[An object has been written into the database.
|
||||
The modifications can be propagated to other objects.]]></description>
|
||||
<event id="EVENT_DB_CREATE_DONE" _delta="define">
|
||||
<description>An object has been created into the database. The modifications can be propagated to other objects.</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -245,13 +214,22 @@ The modifications can be propagated to other objects.]]></description>
|
||||
<description>The object inserted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="is_new">
|
||||
<description>Creation flag</description>
|
||||
<type>boolean</type>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="changes">
|
||||
<description><![CDATA[For updates, the list of changes done during this operation]]></description>
|
||||
<type>array</type>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_UPDATE_DONE" _delta="define">
|
||||
<description>An object has been updated into the database and reloaded.</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<replaces>DBObject::AfterUpdate</replaces>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object updated</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
@@ -260,9 +238,7 @@ The modifications can be propagated to other objects.]]></description>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_CHECK_TO_DELETE" _delta="define">
|
||||
<name>Check to delete</name>
|
||||
<description><![CDATA[Check an object before it is deleted from the database.
|
||||
Call $this->AddDeleteIssue() to signal an issue.]]></description>
|
||||
<description>Check an object before it is deleted from the database. Call DBObject::AddDeleteIssue() to signal an issue</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -278,27 +254,8 @@ Call $this->AddDeleteIssue() to signal an issue.]]></description>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_ABOUT_TO_DELETE" _delta="define">
|
||||
<name>Before delete</name>
|
||||
<description><![CDATA[An object is about to be deleted from the database]]></description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<replaces>cmdbAbstractObject::OnDelete</replaces>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object about to be deleted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_AFTER_DELETE" _delta="define">
|
||||
<name>After delete</name>
|
||||
<description><![CDATA[An object has been deleted into the database]]></description>
|
||||
<event id="EVENT_DB_DELETE_DONE" _delta="define">
|
||||
<description>An object has been deleted into the database</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -314,10 +271,8 @@ Call $this->AddDeleteIssue() to signal an issue.]]></description>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_ENUM_TRANSITIONS" _delta="define">
|
||||
<name>Enum transitions</name>
|
||||
<description><![CDATA[Manage the allowed transitions in current object state.
|
||||
The only action allowed is to deny transitions with $this->DenyTransition()]]></description>
|
||||
<event id="EVENT_DB_BEFORE_APPLY_STIMULUS" _delta="define">
|
||||
<description>A stimulus is about to be applied to an object</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -326,9 +281,89 @@ The only action allowed is to deny transitions with $this->DenyTransition()]]></
|
||||
<description>The object where the stimulus is targeted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="allowed_stimuli">
|
||||
<description>The list of available stimuli in the current state</description>
|
||||
<type>array</type>
|
||||
<event_datum id="stimulus">
|
||||
<description>Current stimulus applied</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="previous_state">
|
||||
<description>Object previous state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="new_state">
|
||||
<description>Object new state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="save_object">
|
||||
<description>The object must be saved in the database</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_AFTER_APPLY_STIMULUS" _delta="define">
|
||||
<description>A stimulus has been applied to an object</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="object">
|
||||
<description>The object where the stimulus is targeted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="stimulus">
|
||||
<description>Current stimulus applied</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="previous_state">
|
||||
<description>Object previous state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="new_state">
|
||||
<description>Object new state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="save_object">
|
||||
<description>The object is asked to be saved in the database</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_APPLY_STIMULUS_FAILED" _delta="define">
|
||||
<description>A stimulus has failed</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
<event_data>
|
||||
<event_datum id="action">
|
||||
<description>The action that failed to apply the stimulus</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="object">
|
||||
<description>The object where the stimulus is targeted</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="stimulus">
|
||||
<description>Current stimulus applied</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="previous_state">
|
||||
<description>Object previous state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="new_state">
|
||||
<description>Object new state</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="save_object">
|
||||
<description>The object must be saved in the database</description>
|
||||
<type>boolean</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
@@ -337,8 +372,7 @@ The only action allowed is to deny transitions with $this->DenyTransition()]]></
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_LINKS_CHANGED" _delta="define">
|
||||
<name>Links on object have changed</name>
|
||||
<description><![CDATA[At least one link class was changed]]></description>
|
||||
<description>At least one link class was changed</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -354,9 +388,7 @@ The only action allowed is to deny transitions with $this->DenyTransition()]]></
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_OBJECT_RELOAD" _delta="define">
|
||||
<name>Object reload</name>
|
||||
<internal>true</internal>
|
||||
<description><![CDATA[An object has been re-loaded from the database]]></description>
|
||||
<description>An object has been re-loaded from the database</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -372,8 +404,7 @@ The only action allowed is to deny transitions with $this->DenyTransition()]]></
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_COMPUTE_VALUES" _delta="define">
|
||||
<name>Recompute object values</name>
|
||||
<description><![CDATA[An object needs to be recomputed after changes]]></description>
|
||||
<description>An object needs to be recomputed after changes</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -390,8 +421,7 @@ The only action allowed is to deny transitions with $this->DenyTransition()]]></
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_ARCHIVE" _delta="define">
|
||||
<name>Object archived</name>
|
||||
<description><![CDATA[An object has been archived]]></description>
|
||||
<description>An object has been archived</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -407,8 +437,7 @@ The only action allowed is to deny transitions with $this->DenyTransition()]]></
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_UNARCHIVE" _delta="define">
|
||||
<name>Object unarchived</name>
|
||||
<description><![CDATA[An object has been unarchived]]></description>
|
||||
<description>An object has been unarchived</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -424,9 +453,7 @@ The only action allowed is to deny transitions with $this->DenyTransition()]]></
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_SET_ATTRIBUTES_FLAGS" _delta="define">
|
||||
<name>Set attributes flags</name>
|
||||
<description><![CDATA[Set object attributes flags.
|
||||
Call $this->AddAttributeFlags() for all the attributes to be set for this target state.]]></description>
|
||||
<description>Set object attributes flags. Call cmdbAbstractObject::AddAttributeFlags() for all the attributes to be set for this target state.</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -437,7 +464,7 @@ Call $this->AddAttributeFlags() for all the attributes to be set for this target
|
||||
</event_datum>
|
||||
<event_datum id="target_state">
|
||||
<description>The target state in which to evaluate the flags</description>
|
||||
<type>string</type>
|
||||
<type>array</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
@@ -446,9 +473,7 @@ Call $this->AddAttributeFlags() for all the attributes to be set for this target
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DB_SET_INITIAL_ATTRIBUTES_FLAGS" _delta="define">
|
||||
<name>Set initial attributes flags</name>
|
||||
<description><![CDATA[Set object initial attributes flags.
|
||||
Call $this->AddInitialAttributeFlags() for all the initial attributes to be set initially.]]></description>
|
||||
<description>Set object initial attributes flags. Call cmdbAbstractObject::AddInitialAttributeFlags() for all the initial attributes to be set initially.</description>
|
||||
<sources>
|
||||
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
|
||||
</sources>
|
||||
@@ -464,8 +489,7 @@ Call $this->AddInitialAttributeFlags() for all the initial attributes to be set
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_DOWNLOAD_DOCUMENT" _delta="define">
|
||||
<name>Document downloaded</name>
|
||||
<description><![CDATA[A document has been downloaded from the GUI]]></description>
|
||||
<description>A document has been downloaded from the GUI</description>
|
||||
<sources>
|
||||
<source id="Document">Document</source>
|
||||
</sources>
|
||||
@@ -485,9 +509,7 @@ Call $this->AddInitialAttributeFlags() for all the initial attributes to be set
|
||||
</event_data>
|
||||
</event>
|
||||
<event id="EVENT_LOGIN" _delta="define">
|
||||
<name>Login</name>
|
||||
<internal>true</internal>
|
||||
<description><![CDATA[Inform the listeners about the connection states]]></description>
|
||||
<description>Inform the listeners about the connection states</description>
|
||||
<event_data>
|
||||
<event_datum id="code">
|
||||
<description>The login step result code (LoginWebPage::EXIT_CODE_...) </description>
|
||||
|
||||
@@ -704,7 +704,7 @@ class DisplayBlock
|
||||
if ($bDoSearch)
|
||||
{
|
||||
// Keep the table_id identifying this table if we're performing a search
|
||||
$sTableId = utils::ReadParam('_table_id_', null, false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
|
||||
$sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data');
|
||||
if ($sTableId != null)
|
||||
{
|
||||
$aExtraParams['table_id'] = $sTableId;
|
||||
@@ -1083,7 +1083,6 @@ JS
|
||||
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams);
|
||||
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
|
||||
}
|
||||
|
||||
// Summary details
|
||||
$aCounts = array();
|
||||
$aStateLabels = array();
|
||||
@@ -1130,18 +1129,6 @@ JS
|
||||
}
|
||||
|
||||
$oBlock = new UIContentBlockWithJSRefreshCallback(null, ["ibo-dashlet-header-dynamic--container"]);
|
||||
|
||||
// N°6394 Make sure to sort elements as defined in the datamodel
|
||||
if (utils::IsNotNullOrEmptyString($sStateAttrCode)) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
|
||||
$aAllowedValues = $oAttDef->GetAllowedValues() ?? [];
|
||||
$AllowedValuesKeys = array_keys($aAllowedValues);
|
||||
|
||||
uksort($aStateLabels, function ($sKey1, $sKey2) use ($AllowedValuesKeys) {
|
||||
return array_search($sKey1, $AllowedValuesKeys) > array_search($sKey2, $AllowedValuesKeys) ? 1 : -1;
|
||||
});
|
||||
}
|
||||
|
||||
foreach ($aStateLabels as $sStateValue => $sStateLabel) {
|
||||
$aCount = $aCounts[$sStateValue];
|
||||
$sHyperlink = $aCount['link'];
|
||||
@@ -1246,10 +1233,6 @@ JS
|
||||
} else {
|
||||
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, null, null, $aRefreshParams);
|
||||
}
|
||||
$sClassDescription = MetaModel::GetClassDescription($sClass);
|
||||
if (utils::IsNotNullOrEmptyString($sClassDescription)) {
|
||||
$oBlock->SetClassDescription($sClassDescription);
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
@@ -1875,13 +1858,7 @@ class MenuBlock extends DisplayBlock
|
||||
/** @var array $aToolkitActions Any "legacy" toolkit menu item, which are now displayed in the same menu as the $aRegularActions, after them */
|
||||
$aToolkitActions = [];
|
||||
|
||||
// Display menu actions only if...
|
||||
if (
|
||||
// ... NOT in a selection mode
|
||||
(!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == ""))
|
||||
// ... "menu" parameter is NOT EXPLICITLY disabled
|
||||
&& (!isset($aExtraParams['menu']) || $aExtraParams['menu'] === "1" || $aExtraParams['menu'] === true)
|
||||
) {
|
||||
if (!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == "")) {
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (utils::IsNotNullOrEmptyString($sContext)) {
|
||||
@@ -2042,7 +2019,7 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
|
||||
//----------------------------------------------------
|
||||
// Any style but NOT \DisplayBlock::ENUM_STYLE_LIST_IN_OBJECT (linksets) actions
|
||||
// Any style but NOT "listInObject" (linksets) actions
|
||||
//----------------------------------------------------
|
||||
if ($this->m_sStyle !== static::ENUM_STYLE_LIST_IN_OBJECT) {
|
||||
switch ($iSetCount) {
|
||||
@@ -2344,15 +2321,10 @@ class MenuBlock extends DisplayBlock
|
||||
false
|
||||
);
|
||||
|
||||
// creation form title
|
||||
if (array_key_exists('creation_in_modal_form_title', $aExtraParams) && $aExtraParams['creation_in_modal_form_title'] !== null) {
|
||||
$oAddLinkActionButton->AddDataAttribute('modal-title', $aExtraParams['creation_in_modal_form_title']);
|
||||
}
|
||||
|
||||
// - If we are used in a Datatable, 'datatable_' will be prefixed to our $sId, so we do the same here
|
||||
$sRealId = $sId;
|
||||
if (in_array($this->m_sStyle, [static::ENUM_STYLE_LIST, 'links', static::ENUM_STYLE_LIST_IN_OBJECT])) {
|
||||
$sRealId = 'datatable_'.$sId;
|
||||
if(in_array($this->m_sStyle, [static::ENUM_STYLE_LIST, 'links', static::ENUM_STYLE_LIST_IN_OBJECT])){
|
||||
$sRealId = 'datatable_' . $sId;
|
||||
}
|
||||
$oAddLinkActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button'])
|
||||
->SetOnClickJsCode("$('#$sRealId').trigger('open_creation_modal.object.itop');");
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -60,24 +60,6 @@ class CoreCannotSaveObjectException extends CoreException
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
public function getTextMessage()
|
||||
{
|
||||
$sTitle = Dict::S('UI:Error:SaveFailed');
|
||||
$sContent = utils::HtmlEntities($sTitle);
|
||||
|
||||
if (count($this->aIssues) == 1) {
|
||||
$sIssue = reset($this->aIssues);
|
||||
$sContent .= utils::HtmlEntities($sIssue);
|
||||
} else {
|
||||
foreach ($this->aIssues as $sError) {
|
||||
$sContent .= " ".utils::HtmlEntities($sError).", ";
|
||||
}
|
||||
}
|
||||
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
|
||||
public function getIssues()
|
||||
{
|
||||
return $this->aIssues;
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6458 object creation
|
||||
*/
|
||||
class InvalidExternalKeyValueException extends CoreUnexpectedValue
|
||||
{
|
||||
private const ENUM_PARAMS_OBJECT = 'current_object';
|
||||
private const ENUM_PARAMS_ATTCODE = 'attcode';
|
||||
private const ENUM_PARAMS_ATTVALUE = 'attvalue';
|
||||
private const ENUM_PARAMS_USER = 'current_user';
|
||||
|
||||
public function __construct($oObject, $sAttCode, $aContextData = null, $oPrevious = null)
|
||||
{
|
||||
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject) . '::' . $oObject->GetKey();
|
||||
$aContextData[self::ENUM_PARAMS_ATTCODE] = $sAttCode;
|
||||
$aContextData[self::ENUM_PARAMS_ATTVALUE] = $oObject->Get($sAttCode);
|
||||
|
||||
$oCurrentUser = UserRights::GetUserObject();
|
||||
if (false === is_null($oCurrentUser)) {
|
||||
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser) . '::' . $oCurrentUser->GetKey();
|
||||
}
|
||||
|
||||
parent::__construct('Attribute pointing to an object that is either non existing or not readable by the current user', $aContextData, '', $oPrevious);
|
||||
}
|
||||
|
||||
public function GetAttCode(): string
|
||||
{
|
||||
return $this->getContextData()[self::ENUM_PARAMS_ATTCODE];
|
||||
}
|
||||
|
||||
public function GetAttValue(): string
|
||||
{
|
||||
return $this->getContextData()[self::ENUM_PARAMS_ATTVALUE];
|
||||
}
|
||||
}
|
||||
@@ -1110,41 +1110,13 @@ $('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId',
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".$this->PrepareValueForRendering()."</textarea>";
|
||||
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".utils::EscapeHtml($this->defaultValue)."</textarea>";
|
||||
}
|
||||
else {
|
||||
$sValue = "<div $sCSSClasses id=\"$sId\">".$this->PrepareValueForRendering()."</div>";
|
||||
$sValue = "<div $sCSSClasses id=\"$sId\">".utils::EscapeHtml($this->defaultValue)."</div>";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null The value itself as expected for rendering. May it be encoded, escaped or else.
|
||||
* @since 3.1.0 N°6405
|
||||
*/
|
||||
protected function PrepareValueForRendering(): ?string
|
||||
{
|
||||
return utils::EscapeHtml($this->defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class DesignerXMLField
|
||||
*
|
||||
* Field to display XML content
|
||||
*
|
||||
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
|
||||
* @since 3.1.0 N°6405
|
||||
*/
|
||||
class DesignerXMLField extends DesignerLongTextField
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function PrepareValueForRendering(): ?string
|
||||
{
|
||||
return utils::EscapeHtml($this->defaultValue, true);
|
||||
}
|
||||
}
|
||||
|
||||
class DesignerIntegerField extends DesignerFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -80,11 +80,6 @@ class LoginBasic extends AbstractLoginFSMExtension
|
||||
{
|
||||
if (Session::Get('login_mode') == 'basic')
|
||||
{
|
||||
$iOnExit = LoginWebPage::getIOnExit();
|
||||
if ($iOnExit === LoginWebPage::EXIT_RETURN)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
||||
}
|
||||
LoginWebPage::HTTP401Error();
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
|
||||
@@ -79,7 +79,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
{
|
||||
self::ResetLoginSession();
|
||||
$iOnExit = LoginWebPage::getIOnExit();
|
||||
if ($iOnExit === LoginWebPage::EXIT_RETURN)
|
||||
if ($iOnExit == LoginWebPage::EXIT_RETURN)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
||||
}
|
||||
@@ -95,12 +95,6 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
{
|
||||
if (!Session::IsSet('login_mode'))
|
||||
{
|
||||
// N°6358 - if EXIT_RETURN was asked, send an error
|
||||
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
}
|
||||
|
||||
// If no plugin validated the user, exit
|
||||
self::ResetLoginSession();
|
||||
exit();
|
||||
@@ -119,11 +113,6 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
Session::Unset('login_temp_auth_user');
|
||||
if (is_null(UserRights::GetUserObject())){
|
||||
//N°7085 avoid infinite loop
|
||||
IssueLog::Error("No user logged in. exit");
|
||||
exit(-1);
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -139,4 +128,4 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,11 +73,6 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
{
|
||||
if (Session::Get('login_mode') == 'external')
|
||||
{
|
||||
$iOnExit = LoginWebPage::getIOnExit();
|
||||
if ($iOnExit === LoginWebPage::EXIT_RETURN)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
||||
}
|
||||
LoginWebPage::HTTP401Error();
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
|
||||
@@ -44,10 +44,6 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
||||
exit;
|
||||
}
|
||||
|
||||
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
// No credentials yet, display the form
|
||||
$oPage = LoginWebPage::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm($this->bForceFormOnError);
|
||||
|
||||
@@ -90,7 +90,7 @@ class LoginWebPage extends NiceWebPage
|
||||
parent::__construct($sTitle);
|
||||
$this->SetStyleSheet();
|
||||
$this->no_cache();
|
||||
$this->add_http_headers();
|
||||
$this->add_xframe_options();
|
||||
}
|
||||
|
||||
public function SetStyleSheet()
|
||||
@@ -248,7 +248,6 @@ class LoginWebPage extends NiceWebPage
|
||||
$oEmail = new Email();
|
||||
$oEmail->SetRecipientTO($sTo);
|
||||
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
|
||||
$sFrom = utils::IsNullOrEmptyString($sFrom) ? MetaModel::GetConfig()->Get('email_default_sender_address') : $sFrom;
|
||||
$oEmail->SetRecipientFrom($sFrom);
|
||||
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject', $oUser->Get('login')));
|
||||
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
|
||||
@@ -392,11 +391,6 @@ class LoginWebPage extends NiceWebPage
|
||||
Session::Unset('can_logoff');
|
||||
Session::Unset('archive_mode');
|
||||
Session::Unset('impersonate_user');
|
||||
Session::Unset('PluginProperties');
|
||||
Session::Unset('UrlMakerClass');
|
||||
Session::Unset('itop_env');
|
||||
Session::Unset('obj_messages');
|
||||
Session::Unset('profile_list');
|
||||
UserRights::_ResetSessionCache();
|
||||
// If it's desired to kill the session, also delete the session cookie.
|
||||
// Note: This will destroy the session, and not just the session data!
|
||||
|
||||
@@ -103,7 +103,7 @@ class ApplicationMenu
|
||||
{
|
||||
self::$sFavoriteSiloQuery = $sOQL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the query used to limit the list of displayed organizations in the drop-down menu
|
||||
* @return string The OQL query returning a list of Organization objects
|
||||
@@ -536,7 +536,7 @@ EOF
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the currently active menu (if any, otherwise the first menu is the default)
|
||||
* @return string The Id of the currently active menu
|
||||
@@ -544,7 +544,7 @@ EOF
|
||||
public static function GetActiveNodeId()
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sMenuId = $oAppContext->GetCurrentValue('menu', null);
|
||||
$sMenuId = $oAppContext->GetCurrentValue('menu', null);
|
||||
if ($sMenuId === null)
|
||||
{
|
||||
$sMenuId = self::GetDefaultMenuId();
|
||||
@@ -654,7 +654,7 @@ abstract class MenuNode
|
||||
|
||||
/**
|
||||
* Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu
|
||||
*/
|
||||
*/
|
||||
protected $m_aEnableStimuli;
|
||||
|
||||
/**
|
||||
@@ -814,7 +814,7 @@ abstract class MenuNode
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a limiting display condition for the same menu node. The conditions will be combined with a AND
|
||||
* @param $oMenuNode MenuNode Another definition of the same menu node, with potentially different access restriction
|
||||
@@ -987,7 +987,7 @@ class TemplateMenuNode extends MenuNode
|
||||
* @var string
|
||||
*/
|
||||
protected $sTemplateFile;
|
||||
|
||||
|
||||
/**
|
||||
* Create a menu item based on a custom template and inserts it into the application's main menu
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
@@ -1058,7 +1058,7 @@ class OQLMenuNode extends MenuNode
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $bSearchFormOpen;
|
||||
|
||||
|
||||
/**
|
||||
* Extra parameters to be passed to the display block to fine tune its appearence
|
||||
*/
|
||||
@@ -1091,7 +1091,7 @@ class OQLMenuNode extends MenuNode
|
||||
// Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects
|
||||
// of the class specified by the OQL...
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set some extra parameters to be passed to the display block to fine tune its appearence
|
||||
* @param array $aParams paramCode => value. See DisplayBlock::GetDisplay for the meaning of the parameters
|
||||
@@ -1111,7 +1111,7 @@ class OQLMenuNode extends MenuNode
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$oTag = new ContextTag(ContextTag::TAG_OBJECT_SEARCH);
|
||||
ContextTag::AddContext(ContextTag::TAG_OBJECT_SEARCH);
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
|
||||
OQLMenuNode::RenderOQLSearch
|
||||
(
|
||||
@@ -1120,7 +1120,7 @@ class OQLMenuNode extends MenuNode
|
||||
'Menu_'.$this->GetMenuId(),
|
||||
$this->bSearch, // Search pane
|
||||
$this->bSearchFormOpen, // Search open
|
||||
$oPage,
|
||||
$oPage,
|
||||
array_merge($this->m_aParams, $aExtraParams),
|
||||
true
|
||||
);
|
||||
@@ -1354,10 +1354,10 @@ class NewObjectMenuNode extends MenuNode
|
||||
{
|
||||
// Enable this menu, only if the current user has enough rights to create such an object, or an object of
|
||||
// any child class
|
||||
|
||||
|
||||
$aSubClasses = MetaModel::EnumChildClasses($this->sClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
|
||||
$bActionIsAllowed = false;
|
||||
|
||||
|
||||
foreach($aSubClasses as $sCandidateClass)
|
||||
{
|
||||
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
@@ -1366,7 +1366,7 @@ class NewObjectMenuNode extends MenuNode
|
||||
break; // Enough for now
|
||||
}
|
||||
}
|
||||
return $bActionIsAllowed;
|
||||
return $bActionIsAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1508,7 +1508,7 @@ class DashboardMenuNode extends MenuNode
|
||||
throw new Exception("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1549,7 +1549,7 @@ class ShortcutContainerMenuNode extends MenuNode
|
||||
$sName = $this->GetMenuId().'_'.$oShortcut->GetKey();
|
||||
new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
|
||||
}
|
||||
|
||||
|
||||
// Complete the tree
|
||||
//
|
||||
parent::PopulateChildMenus();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -74,7 +74,6 @@ abstract class Query extends cmdbAbstractObject
|
||||
"default_value" => 0,
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array(),
|
||||
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("export_last_date", array(
|
||||
@@ -83,7 +82,6 @@ abstract class Query extends cmdbAbstractObject
|
||||
"default_value" => null,
|
||||
"is_null_allowed" => true,
|
||||
"depends_on" => array(),
|
||||
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("export_last_user_id",
|
||||
@@ -95,16 +93,14 @@ abstract class Query extends cmdbAbstractObject
|
||||
"depends_on"=>array(),
|
||||
"display_style"=>'select',
|
||||
"always_load_in_tables"=>false,
|
||||
"on_target_delete"=>DEL_SILENT,
|
||||
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
|
||||
"on_target_delete"=>DEL_SILENT
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("export_last_user_contact",
|
||||
array(
|
||||
"allowed_values"=>null,
|
||||
"extkey_attcode"=> "export_last_user_id",
|
||||
"target_attcode"=>"contactid",
|
||||
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
|
||||
"target_attcode"=>"contactid"
|
||||
)));
|
||||
|
||||
// Display lists
|
||||
@@ -296,7 +292,7 @@ class QueryOQL extends Query
|
||||
}
|
||||
catch
|
||||
(OQLException $e) {
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:RunQuery:Error'), $e->getHtmlDesc())
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::Format('UI:RunQuery:Error'), $e->getHtmlDesc())
|
||||
->SetIsClosable(false)
|
||||
->SetIsCollapsible(false);
|
||||
$oAlert->AddCSSClass('mb-5');
|
||||
|
||||
@@ -15,7 +15,10 @@
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
|
||||
require_once(APPROOT.'core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'application/utils.inc.php');
|
||||
@@ -99,10 +102,7 @@ else
|
||||
Session::Set('itop_env', ITOP_DEFAULT_ENV);
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
try {
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||
}
|
||||
catch (MySQLException $e) {
|
||||
IssueLog::Debug($e->getMessage());
|
||||
throw new MySQLException('Could not connect to the DB server', []);
|
||||
}
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
|
||||
EventService::InitService();
|
||||
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
|
||||
|
||||
@@ -163,11 +163,13 @@ class UIExtKeyWidget
|
||||
$oPage->add_linked_script('../js/extkeywidget.js');
|
||||
$oPage->add_linked_script('../js/forms-json-utils.js');
|
||||
|
||||
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_MODIFY) && $bAllowTargetCreation);
|
||||
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
$bExtensions = true;
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
|
||||
|
||||
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
|
||||
if ($this->bSearchMode) {
|
||||
$sWizHelper = 'null';
|
||||
@@ -975,10 +977,6 @@ HTML
|
||||
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
|
||||
FormHelper::DisableAttributeBlobInputs($this->sTargetClass, $aFormExtraParams);
|
||||
|
||||
if(FormHelper::HasMandatoryAttributeBlobInputs($oNewObj)){
|
||||
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
|
||||
}
|
||||
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), $aFormExtraParams);
|
||||
$oPage->add(<<<HTML
|
||||
</div>
|
||||
@@ -1072,27 +1070,18 @@ JS
|
||||
{
|
||||
$oObj = MetaModel::NewObject($this->sTargetClass);
|
||||
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
|
||||
if (count($aErrors) == 0) {
|
||||
|
||||
// Retrieve JSON data
|
||||
$sJSON = utils::ReadParam('json', '{}', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
$oJSON = json_decode($sJSON);
|
||||
|
||||
$oObj->SetContextSection('temporary_objects', [
|
||||
'create' => [
|
||||
'transaction_id' => utils::ReadParam('root_transaction_id', '', false, utils::ENUM_SANITIZATION_FILTER_TRANSACTION_ID),
|
||||
'host_class' => $oJSON->m_sClass,
|
||||
'host_att_code' => $this->sAttCode,
|
||||
],
|
||||
]);
|
||||
$oObj->DBInsertNoReload();
|
||||
|
||||
if (count($aErrors) == 0)
|
||||
{
|
||||
$oObj->DBInsert();
|
||||
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return array('error' => implode(' ', $aErrors), 'id' => 0);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
catch(Exception $e)
|
||||
{
|
||||
return array('error' => $e->getMessage(), 'id' => 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,16 +71,15 @@ class UIHTMLEditorWidget
|
||||
// To change the default settings of the editor,
|
||||
// a) edit the file /js/ckeditor/config.js
|
||||
// b) or override some of the configuration settings, using the second parameter of ckeditor()
|
||||
$sJSDefineWidth = '';
|
||||
$aConfig = utils::GetCkeditorPref();
|
||||
$sWidthSpec = addslashes(trim($this->m_oAttDef->GetWidth()));
|
||||
if ($sWidthSpec != '') {
|
||||
/*N°6543 - the function min allow to keep text inside the column when width is defined*/
|
||||
$aConfig['width'] = "min($sWidthSpec,100%)";
|
||||
$sJSDefineWidth = '$("#cke_'.$iId.' iframe").contents().find("body").css("width", "'.$sWidthSpec.'")';
|
||||
if ($sWidthSpec != '')
|
||||
{
|
||||
$aConfig['width'] = $sWidthSpec;
|
||||
}
|
||||
$sHeightSpec = addslashes(trim($this->m_oAttDef->GetHeight()));
|
||||
if ($sHeightSpec != '') {
|
||||
if ($sHeightSpec != '')
|
||||
{
|
||||
$aConfig['height'] = $sHeightSpec;
|
||||
}
|
||||
$sConfigJS = json_encode($aConfig);
|
||||
@@ -111,7 +110,6 @@ $('#$iId').on('update', function(evt){
|
||||
else
|
||||
{
|
||||
oMe.data('ckeditorInstance').setReadOnly(oMe.prop('disabled'));
|
||||
$sJSDefineWidth
|
||||
}
|
||||
};
|
||||
setTimeout(delayedSetReadOnly, 50);
|
||||
|
||||
@@ -143,10 +143,6 @@ JS
|
||||
|
||||
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
|
||||
FormHelper::DisableAttributeBlobInputs($sRealClass, $aFormExtraParams);
|
||||
|
||||
if(FormHelper::HasMandatoryAttributeBlobInputs($oObj)){
|
||||
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
|
||||
}
|
||||
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);
|
||||
}
|
||||
|
||||
@@ -178,18 +178,17 @@ class UILinksWidget
|
||||
|
||||
$oDisplayBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay($oPage, "SearchFormToAdd_{$sLinkedSetId}",
|
||||
[
|
||||
'menu' => false,
|
||||
array(
|
||||
'menu' => false,
|
||||
'result_list_outer_selector' => "SearchResultsToAdd_{$sLinkedSetId}",
|
||||
'table_id' => "add_{$sLinkedSetId}",
|
||||
'table_inner_id' => "ResultsToAdd_{$sLinkedSetId}",
|
||||
'selection_mode' => true,
|
||||
'json' => $sJson,
|
||||
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
|
||||
'query_params' => $oFilter->GetInternalParams(),
|
||||
'hidden_criteria' => $sAlreadyLinkedExpression,
|
||||
'submit_on_load' => false,
|
||||
]));
|
||||
'table_id' => "add_{$sLinkedSetId}",
|
||||
'table_inner_id' => "ResultsToAdd_{$sLinkedSetId}",
|
||||
'selection_mode' => true,
|
||||
'json' => $sJson,
|
||||
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
|
||||
'query_params' => $oFilter->GetInternalParams(),
|
||||
'hidden_criteria' => $sAlreadyLinkedExpression,
|
||||
)));
|
||||
|
||||
$oBlock->AddForm();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use Combodo\iTop\Service\Module\ModuleService;
|
||||
use ScssPhp\ScssPhp\Compiler;
|
||||
use ScssPhp\ScssPhp\OutputStyle;
|
||||
use ScssPhp\ScssPhp\ValueConverter;
|
||||
@@ -52,31 +51,22 @@ class utils
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_INTEGER = 'integer';
|
||||
/**
|
||||
* Datamodel class
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606 update PHPDoc
|
||||
* @uses MetaModel::IsValidClass()
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_CLASS = 'class';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606
|
||||
* @uses class_exists()
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_PHP_CLASS = 'php_class';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_STRING = 'string';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param';
|
||||
/**
|
||||
@@ -91,29 +81,24 @@ class utils
|
||||
public const ENUM_SANITIZATION_FILTER_OPERATION = 'operation';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_PARAMETER = 'parameter';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_FIELD_NAME = 'field_name';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_TRANSACTION_ID = 'transaction_id';
|
||||
/**
|
||||
* @var string For XML / HTML node identifiers
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
|
||||
/**
|
||||
* @var string For XML / HTML node id/class selector
|
||||
* @since 3.1.2 3.2.1
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR = 'element_selector';
|
||||
/**
|
||||
* @var string For variables names
|
||||
* @since 3.0.0
|
||||
@@ -121,13 +106,12 @@ class utils
|
||||
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.2 3.1.0 N°4899
|
||||
* @since 2.7.10 N°6606
|
||||
* @since 3.0.2, 3.1.0 N°4899
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_URL = 'url';
|
||||
|
||||
@@ -170,8 +154,6 @@ class utils
|
||||
|
||||
private static $iNextId = 0;
|
||||
|
||||
private static $m_sAppRootUrl = null;
|
||||
|
||||
protected static function LoadParamFile($sParamFile)
|
||||
{
|
||||
if (!file_exists($sParamFile)) {
|
||||
@@ -243,8 +225,13 @@ class utils
|
||||
|
||||
public static function IsModeCLI()
|
||||
{
|
||||
$sCleanName = strtolower(trim(PHP_SAPI));
|
||||
return ($sCleanName === 'cli');
|
||||
$sSAPIName = php_sapi_name();
|
||||
$sCleanName = strtolower(trim($sSAPIName));
|
||||
if ($sCleanName == 'cli') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -367,13 +354,13 @@ class utils
|
||||
}
|
||||
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
||||
}
|
||||
|
||||
|
||||
public static function ReadPostedParam($sName, $defaultValue = '', $sSanitizationFilter = 'parameter')
|
||||
{
|
||||
$retValue = isset($_POST[$sName]) ? $_POST[$sName] : $defaultValue;
|
||||
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
||||
}
|
||||
|
||||
|
||||
public static function Sanitize($value, $defaultValue, $sSanitizationFilter)
|
||||
{
|
||||
if ($value === $defaultValue)
|
||||
@@ -389,7 +376,7 @@ class utils
|
||||
$retValue = $defaultValue;
|
||||
}
|
||||
}
|
||||
return $retValue;
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -408,10 +395,6 @@ class utils
|
||||
* @since 2.7.0 new 'element_identifier' filter
|
||||
* @since 3.0.0 new utils::ENUM_SANITIZATION_* const
|
||||
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
|
||||
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
|
||||
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
|
||||
*
|
||||
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
|
||||
*/
|
||||
protected static function Sanitize_Internal($value, $sSanitizationFilter)
|
||||
{
|
||||
@@ -432,13 +415,6 @@ class utils
|
||||
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_PHP_CLASS:
|
||||
$retValue = $value;
|
||||
if (!class_exists($value)) {
|
||||
$retValue = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
|
||||
case static::ENUM_SANITIZATION_FILTER_ROUTE:
|
||||
case static::ENUM_SANITIZATION_FILTER_OPERATION:
|
||||
@@ -494,17 +470,8 @@ class utils
|
||||
}
|
||||
break;
|
||||
|
||||
// For XML / HTML node identifiers
|
||||
case static::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER:
|
||||
$retValue = preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
|
||||
$retValue = filter_var($retValue, FILTER_VALIDATE_REGEXP,
|
||||
['options' => ['regexp' => '/^[A-Za-z0-9][A-Za-z0-9_-]*$/']]);
|
||||
break;
|
||||
|
||||
// For XML / HTML node id selector
|
||||
case static::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR:
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
|
||||
['options' => ['regexp' => '/^[#\.][A-Za-z0-9][A-Za-z0-9_-]*$/']]);
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_VARIABLE_NAME:
|
||||
@@ -513,8 +480,7 @@ class utils
|
||||
|
||||
// For URL
|
||||
case static::ENUM_SANITIZATION_FILTER_URL:
|
||||
// N°6350 - returns only valid URLs
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_URL);
|
||||
$retValue = filter_var($value, FILTER_SANITIZE_URL);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -554,11 +520,11 @@ class utils
|
||||
$sMimeType = self::GetFileMimeType($sTmpName);
|
||||
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
|
||||
break;
|
||||
|
||||
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
// no file to load, it's a normal case, just return an empty document
|
||||
break;
|
||||
|
||||
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadedFileTooBig', ini_get('upload_max_filesize')));
|
||||
@@ -567,7 +533,7 @@ class utils
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
throw new FileUploadException(Dict::S('UI:Error:UploadedFileTruncated.'));
|
||||
break;
|
||||
|
||||
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
throw new FileUploadException(Dict::S('UI:Error:NoTmpDir'));
|
||||
break;
|
||||
@@ -580,7 +546,7 @@ class utils
|
||||
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $sName));
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $sError));
|
||||
break;
|
||||
@@ -686,17 +652,17 @@ class utils
|
||||
|
||||
return $aSelectedObj;
|
||||
}
|
||||
|
||||
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
return privUITransaction::GetNewTransactionId();
|
||||
}
|
||||
|
||||
|
||||
public static function IsTransactionValid($sId, $bRemoveTransaction = true)
|
||||
{
|
||||
return privUITransaction::IsTransactionValid($sId, $bRemoveTransaction);
|
||||
}
|
||||
|
||||
|
||||
public static function RemoveTransaction($sId)
|
||||
{
|
||||
return privUITransaction::RemoveTransaction($sId);
|
||||
@@ -880,9 +846,9 @@ class utils
|
||||
$aDateTokens = array_keys($aSpec);
|
||||
$aDateRegexps = array_values($aSpec);
|
||||
}
|
||||
|
||||
|
||||
$sDateRegexp = str_replace($aDateTokens, $aDateRegexps, $sFormat);
|
||||
|
||||
|
||||
if (preg_match('!^(?<head>)'.$sDateRegexp.'(?<tail>)$!', $sDate, $aMatches))
|
||||
{
|
||||
$sYear = isset($aMatches['year']) ? $aMatches['year'] : 0;
|
||||
@@ -899,7 +865,7 @@ class utils
|
||||
}
|
||||
// http://www.spaweditor.com/scripts/regex/index.php
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert an old date/time format specification (using % placeholders)
|
||||
* to a format compatible with DateTime::createFromFormat
|
||||
@@ -1057,7 +1023,7 @@ class utils
|
||||
*/
|
||||
public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false)
|
||||
{
|
||||
$sUrl = static::$m_sAppRootUrl;
|
||||
static $sUrl = null;
|
||||
if ($sUrl === null || $bForceTrustProxy)
|
||||
{
|
||||
$sUrl = self::GetConfig()->Get('app_root_url');
|
||||
@@ -1078,9 +1044,8 @@ class utils
|
||||
}
|
||||
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
|
||||
}
|
||||
static::$m_sAppRootUrl = $sUrl;
|
||||
}
|
||||
return static::$m_sAppRootUrl;
|
||||
return $sUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1431,23 +1396,13 @@ class utils
|
||||
return APPROOT . 'env-' . MetaModel::GetEnvironment() . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string A path to the folder into which data can be written
|
||||
* @internal
|
||||
* @since N°6097 2.7.10 3.0.4 3.1.1
|
||||
*/
|
||||
public static function GetDataPath(): string
|
||||
{
|
||||
return APPROOT.'data/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string A path to a folder into which any module can store cache data
|
||||
* The corresponding folder is created or cleaned upon code compilation
|
||||
*/
|
||||
public static function GetCachePath()
|
||||
{
|
||||
return static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
|
||||
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1497,7 +1452,7 @@ class utils
|
||||
$aResult = [];
|
||||
|
||||
// 1st - add standard built-in menu items
|
||||
//
|
||||
//
|
||||
switch($iMenuId)
|
||||
{
|
||||
case iPopupMenuExtension::MENU_OBJLIST_ACTIONS:
|
||||
@@ -1881,7 +1836,7 @@ SQL;
|
||||
return $sProposed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Some characters cause troubles with jQuery when used inside DOM IDs, so let's replace them by the safe _ (underscore)
|
||||
* @param string $sId The ID to sanitize
|
||||
@@ -1891,7 +1846,7 @@ SQL;
|
||||
{
|
||||
return str_replace(array(':', '[', ']', '+', '-', ' '), '_', $sId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to execute an HTTP POST request, uses CURL PHP extension
|
||||
*
|
||||
@@ -1976,7 +1931,7 @@ SQL;
|
||||
|
||||
/**
|
||||
* Get a standard list of character sets
|
||||
*
|
||||
*
|
||||
* @param array $aAdditionalEncodings Additional values
|
||||
* @return array of iconv code => english label, sorted by label
|
||||
*/
|
||||
@@ -2015,7 +1970,6 @@ SQL;
|
||||
|
||||
/**
|
||||
* @param string $sValue
|
||||
* @param bool $bDoubleEncode Whether to double encode the value or not
|
||||
*
|
||||
* @return string passed value with only characters having a special meaning in HTML escaped as entities
|
||||
* Since 3.0.0 we were using for this {@link HtmlEntities} but it was overkill and leads to double escaping !
|
||||
@@ -2023,15 +1977,14 @@ SQL;
|
||||
* @uses \htmlspecialchars()
|
||||
* @link https://www.php.net/manual/fr/function.htmlspecialchars.php
|
||||
* @since 3.0.0 N°3623
|
||||
* @since 3.1.0 N°6405 Add $bDoubleEncode parameter
|
||||
*/
|
||||
public static function EscapeHtml($sValue, bool $bDoubleEncode = false)
|
||||
public static function EscapeHtml($sValue)
|
||||
{
|
||||
return htmlspecialchars(
|
||||
$sValue ?? '',
|
||||
ENT_QUOTES | ENT_DISALLOWED | ENT_HTML5,
|
||||
WebPage::PAGES_CHARSET,
|
||||
$bDoubleEncode
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2083,7 +2036,7 @@ SQL;
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert (?) plain text to some HTML markup by replacing newlines by <br/> tags
|
||||
* and escaping HTML entities
|
||||
@@ -2092,15 +2045,12 @@ SQL;
|
||||
*/
|
||||
public static function TextToHtml($sText)
|
||||
{
|
||||
if (static::IsNullOrEmptyString($sText)){
|
||||
return '';
|
||||
}
|
||||
$sText = str_replace("\r\n", "\n", $sText);
|
||||
$sText = str_replace("\r", "\n", $sText);
|
||||
|
||||
return str_replace("\n", '<br/>', utils::EscapeHtml($sText));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Eventually compiles the SASS (.scss) file into the CSS (.css) file
|
||||
*
|
||||
@@ -2214,7 +2164,7 @@ SQL;
|
||||
case 'image/png':
|
||||
$img = @imagecreatefromstring($oImage->GetData());
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// Unsupported image type, return the image as-is
|
||||
//throw new Exception("Unsupported image type: '".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
|
||||
@@ -2228,14 +2178,14 @@ SQL;
|
||||
else
|
||||
{
|
||||
// Let's scale the image, preserving the transparency for GIFs and PNGs
|
||||
|
||||
|
||||
$fScale = min($iMaxImageWidth / $iWidth, $iMaxImageHeight / $iHeight);
|
||||
|
||||
$iNewWidth = $iWidth * $fScale;
|
||||
$iNewHeight = $iHeight * $fScale;
|
||||
|
||||
|
||||
$new = imagecreatetruecolor($iNewWidth, $iNewHeight);
|
||||
|
||||
|
||||
// Preserve transparency
|
||||
if(($oImage->GetMimeType() == "image/gif") || ($oImage->GetMimeType() == "image/png"))
|
||||
{
|
||||
@@ -2243,38 +2193,38 @@ SQL;
|
||||
imagealphablending($new, false);
|
||||
imagesavealpha($new, true);
|
||||
}
|
||||
|
||||
|
||||
imagecopyresampled($new, $img, 0, 0, 0, 0, $iNewWidth, $iNewHeight, $iWidth, $iHeight);
|
||||
|
||||
|
||||
ob_start();
|
||||
switch ($oImage->GetMimeType())
|
||||
{
|
||||
case 'image/gif':
|
||||
imagegif($new); // send image to output buffer
|
||||
break;
|
||||
|
||||
|
||||
case 'image/jpeg':
|
||||
imagejpeg($new, null, 80); // null = send image to output buffer, 80 = good quality
|
||||
break;
|
||||
|
||||
|
||||
case 'image/png':
|
||||
imagepng($new, null, 5); // null = send image to output buffer, 5 = medium compression
|
||||
break;
|
||||
}
|
||||
$oResampledImage = new ormDocument(ob_get_contents(), $oImage->GetMimeType(), $oImage->GetFileName());
|
||||
@ob_end_clean();
|
||||
|
||||
|
||||
imagedestroy($img);
|
||||
imagedestroy($new);
|
||||
|
||||
|
||||
return $oResampledImage;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a 128 bit UUID in the format: {########-####-####-####-############}
|
||||
*
|
||||
*
|
||||
* Note: this method can be run from the command line as well as from the web server.
|
||||
* Note2: this method is not cryptographically secure! If you need a cryptographically secure value
|
||||
* consider using open_ssl or PHP 7 methods.
|
||||
@@ -2310,9 +2260,26 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleName($iCallDepth = 0)
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
|
||||
$sCurrentModuleName = '';
|
||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
|
||||
|
||||
foreach(GetModulesInfo() as $sModuleName => $aInfo)
|
||||
{
|
||||
if ($aInfo['root_dir'] !== '')
|
||||
{
|
||||
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
|
||||
|
||||
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
|
||||
{
|
||||
$sCurrentModuleName = $sModuleName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sCurrentModuleName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
|
||||
*
|
||||
@@ -2332,7 +2299,24 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleDir($iCallDepth)
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleDir($iCallDepth);
|
||||
$sCurrentModuleDir = '';
|
||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
|
||||
|
||||
foreach(GetModulesInfo() as $sModuleName => $aInfo)
|
||||
{
|
||||
if ($aInfo['root_dir'] !== '')
|
||||
{
|
||||
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
|
||||
|
||||
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
|
||||
{
|
||||
$sCurrentModuleDir = basename($sRootDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sCurrentModuleDir;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2347,9 +2331,14 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleUrl()
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
|
||||
$sDir = static::GetCurrentModuleDir(1);
|
||||
if ( $sDir !== '')
|
||||
{
|
||||
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sProperty The name of the property to retrieve
|
||||
* @param mixed $defaultvalue
|
||||
@@ -2357,18 +2346,24 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
|
||||
$sModuleName = static::GetCurrentModuleName(1);
|
||||
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sModuleName
|
||||
* @return string|NULL compiled version of a given module, as it was seen by the compiler
|
||||
*/
|
||||
public static function GetCompiledModuleVersion($sModuleName)
|
||||
{
|
||||
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
|
||||
$aModulesInfo = GetModulesInfo();
|
||||
if (array_key_exists($sModuleName, $aModulesInfo))
|
||||
{
|
||||
return $aModulesInfo[$sModuleName]['version'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given path/url is an http(s) URL
|
||||
* @param string $sPath
|
||||
@@ -2383,7 +2378,7 @@ SQL;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given URL is a link to download a document/image on the CURRENT iTop
|
||||
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
|
||||
@@ -2432,7 +2427,7 @@ SQL;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read the content of a file (and retrieve its MIME type) from either:
|
||||
* - an URL pointing to a blob (image/document) on the current iTop server
|
||||
@@ -2476,7 +2471,7 @@ SQL;
|
||||
'html' => 'text/html',
|
||||
'exe' => 'application/octet-stream',
|
||||
);
|
||||
|
||||
|
||||
$sData = null;
|
||||
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
|
||||
$sFileName = 'uploaded-file'; // Default name for downloaded-files
|
||||
@@ -2508,17 +2503,15 @@ SQL;
|
||||
$aHeaders = static::ParseHeaders($http_response_header);
|
||||
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
|
||||
// Compute the file extension from the MIME Type
|
||||
foreach ($aKnownExtensions as $sExtValue => $sMime) {
|
||||
if ($sMime === $sMimeType) {
|
||||
foreach($aKnownExtensions as $sExtValue => $sMime)
|
||||
{
|
||||
if ($sMime === $sMimeType)
|
||||
{
|
||||
$sExtension = '.'.$sExtValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$sPathName = pathinfo($sPath, PATHINFO_FILENAME);
|
||||
if (utils::IsNotNullOrEmptyString($sPathName)) {
|
||||
$sFileName = $sPathName;
|
||||
}
|
||||
$sFileName .= $sExtension;
|
||||
}
|
||||
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
|
||||
@@ -2534,7 +2527,7 @@ SQL;
|
||||
}
|
||||
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
|
||||
$sFileName = basename($sPath);
|
||||
|
||||
|
||||
if (array_key_exists($sExtension, $aKnownExtensions))
|
||||
{
|
||||
$sMimeType = $aKnownExtensions[$sExtension];
|
||||
@@ -2548,7 +2541,7 @@ SQL;
|
||||
}
|
||||
return $oUploadedDoc;
|
||||
}
|
||||
|
||||
|
||||
protected static function ParseHeaders($aHeaders)
|
||||
{
|
||||
$aCleanHeaders = array();
|
||||
@@ -2573,7 +2566,7 @@ SQL;
|
||||
}
|
||||
return $aCleanHeaders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string a string based on compilation time or (if not available because the datamodel has not been loaded)
|
||||
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
|
||||
@@ -2693,26 +2686,24 @@ SQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local path relative to the iTop installation (APPROOT or the given base path)
|
||||
* Returns the local path relative to the iTop installation of an existing file
|
||||
* Dir separator is changed to '/' for consistency among the different OS
|
||||
*
|
||||
* @param string $sAbsolutePath absolute path
|
||||
* @param string $sBasePath Base path for the resulting local path (default APPROOT)
|
||||
*
|
||||
* @return false|string The generated local path or false if absolute path is not under the base path
|
||||
* @since 3.1.1 Added base path defaulted to previous version APPROOT
|
||||
* @return false|string
|
||||
*/
|
||||
final public static function LocalPath($sAbsolutePath, string $sBasePath = APPROOT)
|
||||
final public static function LocalPath($sAbsolutePath)
|
||||
{
|
||||
$sRootPath = realpath($sBasePath);
|
||||
$sRootPath = realpath(APPROOT);
|
||||
$sFullPath = realpath($sAbsolutePath);
|
||||
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$sLocalPath = substr($sFullPath, strlen($sRootPath.DIRECTORY_SEPARATOR));
|
||||
|
||||
return str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
|
||||
$sLocalPath = str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
|
||||
return $sLocalPath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2904,7 +2895,7 @@ HTML;
|
||||
|
||||
// Add already loaded classes
|
||||
$aCurrentClasses = array_fill_keys(get_declared_classes(), '');
|
||||
$aClassMap = array_merge($aCurrentClasses, $aClassMap);
|
||||
$aClassMap = array_merge($aClassMap, $aCurrentClasses);
|
||||
|
||||
foreach ($aClassMap as $sPHPClass => $sPHPFile) {
|
||||
$bSkipped = false;
|
||||
@@ -2913,8 +2904,7 @@ HTML;
|
||||
if ($sClassNameFilter !== '' && strpos($sPHPClass, $sClassNameFilter) === false) {
|
||||
$bSkipped = true;
|
||||
}
|
||||
// For some PHP classes we don't have their file path as they are already in memory, so we never filter on their paths
|
||||
elseif (utils::IsNotNullOrEmptyString($sPHPFile)) {
|
||||
else {
|
||||
$sPHPFile = self::LocalPath($sPHPFile);
|
||||
if ($sPHPFile !== false) {
|
||||
$sPHPFile = '/'.$sPHPFile; // for regex
|
||||
@@ -2929,12 +2919,11 @@ HTML;
|
||||
$bSkipped = true; // file not found
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!$bSkipped){
|
||||
try {
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($oRefClass->implementsInterface($sInterface) &&
|
||||
!$oRefClass->isInterface() && !$oRefClass->isAbstract() && !$oRefClass->isTrait()) {
|
||||
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) {
|
||||
$aMatchingClasses[] = $sPHPClass;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
@@ -3040,7 +3029,6 @@ HTML;
|
||||
*
|
||||
* @return bool if string null or empty
|
||||
* @since 3.0.2 N°5302
|
||||
* @since 2.7.10 N°6458 add method in the 2.7 branch
|
||||
*/
|
||||
public static function IsNullOrEmptyString(?string $sString): bool
|
||||
{
|
||||
@@ -3056,7 +3044,6 @@ HTML;
|
||||
*
|
||||
* @return bool if string is not null and not empty
|
||||
* @since 3.0.2 N°5302
|
||||
* @since 2.7.10 N°6458 add method in the 2.7 branch
|
||||
*/
|
||||
public static function IsNotNullOrEmptyString(?string $sString): bool
|
||||
{
|
||||
@@ -3381,22 +3368,5 @@ HTML;
|
||||
{
|
||||
return in_array($sTrait, self::TraitsUsedByClass($sClass, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stack trace as string array.
|
||||
*
|
||||
* @return array
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public static function GetStackTraceAsArray(): array
|
||||
{
|
||||
$e = new Exception();
|
||||
$aTrace = explode("\n", $e->getTraceAsString());
|
||||
// Remove call to this method
|
||||
array_shift($aTrace);
|
||||
// Remove Main
|
||||
array_pop($aTrace);
|
||||
|
||||
return $aTrace;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -351,7 +351,6 @@ class WizardHelper
|
||||
/**
|
||||
* @return string JS code to be executed for fields update
|
||||
* @since 3.0.0 N°3198
|
||||
* @deprecated 3.0.3-2 3.0.4 3.1.1 3.2.0 Use {@see \WizardHelper::AddJsForUpdateFields()} instead
|
||||
*/
|
||||
public function GetJsForUpdateFields()
|
||||
{
|
||||
@@ -364,32 +363,6 @@ class WizardHelper
|
||||
JS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add necessary JS snippets (to the page) to be executed for fields update
|
||||
*
|
||||
* @param \WebPage $oPage
|
||||
* @return void
|
||||
* @since 3.0.3-2 3.0.4 3.1.1 3.2.0 N°6766
|
||||
*/
|
||||
public function AddJsForUpdateFields(WebPage $oPage)
|
||||
{
|
||||
$sWizardHelperJsVar = (!is_null($this->m_aData['m_sWizHelperJsVarName'])) ? utils::Sanitize($this->m_aData['m_sWizHelperJsVarName'], '', utils::ENUM_SANITIZATION_FILTER_PARAMETER) : 'oWizardHelper'.$this->GetFormPrefix();
|
||||
$sWizardHelperJson = $this->ToJSON();
|
||||
|
||||
$oPage->add_script(<<<JS
|
||||
{$sWizardHelperJsVar}.m_oData = {$sWizardHelperJson};
|
||||
{$sWizardHelperJsVar}.UpdateFields();
|
||||
JS
|
||||
);
|
||||
$oPage->add_ready_script(<<<JS
|
||||
if ({$sWizardHelperJsVar}.m_oDependenciesUpdatedPromiseResolve !== null){
|
||||
{$sWizardHelperJsVar}.m_oDependenciesUpdatedPromiseResolve();
|
||||
}
|
||||
JS
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Function with an old pattern of code
|
||||
* @deprecated 3.1.0
|
||||
@@ -398,9 +371,11 @@ JS
|
||||
{
|
||||
$aSet = json_decode($sJsonSet, true); // true means hash array instead of object
|
||||
$oSet = CMDBObjectSet::FromScratch($sLinkClass);
|
||||
foreach ($aSet as $aLinkObj) {
|
||||
foreach ($aSet as $aLinkObj)
|
||||
{
|
||||
$oLink = MetaModel::NewObject($sLinkClass);
|
||||
foreach ($aLinkObj as $sAttCode => $value) {
|
||||
foreach ($aLinkObj as $sAttCode => $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode);
|
||||
if (($oAttDef->IsExternalKey()) && ($value != '') && ($value > 0))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -23,13 +23,11 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.1');
|
||||
* @used-by utils::GetItopVersionWikiSyntax()
|
||||
* @used-by iTopModulesPhpVersionIntegrationTest
|
||||
*/
|
||||
define('ITOP_CORE_VERSION', '3.1.2');
|
||||
define('ITOP_CORE_VERSION', '3.1.0');
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.4 3.1.0 3.2.0 N°6274 Allow to test if PHPUnit is currently running. Starting with PHPUnit 9.5 we'll be able to replace it with $GLOBALS['phpunit_version']
|
||||
* @since 3.0.4 3.1.1 3.2.0 N°6976 Fix constant name (DeprecatedCallsLog error handler was never set)
|
||||
* @since 3.0.4 N°6274 Allow to test if PHPUnit is currently running. Starting with PHPUnit 9.5 we'll be able to replace it with $GLOBALS['phpunit_version']
|
||||
*/
|
||||
const ITOP_PHPUNIT_RUNNING_CONSTANT_NAME = 'ITOP_PHPUNIT_RUNNING';
|
||||
define('ITOP_PHPUNIT_RUNNING_CONSTANT_NAME', 'ITOP_PHPUNIT_RUNNING');
|
||||
|
||||
require_once APPROOT.'bootstrap.inc.php';
|
||||
|
||||
@@ -45,7 +45,6 @@ define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
|
||||
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
|
||||
|
||||
$fItopStarted = microtime(true);
|
||||
$iItopInitialMemory = memory_get_usage(true);
|
||||
|
||||
if (!isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) {
|
||||
require_once APPROOT.'/lib/autoload.php';
|
||||
@@ -68,7 +67,7 @@ if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
|
||||
http_response_code(503);
|
||||
// Display message depending on the request
|
||||
include(APPROOT.'application/maintenancemsg.php');
|
||||
$sSAPIName = strtoupper(trim(PHP_SAPI));
|
||||
$sSAPIName = strtoupper(trim(php_sapi_name()));
|
||||
|
||||
switch (true)
|
||||
{
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
"ext-json": "*",
|
||||
"ext-mysqli": "*",
|
||||
"ext-soap": "*",
|
||||
"apereo/phpcas": "~1.6.0",
|
||||
"apereo/phpcas" : "~1.6.0",
|
||||
"combodo/tcpdf": "~6.4.4",
|
||||
"firebase/php-jwt": "~6.4.0",
|
||||
"guzzlehttp/guzzle": "^7.5.1",
|
||||
"guzzlehttp/guzzle": "^7.4.5",
|
||||
"laminas/laminas-mail": "^2.11",
|
||||
"laminas/laminas-servicemanager": "^3.5",
|
||||
"league/oauth2-google": "^3.0",
|
||||
|
||||
150
composer.lock
generated
150
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "cb3883f141f50e1bfbc957880ab93be1",
|
||||
"content-hash": "69db9dbdea61a588fa2058724d91a579",
|
||||
"packages": [
|
||||
{
|
||||
"name": "apereo/phpcas",
|
||||
@@ -217,22 +217,22 @@
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "7.7.0",
|
||||
"version": "7.4.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5"
|
||||
"reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5",
|
||||
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/1dd98b0564cb3f6bd16ce683cb755f94c10fbd82",
|
||||
"reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/promises": "^1.5.3 || ^2.0",
|
||||
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
|
||||
"guzzlehttp/promises": "^1.5",
|
||||
"guzzlehttp/psr7": "^1.9 || ^2.4",
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-client": "^1.0",
|
||||
"symfony/deprecation-contracts": "^2.2 || ^3.0"
|
||||
@@ -241,11 +241,10 @@
|
||||
"psr/http-client-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
||||
"bamarni/composer-bin-plugin": "^1.4.1",
|
||||
"ext-curl": "*",
|
||||
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
|
||||
"php-http/message-factory": "^1.1",
|
||||
"phpunit/phpunit": "^8.5.29 || ^9.5.23",
|
||||
"php-http/client-integration-tests": "^3.0",
|
||||
"phpunit/phpunit": "^8.5.5 || ^9.3.5",
|
||||
"psr/log": "^1.1 || ^2.0 || ^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
@@ -255,9 +254,8 @@
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
"branch-alias": {
|
||||
"dev-master": "7.4-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -323,7 +321,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||
"source": "https://github.com/guzzle/guzzle/tree/7.7.0"
|
||||
"source": "https://github.com/guzzle/guzzle/tree/7.4.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -339,37 +337,38 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-05-21T14:04:53+00:00"
|
||||
"time": "2022-06-20T22:16:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "2.0.0",
|
||||
"version": "1.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6"
|
||||
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
|
||||
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
|
||||
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0"
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
||||
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
|
||||
"symfony/phpunit-bridge": "^4.4 || ^5.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
"branch-alias": {
|
||||
"dev-master": "1.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
}
|
||||
@@ -406,7 +405,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/promises/issues",
|
||||
"source": "https://github.com/guzzle/promises/tree/2.0.0"
|
||||
"source": "https://github.com/guzzle/promises/tree/1.5.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -422,26 +421,26 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-05-21T13:50:22+00:00"
|
||||
"time": "2021-10-22T20:56:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.5.0",
|
||||
"version": "2.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "b635f279edd83fc275f822a1188157ffea568ff6"
|
||||
"reference": "13388f00956b1503577598873fffb5ae994b5737"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
|
||||
"reference": "b635f279edd83fc275f822a1188157ffea568ff6",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/13388f00956b1503577598873fffb5ae994b5737",
|
||||
"reference": "13388f00956b1503577598873fffb5ae994b5737",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2.5 || ^8.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 || ^2.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"ralouphie/getallheaders": "^3.0"
|
||||
},
|
||||
"provide": {
|
||||
@@ -449,18 +448,17 @@
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.8.1",
|
||||
"bamarni/composer-bin-plugin": "^1.4.1",
|
||||
"http-interop/http-factory-tests": "^0.9",
|
||||
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
|
||||
"phpunit/phpunit": "^8.5.8 || ^9.3.10"
|
||||
},
|
||||
"suggest": {
|
||||
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"bamarni-bin": {
|
||||
"bin-links": true,
|
||||
"forward-command": false
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -522,7 +520,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.5.0"
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -538,7 +536,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-04-17T16:11:26+00:00"
|
||||
"time": "2022-06-20T21:43:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laminas/laminas-loader",
|
||||
@@ -1670,21 +1668,21 @@
|
||||
},
|
||||
{
|
||||
"name": "psr/http-client",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-client.git",
|
||||
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31"
|
||||
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31",
|
||||
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31",
|
||||
"url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
|
||||
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0 || ^8.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -1704,7 +1702,7 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP clients",
|
||||
@@ -1716,27 +1714,27 @@
|
||||
"psr-18"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-client/tree/1.0.2"
|
||||
"source": "https://github.com/php-fig/http-client/tree/master"
|
||||
},
|
||||
"time": "2023-04-10T20:12:12+00:00"
|
||||
"time": "2020-06-29T06:28:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "e616d01114759c4c489f93b099585439f795fe35"
|
||||
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
|
||||
"reference": "e616d01114759c4c489f93b099585439f795fe35",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
|
||||
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@@ -1756,7 +1754,7 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interfaces for PSR-7 HTTP message factories",
|
||||
@@ -1771,31 +1769,31 @@
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-factory/tree/1.0.2"
|
||||
"source": "https://github.com/php-fig/http-factory/tree/master"
|
||||
},
|
||||
"time": "2023-04-10T20:10:41+00:00"
|
||||
"time": "2019-04-30T12:38:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "2.0",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
|
||||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -1810,7 +1808,7 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
@@ -1824,9 +1822,9 @@
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/2.0"
|
||||
"source": "https://github.com/php-fig/http-message/tree/master"
|
||||
},
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
"time": "2016-08-06T14:39:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
@@ -4476,16 +4474,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/twig-bridge",
|
||||
"version": "v5.4.31",
|
||||
"version": "v5.4.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/twig-bridge.git",
|
||||
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942"
|
||||
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
|
||||
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
|
||||
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
|
||||
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4498,22 +4496,22 @@
|
||||
"phpdocumentor/reflection-docblock": "<3.2.2",
|
||||
"phpdocumentor/type-resolver": "<1.4.0",
|
||||
"symfony/console": "<5.3",
|
||||
"symfony/form": "<5.4.21|>=6,<6.2.7",
|
||||
"symfony/form": "<5.3",
|
||||
"symfony/http-foundation": "<5.3",
|
||||
"symfony/http-kernel": "<4.4",
|
||||
"symfony/translation": "<5.2",
|
||||
"symfony/workflow": "<5.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "^1.12|^2",
|
||||
"egulias/email-validator": "^2.1.10|^3|^4",
|
||||
"doctrine/annotations": "^1.12",
|
||||
"egulias/email-validator": "^2.1.10|^3",
|
||||
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
|
||||
"symfony/asset": "^4.4|^5.0|^6.0",
|
||||
"symfony/console": "^5.3|^6.0",
|
||||
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
|
||||
"symfony/expression-language": "^4.4|^5.0|^6.0",
|
||||
"symfony/finder": "^4.4|^5.0|^6.0",
|
||||
"symfony/form": "^5.4.21|^6.2.7",
|
||||
"symfony/form": "^5.3|^6.0",
|
||||
"symfony/http-foundation": "^5.3|^6.0",
|
||||
"symfony/http-kernel": "^4.4|^5.0|^6.0",
|
||||
"symfony/intl": "^4.4|^5.0|^6.0",
|
||||
@@ -4577,7 +4575,7 @@
|
||||
"description": "Provides integration for Twig with various Symfony components",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.31"
|
||||
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -4593,7 +4591,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-11-09T21:19:08+00:00"
|
||||
"time": "2022-07-20T13:00:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/twig-bundle",
|
||||
@@ -5276,5 +5274,5 @@
|
||||
"platform-overrides": {
|
||||
"php": "7.4.0"
|
||||
},
|
||||
"plugin-api-version": "2.6.0"
|
||||
"plugin-api-version": "2.1.0"
|
||||
}
|
||||
|
||||
@@ -59,17 +59,9 @@ class DbConnectionWrapper
|
||||
* Use this to register a mock that will handle {@see mysqli::query()}
|
||||
*
|
||||
* @param \mysqli|null $oMysqli
|
||||
* @since 3.0.4 3.1.1 3.2.0 Param $oMysqli becomes nullable
|
||||
* @since 3.1.0-4 N°6848 backport of restoring cnx on null parameter value
|
||||
*/
|
||||
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli = null): void
|
||||
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli): void
|
||||
{
|
||||
if (is_null($oMysqli)) {
|
||||
// Reset to standard connection
|
||||
static::$oDbCnxMockableForQuery = static::$oDbCnxStandard;
|
||||
}
|
||||
else {
|
||||
static::$oDbCnxMockableForQuery = $oMysqli;
|
||||
}
|
||||
static::$oDbCnxMockableForQuery = $oMysqli;
|
||||
}
|
||||
}
|
||||
@@ -419,7 +419,6 @@ class MyHelpers
|
||||
//}
|
||||
return $sOutput;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -524,3 +523,5 @@ class Str
|
||||
return (strtolower($sString) == $sString);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class TemporaryObjectDescriptor
|
||||
*
|
||||
* Descriptor to track a temporary object.
|
||||
*
|
||||
* @experimental do not use, this feature will be part of a future version
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
class TemporaryObjectDescriptor extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array(
|
||||
'category' => 'core',
|
||||
'key_type' => 'autoincrement',
|
||||
'name_attcode' => array('item_class', 'temp_id'),
|
||||
'image_attcode' => '',
|
||||
'state_attcode' => '',
|
||||
'reconc_keys' => array(''),
|
||||
'db_table' => 'priv_temporary_object_descriptor',
|
||||
'db_key_field' => 'id',
|
||||
'db_finalclass_field' => '',
|
||||
'style' => new ormStyle(null, null, null, null, null, null),
|
||||
'indexes' => array(
|
||||
1 =>
|
||||
array(
|
||||
0 => 'temp_id',
|
||||
),
|
||||
2 =>
|
||||
array(
|
||||
0 => 'item_class',
|
||||
1 => 'item_id',
|
||||
),
|
||||
),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime('expiration_date', array('sql' => 'expiration_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString('temp_id', array('sql' => 'temp_id', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString('item_class', array('sql' => 'item_class', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeObjectKey('item_id', array('class_attcode' => 'item_class', 'sql' => 'item_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime('creation_date', array('sql' => 'creation_date', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString('host_class', array('sql' => 'host_class', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeObjectKey('host_id', array('class_attcode' => 'host_class', 'sql' => 'host_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString('host_att_code', array('sql' => 'host_att_code', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("operation", array("allowed_values" => new ValueSetEnum('create,delete'), "sql" => "operation", "default_value" => "create", "is_null_allowed" => true, "depends_on" => array())));
|
||||
|
||||
MetaModel::Init_SetZListItems('details', array(
|
||||
0 => 'temp_id',
|
||||
1 => 'item_class',
|
||||
2 => 'item_id',
|
||||
3 => 'creation_date',
|
||||
4 => 'expiration_date',
|
||||
5 => 'meta',
|
||||
));
|
||||
MetaModel::Init_SetZListItems('standard_search', array(
|
||||
0 => 'temp_id',
|
||||
1 => 'item_class',
|
||||
2 => 'item_id',
|
||||
));
|
||||
MetaModel::Init_SetZListItems('list', array(
|
||||
0 => 'temp_id',
|
||||
1 => 'item_class',
|
||||
2 => 'item_id',
|
||||
3 => 'creation_date',
|
||||
4 => 'expiration_date',
|
||||
));;
|
||||
}
|
||||
|
||||
|
||||
public function DBInsertNoReload()
|
||||
{
|
||||
$this->SetCurrentDateIfNull('creation_date');
|
||||
|
||||
return parent::DBInsertNoReload();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set/Update all of the '_item' fields
|
||||
*
|
||||
* @param object $oItem Container item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function SetItem($oItem, $bUpdateOnChange = false)
|
||||
{
|
||||
$sClass = get_class($oItem);
|
||||
$iItemId = $oItem->GetKey();
|
||||
|
||||
$this->Set('item_class', $sClass);
|
||||
$this->Set('item_id', $iItemId);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
|
||||
|
||||
/**
|
||||
* Persistent classes (internal): user defined actions
|
||||
@@ -226,22 +225,7 @@ class ActionEmail extends ActionNotification
|
||||
* @since 3.0.1
|
||||
*/
|
||||
const ENUM_HEADER_NAME_REFERENCES = 'References';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
const TEMPLATE_BODY_CONTENT = '$content$';
|
||||
/**
|
||||
* Wraps the 'body' of the message for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
*/
|
||||
const CONTENT_HIGHLIGHT = '<div style="border:2px dashed #6800ff;position:relative;padding:2px;margin-top:14px;"><div style="background-color:#6800ff;color:#fff;font-family:Courier New, sans-serif;font-size:14px;line-height:16px;padding:3px;display:block;position:absolute;top:-22px;right:0;">$content$</div>%s</div>';
|
||||
/**
|
||||
* Wraps a placeholder of the email's body for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
|
||||
* @var string
|
||||
*/
|
||||
const FIELD_HIGHLIGHT = '<span style="background-color:#6800ff;color:#fff;font-size:smaller;font-family:Courier New, sans-serif;padding:2px;">\\$$1\\$</span>';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -273,10 +257,6 @@ class ActionEmail extends ActionNotification
|
||||
MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", array("allowed_values" => null, "sql" => "subject", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeTemplateHTML("body", array("allowed_values" => null, "sql" => "body", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("allowed_values" => new ValueSetEnum('low,normal,high'), "sql" => "importance", "default_value" => 'normal', "is_null_allowed" => false, "depends_on" => array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeBlob("html_template", array("is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("ignore_notify", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "ignore_notify", "default_value" => 'yes', "is_null_allowed" => false, "depends_on" => array())));
|
||||
|
||||
|
||||
// Display lists
|
||||
// - Attributes to be displayed for the complete details
|
||||
@@ -286,10 +266,8 @@ class ActionEmail extends ActionNotification
|
||||
0 => 'name',
|
||||
1 => 'description',
|
||||
2 => 'status',
|
||||
3 => 'language',
|
||||
4 => 'html_template',
|
||||
5 => 'subject',
|
||||
6 => 'body',
|
||||
3 => 'subject',
|
||||
4 => 'body',
|
||||
// 5 => 'importance', not handled when sending the mail, better hide it then
|
||||
),
|
||||
'fieldset:ActionEmail:trigger' => array(
|
||||
@@ -303,21 +281,20 @@ class ActionEmail extends ActionNotification
|
||||
2 => 'reply_to',
|
||||
3 => 'reply_to_label',
|
||||
4 => 'test_recipient',
|
||||
5 => 'ignore_notify',
|
||||
6 => 'to',
|
||||
7 => 'cc',
|
||||
8 => 'bcc',
|
||||
5 => 'to',
|
||||
6 => 'cc',
|
||||
7 => 'bcc',
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
// - Attributes to be displayed for a list
|
||||
MetaModel::Init_SetZListItems('list', array('status', 'to', 'subject', 'language'));
|
||||
MetaModel::Init_SetZListItems('list', array('status', 'to', 'subject'));
|
||||
// Search criteria
|
||||
// - Standard criteria of the search
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'status', 'subject', 'language'));
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'status', 'subject'));
|
||||
// - Default criteria for the search
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status', 'subject', 'language'));
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status', 'subject'));
|
||||
}
|
||||
|
||||
// count the recipients found
|
||||
@@ -347,15 +324,6 @@ class ActionEmail extends ActionNotification
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
||||
if ($this->Get('ignore_notify') === 'no') {
|
||||
// In theory it is possible to notify *any* kind of object,
|
||||
// as long as there is an email attribute in the class
|
||||
// So let's not assume that the selected class is a Person
|
||||
$sFirstSelectedClass = $oSearch->GetClass();
|
||||
if (MetaModel::IsValidAttCode($sFirstSelectedClass, 'notify')) {
|
||||
$oSearch->AddCondition('notify', 'yes');
|
||||
}
|
||||
}
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
catch (OQLException $e)
|
||||
@@ -480,27 +448,114 @@ class ActionEmail extends ActionNotification
|
||||
*/
|
||||
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
|
||||
{
|
||||
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
|
||||
try
|
||||
{
|
||||
$this->m_iRecipients = 0;
|
||||
$this->m_aMailErrors = array();
|
||||
|
||||
// Determine recipients
|
||||
//
|
||||
$sTo = $this->FindRecipients('to', $aContextArgs);
|
||||
$sCC = $this->FindRecipients('cc', $aContextArgs);
|
||||
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
|
||||
|
||||
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
|
||||
$sFromLabel = MetaModel::ApplyParams($this->Get('from_label'), $aContextArgs);
|
||||
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
|
||||
$sReplyToLabel = MetaModel::ApplyParams($this->Get('reply_to_label'), $aContextArgs);
|
||||
|
||||
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
||||
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
|
||||
|
||||
$oObj = $aContextArgs['this->object()'];
|
||||
$sMessageId = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
|
||||
$sReference = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
throw $e;
|
||||
}
|
||||
finally {
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
}
|
||||
|
||||
if (!is_null($oLog)) {
|
||||
// Note: we have to secure this because those values are calculated
|
||||
// inside the try statement, and we would like to keep track of as
|
||||
// many data as we could while some variables may still be undefined
|
||||
if (isset($sTo)) {
|
||||
$oLog->Set('to', $sTo);
|
||||
}
|
||||
if (isset($sCC)) {
|
||||
$oLog->Set('cc', $sCC);
|
||||
}
|
||||
if (isset($sBCC)) {
|
||||
$oLog->Set('bcc', $sBCC);
|
||||
}
|
||||
if (isset($sFrom)) {
|
||||
$oLog->Set('from', $sFrom);
|
||||
}
|
||||
if (isset($sSubject)) {
|
||||
$oLog->Set('subject', $sSubject);
|
||||
}
|
||||
if (isset($sBody)) {
|
||||
$oLog->Set('body', $sBody);
|
||||
}
|
||||
}
|
||||
$sStyles = file_get_contents(APPROOT.'css/email.css');
|
||||
$sStyles .= MetaModel::GetConfig()->Get('email_css');
|
||||
|
||||
|
||||
$oEmail = new EMail();
|
||||
|
||||
$aEmailContent = $this->PrepareMessageContent($aContextArgs, $oLog);
|
||||
$oEmail->SetSubject($aEmailContent['subject']);
|
||||
$oEmail->SetBody($aEmailContent['body'], 'text/html', $sStyles);
|
||||
$oEmail->SetRecipientTO($aEmailContent['to']);
|
||||
$oEmail->SetRecipientCC($aEmailContent['cc']);
|
||||
$oEmail->SetRecipientBCC($aEmailContent['bcc']);
|
||||
$oEmail->SetRecipientFrom($aEmailContent['from'], $aEmailContent['from_label']);
|
||||
$oEmail->SetRecipientReplyTo($aEmailContent['reply_to'], $aEmailContent['reply_to_label']);
|
||||
$oEmail->SetReferences($aEmailContent['references']);
|
||||
$oEmail->SetMessageId($aEmailContent['message_id']);
|
||||
$oEmail->SetInReplyTo($aEmailContent['in_reply_to']);
|
||||
|
||||
foreach($aEmailContent['attachments'] as $aAttachment) {
|
||||
$oEmail->AddAttachment($aAttachment['data'], $aAttachment['filename'], $aAttachment['mime_type']);
|
||||
|
||||
if ($this->IsBeingTested()) {
|
||||
$oEmail->SetSubject('TEST['.$sSubject.']');
|
||||
$sTestBody = $sBody;
|
||||
$sTestBody .= "<div style=\"border: dashed;\">\n";
|
||||
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
|
||||
$sTestBody .= "<p>The email should be sent with the following properties\n";
|
||||
$sTestBody .= "<ul>\n";
|
||||
$sTestBody .= "<li>TO: $sTo</li>\n";
|
||||
$sTestBody .= "<li>CC: $sCC</li>\n";
|
||||
$sTestBody .= "<li>BCC: $sBCC</li>\n";
|
||||
$sTestBody .= empty($sFromLabel) ? "<li>From: $sFrom</li>\n" : "<li>From: $sFromLabel <$sFrom></li>\n";
|
||||
$sTestBody .= empty($sReplyToLabel) ? "<li>Reply-To: $sReplyTo</li>\n" : "<li>Reply-To: $sReplyToLabel <$sReplyTo></li>\n";
|
||||
$sTestBody .= "<li>References: $sReference</li>\n";
|
||||
$sTestBody .= "</ul>\n";
|
||||
$sTestBody .= "</p>\n";
|
||||
$sTestBody .= "</div>\n";
|
||||
$oEmail->SetBody($sTestBody, 'text/html', $sStyles);
|
||||
$oEmail->SetRecipientTO($this->Get('test_recipient'));
|
||||
$oEmail->SetRecipientFrom($sFrom, $sFromLabel);
|
||||
$oEmail->SetReferences($sReference);
|
||||
$oEmail->SetMessageId($sMessageId);
|
||||
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
|
||||
$oEmail->SetInReplyTo($sReference);
|
||||
} else {
|
||||
$oEmail->SetSubject($sSubject);
|
||||
$oEmail->SetBody($sBody, 'text/html', $sStyles);
|
||||
$oEmail->SetRecipientTO($sTo);
|
||||
$oEmail->SetRecipientCC($sCC);
|
||||
$oEmail->SetRecipientBCC($sBCC);
|
||||
$oEmail->SetRecipientFrom($sFrom, $sFromLabel);
|
||||
$oEmail->SetRecipientReplyTo($sReplyTo, $sReplyToLabel);
|
||||
$oEmail->SetReferences($sReference);
|
||||
$oEmail->SetMessageId($sMessageId);
|
||||
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
|
||||
$oEmail->SetInReplyTo($sReference);
|
||||
}
|
||||
|
||||
|
||||
if (isset($aContextArgs['attachments']))
|
||||
{
|
||||
$aAttachmentReport = array();
|
||||
foreach($aContextArgs['attachments'] as $oDocument)
|
||||
{
|
||||
$oEmail->AddAttachment($oDocument->GetData(), $oDocument->GetFileName(), $oDocument->GetMimeType());
|
||||
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData()));
|
||||
}
|
||||
$oLog->Set('attachments', $aAttachmentReport);
|
||||
}
|
||||
|
||||
if (empty($this->m_aMailErrors))
|
||||
{
|
||||
if ($this->m_iRecipients == 0)
|
||||
@@ -509,7 +564,6 @@ class ActionEmail extends ActionNotification
|
||||
}
|
||||
else
|
||||
{
|
||||
$aErrors = [];
|
||||
$iRes = $oEmail->Send($aErrors, false, $oLog); // allow asynchronous mode
|
||||
switch ($iRes)
|
||||
{
|
||||
@@ -534,148 +588,13 @@ class ActionEmail extends ActionNotification
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aContextArgs
|
||||
* @param \EventNotification $oLog
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
* @since 3.1.0 N°918
|
||||
*/
|
||||
protected function PrepareMessageContent($aContextArgs, &$oLog): array
|
||||
{
|
||||
$aMessageContent = [
|
||||
'to' => '',
|
||||
'cc' => '',
|
||||
'bcc' => '',
|
||||
'from' => '',
|
||||
'from_label' => '',
|
||||
'reply_to' => '',
|
||||
'reply_to_label' => '',
|
||||
'subject' => '',
|
||||
'body' => '',
|
||||
'references' => '',
|
||||
'message_id' => '',
|
||||
'in_reply_to' => '',
|
||||
'attachments' => [],
|
||||
];
|
||||
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
|
||||
$sPreviousLanguage = Dict::GetUserLanguage();
|
||||
$aPreviousPluginProperties = ApplicationContext::GetPluginProperties('QueryLocalizerPlugin');
|
||||
if ($this->Get('language') !== '') {
|
||||
// If a language is specified for this action, force this language
|
||||
// when rendering all placeholders inside this message
|
||||
Dict::SetUserLanguage($this->Get('language'));
|
||||
AttributeDateTime::LoadFormatFromConfig();
|
||||
ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', $this->Get('language'));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$this->m_iRecipients = 0;
|
||||
$this->m_aMailErrors = array();
|
||||
|
||||
// Determine recipients
|
||||
//
|
||||
$aMessageContent['to'] = $this->FindRecipients('to', $aContextArgs);
|
||||
$aMessageContent['cc'] = $this->FindRecipients('cc', $aContextArgs);
|
||||
$aMessageContent['bcc'] = $this->FindRecipients('bcc', $aContextArgs);
|
||||
|
||||
$aMessageContent['from'] = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
|
||||
$aMessageContent['from_label'] = MetaModel::ApplyParams($this->Get('from_label'), $aContextArgs);
|
||||
$aMessageContent['reply_to'] = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
|
||||
$aMessageContent['reply_to_label'] = MetaModel::ApplyParams($this->Get('reply_to_label'), $aContextArgs);
|
||||
|
||||
$aMessageContent['subject'] = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
||||
$sBody = $this->BuildMessageBody(false);
|
||||
$aMessageContent['body'] = MetaModel::ApplyParams($sBody, $aContextArgs);
|
||||
|
||||
$oObj = $aContextArgs['this->object()'];
|
||||
$aMessageContent['message_id'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
|
||||
$aMessageContent['references'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
throw $e;
|
||||
}
|
||||
finally {
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
Dict::SetUserLanguage($sPreviousLanguage);
|
||||
AttributeDateTime::LoadFormatFromConfig();
|
||||
ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', $aPreviousPluginProperties['language_code'] ?? null);
|
||||
}
|
||||
|
||||
if (!is_null($oLog)) {
|
||||
// Note: we have to secure this because those values are calculated
|
||||
// inside the try statement, and we would like to keep track of as
|
||||
// many data as we could while some variables may still be undefined
|
||||
if (isset($aMessageContent['to'])) {
|
||||
$oLog->Set('to', $aMessageContent['to']);
|
||||
}
|
||||
if (isset($aMessageContent['cc'])) {
|
||||
$oLog->Set('cc', $aMessageContent['cc']);
|
||||
}
|
||||
if (isset($aMessageContent['bcc'])) {
|
||||
$oLog->Set('bcc', $aMessageContent['bcc']);
|
||||
}
|
||||
if (isset($aMessageContent['from'])) {
|
||||
$oLog->Set('from', $aMessageContent['from']);
|
||||
}
|
||||
if (isset($aMessageContent['subject'])) {
|
||||
$oLog->Set('subject', $aMessageContent['subject']);
|
||||
}
|
||||
if (isset($aMessageContent['body'])) {
|
||||
$oLog->Set('body', HTMLSanitizer::Sanitize($aMessageContent['body']));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->IsBeingTested()) {
|
||||
$sTestBody = $aMessageContent['body'];
|
||||
$sTestBody .= "<div style=\"border: dashed;\">\n";
|
||||
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
|
||||
$sTestBody .= "<p>The email should be sent with the following properties\n";
|
||||
$sTestBody .= "<ul>\n";
|
||||
$sTestBody .= "<li>TO: {$aMessageContent['to']}</li>\n";
|
||||
$sTestBody .= "<li>CC: {$aMessageContent['cc']}</li>\n";
|
||||
$sTestBody .= "<li>BCC: {$aMessageContent['bcc']}</li>\n";
|
||||
$sTestBody .= empty($aMessageContent['from_label']) ? "<li>From: {$aMessageContent['from']}</li>\n" : "<li>From: {$aMessageContent['from_label']} <{$aMessageContent['from']}></li>\n";
|
||||
$sTestBody .= empty($aMessageContent['reply_to_label']) ? "<li>Reply-To: {$aMessageContent['reply_to']}</li>\n" : "<li>Reply-To: {$aMessageContent['reply_to_label']} <{$aMessageContent['reply_to']}></li>\n";
|
||||
$sTestBody .= "<li>References: {$aMessageContent['references']}</li>\n";
|
||||
$sTestBody .= "</ul>\n";
|
||||
$sTestBody .= "</p>\n";
|
||||
$sTestBody .= "</div>\n";
|
||||
$aMessageContent['subject'] = 'TEST['.$aMessageContent['subject'].']';
|
||||
$aMessageContent['body'] = $sTestBody;
|
||||
$aMessageContent['to'] = $this->Get('test_recipient');
|
||||
// N°6677 Ensure emails in test are never sent to cc'd and bcc'd addresses
|
||||
$aMessageContent['cc'] = '';
|
||||
$aMessageContent['bcc'] = '';
|
||||
}
|
||||
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
|
||||
$aMessageContent['in_reply_to'] = $aMessageContent['references'];
|
||||
|
||||
if (isset($aContextArgs['attachments']))
|
||||
{
|
||||
$aAttachmentReport = array();
|
||||
foreach($aContextArgs['attachments'] as $oDocument)
|
||||
{
|
||||
$aMessageContent['attachments'][] = ['data' => $oDocument->GetData(), 'filename' => $oDocument->GetFileName(), 'mime_type' => $oDocument->GetMimeType()];
|
||||
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData()));
|
||||
}
|
||||
$oLog->Set('attachments', $aAttachmentReport);
|
||||
}
|
||||
|
||||
return $aMessageContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject $oObject
|
||||
* @param string $sHeaderName {@see \ActionEmail::ENUM_HEADER_NAME_REFERENCES}, {@see \ActionEmail::ENUM_HEADER_NAME_MESSAGE_ID}
|
||||
*
|
||||
* @return string The formatted identifier for $sHeaderName based on $oObject
|
||||
* @throws \Exception
|
||||
* @since 3.1.0 N°4849
|
||||
* @since 3.0.1 N°4849
|
||||
*/
|
||||
protected function GenerateIdentifierForHeaders(DBObject $oObject, string $sHeaderName): string
|
||||
{
|
||||
@@ -710,65 +629,4 @@ class ActionEmail extends ActionNotification
|
||||
]);
|
||||
throw new Exception($sErrorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose the body of the message from the 'body' attribute and the HTML template (if any)
|
||||
* @since 3.1.0 N°4849
|
||||
* @param bool $bHighlightPlaceholders If true add some extra HTML around placeholders to highlight them
|
||||
* @return string
|
||||
*/
|
||||
protected function BuildMessageBody(bool $bHighlightPlaceholders = false): string
|
||||
{
|
||||
$sBody = $this->Get('body');
|
||||
/** @var ormDocument $oHtmlTemplate */
|
||||
$oHtmlTemplate = $this->Get('html_template');
|
||||
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
|
||||
$sHtmlTemplate = $oHtmlTemplate->GetData();
|
||||
if (false !== mb_strpos($sHtmlTemplate, static::TEMPLATE_BODY_CONTENT)) {
|
||||
if ($bHighlightPlaceholders) {
|
||||
// Highlight the $content$ block
|
||||
$sBody = sprintf(static::CONTENT_HIGHLIGHT, $sBody);
|
||||
}
|
||||
$sBody = str_replace(static::TEMPLATE_BODY_CONTENT, $sBody, $oHtmlTemplate->GetData()); // str_replace is ok as long as both strings are well-formed UTF-8
|
||||
} else {
|
||||
$sBody = $oHtmlTemplate->GetData();
|
||||
}
|
||||
}
|
||||
if($bHighlightPlaceholders) {
|
||||
// Highlight all placeholders
|
||||
$sBody = preg_replace('/\\$([^$]+)\\$/', static::FIELD_HIGHLIGHT, $sBody);
|
||||
}
|
||||
return $sBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°4849
|
||||
* @inheritDoc
|
||||
* @see cmdbAbstractObject::DisplayBareRelations()
|
||||
*/
|
||||
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
parent::DisplayBareRelations($oPage, false);
|
||||
if (!$bEditMode) {
|
||||
$oPage->SetCurrentTab('action_email__preview', Dict::S('ActionEmail:preview_tab'), Dict::S('ActionEmail:preview_tab+'));
|
||||
$sBody = $this->BuildMessageBody(true);
|
||||
TwigHelper::RenderIntoPage($oPage, APPROOT.'/', 'templates/datamodel/ActionEmail/email-notification-preview', ['iframe_content' => $sBody]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0
|
||||
* @inheritDoc
|
||||
* @see cmdbAbstractObject::DoCheckToWrite()
|
||||
*/
|
||||
public function DoCheckToWrite()
|
||||
{
|
||||
parent::DoCheckToWrite();
|
||||
$oHtmlTemplate = $this->Get('html_template');
|
||||
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
|
||||
if (false === mb_strpos($oHtmlTemplate->GetData(), static::TEMPLATE_BODY_CONTENT)) {
|
||||
$this->m_aCheckWarnings[] = Dict::Format('ActionEmail:content_placeholder_missing', static::TEMPLATE_BODY_CONTENT, Dict::S('Class:ActionEmail/Attribute:body'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,15 +60,14 @@ function apc_store($key, $var = NULL, $ttl = 0)
|
||||
*/
|
||||
function apc_fetch($key)
|
||||
{
|
||||
if (is_array($key)) {
|
||||
$aResult = [];
|
||||
foreach ($key as $sKey) {
|
||||
if (is_array($key))
|
||||
{
|
||||
$aResult = array();
|
||||
foreach($key as $sKey)
|
||||
{
|
||||
$aResult[$sKey] = apcFile::FetchOneFile($sKey);
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
} elseif (is_null($key)) {
|
||||
return false;
|
||||
}
|
||||
return apcFile::FetchOneFile($key);
|
||||
}
|
||||
@@ -212,16 +211,21 @@ class apcFile
|
||||
*/
|
||||
static public function StoreOneFile($sKey, $value, $iTTL)
|
||||
{
|
||||
if (empty($sKey)) {
|
||||
if (empty($sKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (is_file(self::GetCacheFileName($sKey))) {
|
||||
|
||||
if (is_file(self::GetCacheFileName($sKey)))
|
||||
{
|
||||
@unlink(self::GetCacheFileName($sKey));
|
||||
}
|
||||
if (is_file(self::GetCacheFileName('-'.$sKey))) {
|
||||
if (is_file(self::GetCacheFileName('-'.$sKey)))
|
||||
{
|
||||
@unlink(self::GetCacheFileName('-'.$sKey));
|
||||
}
|
||||
if ($iTTL > 0) {
|
||||
if ($iTTL > 0)
|
||||
{
|
||||
// hint for ttl management
|
||||
$sKey = '-'.$sKey;
|
||||
}
|
||||
@@ -229,14 +233,15 @@ class apcFile
|
||||
$sFilename = self::GetCacheFileName($sKey);
|
||||
// try to create the folder
|
||||
$sDirname = dirname($sFilename);
|
||||
if (!is_dir($sDirname)) {
|
||||
if (!@mkdir($sDirname, 0755, true)) {
|
||||
if (!file_exists($sDirname))
|
||||
{
|
||||
if (!@mkdir($sDirname, 0755, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$bRes = !(@file_put_contents($sFilename, serialize($value), LOCK_EX) === false);
|
||||
self::AddFile($sFilename);
|
||||
|
||||
return $bRes;
|
||||
}
|
||||
|
||||
@@ -320,15 +325,19 @@ class apcFile
|
||||
*/
|
||||
static protected function ReadCacheLocked($sFilename)
|
||||
{
|
||||
$sContent = false;
|
||||
$file = @fopen($sFilename, 'r');
|
||||
if ($file !== false) {
|
||||
if (flock($file, LOCK_SH)) {
|
||||
$sContent = file_get_contents($sFilename);
|
||||
flock($file, LOCK_UN);
|
||||
}
|
||||
fclose($file);
|
||||
if (!is_file($sFilename))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$file = @fopen($sFilename, 'r');
|
||||
if ($file === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
flock($file, LOCK_SH);
|
||||
$sContent = @fread($file, @filesize($sFilename));
|
||||
flock($file, LOCK_UN);
|
||||
fclose($file);
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ use Combodo\iTop\Application\UI\Links\Set\BlockLinkSetDisplayAsProperty;
|
||||
use Combodo\iTop\Form\Field\LabelField;
|
||||
use Combodo\iTop\Form\Field\TextAreaField;
|
||||
use Combodo\iTop\Form\Form;
|
||||
use Combodo\iTop\Form\Validator\CustomRegexpValidator;
|
||||
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
|
||||
use Combodo\iTop\Form\Validator\Validator;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
use Combodo\iTop\Service\Links\LinkSetModel;
|
||||
@@ -91,11 +92,8 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
|
||||
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
|
||||
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
|
||||
|
||||
define('LINKSET_EDITWHEN_NEVER', 0); // The linkset cannot be edited at all from inside this object
|
||||
define('LINKSET_EDITWHEN_ON_HOST_EDITION', 1); // The only possible action is to open a new window to create a new object
|
||||
define('LINKSET_EDITWHEN_ON_HOST_DISPLAY', 2); // Show the usual 'Actions' popup menu
|
||||
define('LINKSET_EDITWHEN_ALWAYS', 3); // Show the usual 'Actions' popup menu
|
||||
|
||||
define('LINKSET_RELATIONTYPE_PROPERTY', 'property');
|
||||
define('LINKSET_RELATIONTYPE_LINK', 'link');
|
||||
|
||||
define('LINKSET_DISPLAY_STYLE_PROPERTY', 'property');
|
||||
define('LINKSET_DISPLAY_STYLE_TAB', 'tab');
|
||||
@@ -797,14 +795,14 @@ abstract class AttributeDefinition
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
// Default implementation, we don't really know what type $proposedValue will be
|
||||
return !(is_null($proposedValue));
|
||||
return is_null($proposedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
|
||||
*
|
||||
* @param mixed $proposedValue
|
||||
* @param \DBObject $oHostObj
|
||||
* @param $proposedValue
|
||||
* @param $oHostObj
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -994,21 +992,17 @@ abstract class AttributeDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to form a value, given JSON decoded data. This way the attribute itself handles the transformation from the JSON structure to the expected data (the one that
|
||||
* needs to be used in the {@see \DBObject::Set()} method).
|
||||
*
|
||||
* Note that for CSV and XML this isn't done yet (no delegation to the attribute but switch/case inside controllers) :/
|
||||
*
|
||||
* @see GetForJSON for the reverse operation
|
||||
* Helper to form a value, given JSON decoded data
|
||||
*
|
||||
* @param string $json JSON encoded value
|
||||
*
|
||||
* @return mixed JSON decoded data, depending on the attribute type
|
||||
* @return mixed JSON decoded data
|
||||
*
|
||||
* @see GetForJSON for the reverse operation
|
||||
*/
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
// Pass-through in most of the cases
|
||||
// Passthrough in most of the cases
|
||||
return $json;
|
||||
}
|
||||
|
||||
@@ -1127,7 +1121,7 @@ abstract class AttributeDefinition
|
||||
|
||||
// Validation pattern
|
||||
if ($this->GetValidationPattern() !== '') {
|
||||
$oFormField->AddValidator(new CustomRegexpValidator($this->GetValidationPattern()));
|
||||
$oFormField->AddValidator(new Validator($this->GetValidationPattern()));
|
||||
}
|
||||
|
||||
// Description
|
||||
@@ -1156,13 +1150,6 @@ abstract class AttributeDefinition
|
||||
$oFormField->AddMetadata('value-raw', (string)$oObject->Get($this->GetCode()));
|
||||
}
|
||||
|
||||
// We don't want to invalidate field because of old untouched values that are no longer valid
|
||||
$aModifiedAttCodes = $oObject->ListChanges();
|
||||
$bAttributeHasBeenModified = array_key_exists($this->GetCode(), $aModifiedAttCodes);
|
||||
if (false === $bAttributeHasBeenModified) {
|
||||
$oFormField->SetValidationDisabled(true);
|
||||
}
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
@@ -1705,19 +1692,20 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
|
||||
/**
|
||||
* @return string see LINKSET_EDITMODE_* constants
|
||||
* @since 3.1.0 N°5563 relations are edited using new attributes in details mode, but as nothing changed in edit mode we are still using edit_mode attribute
|
||||
*/
|
||||
public function GetEditMode()
|
||||
{
|
||||
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int see LINKSET_EDITWHEN_* constants
|
||||
* @since 3.1.1 3.2.0 N°6385
|
||||
* @return string see LINKSET_RELATIONTYPE_* constants
|
||||
* @since 3.1.0 N°5563
|
||||
*/
|
||||
public function GetEditWhen(): int
|
||||
public function GetRelationType()
|
||||
{
|
||||
return $this->GetOptional('edit_when', LINKSET_EDITWHEN_ALWAYS);
|
||||
return $this->GetOptional('relation_type', LINKSET_RELATIONTYPE_LINK);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1735,24 +1723,14 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the current Attribute has constraints (php constraints or datamodel constraints)
|
||||
* @return bool true if Attribute has constraints
|
||||
* @since 3.1.0 N°6228
|
||||
* @return boolean
|
||||
* @since 3.1.0 N°5563
|
||||
*/
|
||||
public function HasPHPConstraint(): bool
|
||||
public function GetReadOnly()
|
||||
{
|
||||
return $this->GetOptional('with_php_constraint', false);
|
||||
return $this->GetOptional('read_only', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool true if Attribute has computation (DB_LINKS_CHANGED event propagation, `with_php_computation` attribute xml property), false otherwise
|
||||
* @since 3.1.1 3.2.0 N°6228
|
||||
*/
|
||||
public function HasPHPComputation(): bool
|
||||
{
|
||||
return $this->GetOptional('with_php_computation', false);
|
||||
}
|
||||
|
||||
public function GetLinkedClass()
|
||||
{
|
||||
return $this->Get('linked_class');
|
||||
@@ -1779,58 +1757,12 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
}
|
||||
|
||||
/** @inheritDoc * */
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if($this->GetDisplayStyle() === LINKSET_DISPLAY_STYLE_TAB){
|
||||
return $this->GetAsHTMLForTab($sValue, $oHostObject, $bLocalize);
|
||||
}
|
||||
else{
|
||||
return $this->GetAsHTMLForProperty($sValue, $oHostObject, $bLocalize);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsHTMLForTab($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($sValue) && ($sValue instanceof ormLinkSet))
|
||||
{
|
||||
$sValue->Rewind();
|
||||
$aItems = array();
|
||||
while ($oObj = $sValue->Fetch())
|
||||
{
|
||||
// Show only relevant information (hide the external key to the current object)
|
||||
$aAttributes = array();
|
||||
foreach(MetaModel::ListAttributeDefs($this->GetLinkedClass()) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($sAttCode == $this->GetExtKeyToMe())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ($oAttDef->IsExternalField())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$sAttValue = $oObj->GetAsHTML($sAttCode);
|
||||
if (strlen($sAttValue) > 0)
|
||||
{
|
||||
$aAttributes[] = $sAttValue;
|
||||
}
|
||||
}
|
||||
$sAttributes = implode(', ', $aAttributes);
|
||||
$aItems[] = $sAttributes;
|
||||
}
|
||||
|
||||
return implode('<br/>', $aItems);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetAsHTMLForProperty($sValue, $oHostObject = null, $bLocalize = true): string
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true): string
|
||||
{
|
||||
try {
|
||||
|
||||
/** @var ormLinkSet $sValue */
|
||||
if (is_null($sValue) || $sValue->Count() === 0) {
|
||||
if ($sValue->Count() === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -2449,61 +2381,43 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
{
|
||||
if ($oFormField === null)
|
||||
{
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
$oFormField = new $sFormFieldClass($this->GetCode());
|
||||
}
|
||||
|
||||
// Setting target class
|
||||
// Setting target class
|
||||
if (!$this->IsIndirect()) {
|
||||
$sTargetClass = $this->GetLinkedClass();
|
||||
} else {
|
||||
/** @var \AttributeExternalKey $oRemoteAttDef */
|
||||
/** @var \AttributeLinkedSetIndirect $this */
|
||||
$oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
|
||||
$sTargetClass = $oRemoteAttDef->GetTargetClass();
|
||||
$sTargetClass = $this->GetLinkedClass();
|
||||
} else {
|
||||
/** @var \AttributeExternalKey $oRemoteAttDef */
|
||||
/** @var \AttributeLinkedSetIndirect $this */
|
||||
$oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
|
||||
$sTargetClass = $oRemoteAttDef->GetTargetClass();
|
||||
|
||||
/** @var \AttributeLinkedSetIndirect $this */
|
||||
$oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote());
|
||||
}
|
||||
$oFormField->SetTargetClass($sTargetClass);
|
||||
$oFormField->SetLinkedClass($this->GetLinkedClass());
|
||||
$oFormField->SetIndirect($this->IsIndirect());
|
||||
// Setting attcodes to display
|
||||
$aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list'));
|
||||
// - Adding friendlyname attribute to the list is not already in it
|
||||
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass);
|
||||
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) {
|
||||
$aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay);
|
||||
}
|
||||
// - Adding attribute properties
|
||||
$aAttributesToDisplay = array();
|
||||
foreach ($aAttCodesToDisplay as $sAttCodeToDisplay) {
|
||||
$oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay);
|
||||
$aAttributesToDisplay[$sAttCodeToDisplay] = [
|
||||
'att_code' => $sAttCodeToDisplay,
|
||||
'label' => $oAttDefToDisplay->GetLabel(),
|
||||
];
|
||||
}
|
||||
$oFormField->SetAttributesToDisplay($aAttributesToDisplay);
|
||||
/** @var \AttributeLinkedSetIndirect $this */
|
||||
$oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote());
|
||||
}
|
||||
$oFormField->SetTargetClass($sTargetClass);
|
||||
$oFormField->SetIndirect($this->IsIndirect());
|
||||
// Setting attcodes to display
|
||||
$aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list'));
|
||||
// - Adding friendlyname attribute to the list is not already in it
|
||||
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass);
|
||||
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) {
|
||||
$aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay);
|
||||
}
|
||||
// - Adding attribute labels
|
||||
$aAttributesToDisplay = array();
|
||||
foreach ($aAttCodesToDisplay as $sAttCodeToDisplay) {
|
||||
$oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay);
|
||||
$aAttributesToDisplay[$sAttCodeToDisplay] = $oAttDefToDisplay->GetLabel();
|
||||
}
|
||||
$oFormField->SetAttributesToDisplay($aAttributesToDisplay);
|
||||
|
||||
// Append lnk attributes (filtered from zlist)
|
||||
if ($this->IsIndirect()) {
|
||||
$aLnkAttDefToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($this->m_sHostClass, $this->m_sCode);
|
||||
$aLnkAttributesToDisplay = array();
|
||||
foreach ($aLnkAttDefToDisplay as $oLnkAttDefToDisplay) {
|
||||
$aLnkAttributesToDisplay[$oLnkAttDefToDisplay->GetCode()] = [
|
||||
'att_code' => $oLnkAttDefToDisplay->GetCode(),
|
||||
'label' => $oLnkAttDefToDisplay->GetLabel(),
|
||||
'mandatory' => !$oLnkAttDefToDisplay->IsNullAllowed(),
|
||||
];
|
||||
}
|
||||
$oFormField->SetLnkAttributesToDisplay($aLnkAttributesToDisplay);
|
||||
}
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
public function IsPartOfFingerprint()
|
||||
{
|
||||
@@ -2609,6 +2523,15 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
|
||||
return $this->GetOptional("duplicates", false);
|
||||
} // The same object may be linked several times... or not...
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @since 3.1.0 N°5563
|
||||
*/
|
||||
public function GetReadOnly()
|
||||
{
|
||||
return $this->GetOptional('read_only', false);
|
||||
}
|
||||
|
||||
public function GetTrackingLevel()
|
||||
{
|
||||
return $this->GetOptional('tracking_level',
|
||||
@@ -3061,11 +2984,6 @@ class AttributeObjectKey extends AttributeDBFieldVoid
|
||||
return ((int) $proposedValue) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param int|DBObject $proposedValue Object key or valid ({@see MetaModel::IsValidObject()}) datamodel object
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue))
|
||||
@@ -3078,6 +2996,7 @@ class AttributeObjectKey extends AttributeDBFieldVoid
|
||||
}
|
||||
if (MetaModel::IsValidObject($proposedValue))
|
||||
{
|
||||
/** @var \DBObject $proposedValue */
|
||||
return $proposedValue->GetKey();
|
||||
}
|
||||
|
||||
@@ -3197,7 +3116,7 @@ class AttributeDecimal extends AttributeDBField
|
||||
$iPrecision = $this->Get('decimals');
|
||||
$iNbIntegerDigits = $iNbDigits - $iPrecision - 1; // -1 because the first digit is treated separately in the pattern below
|
||||
|
||||
return "^[\-\+]?[0-9]\d{0,$iNbIntegerDigits}(\.\d{0,$iPrecision})?$";
|
||||
return "^[-+]?[0-9]\d{0,$iNbIntegerDigits}(\.\d{0,$iPrecision})?$";
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
@@ -3919,12 +3838,6 @@ class AttributeApplicationLanguage extends AttributeString
|
||||
{
|
||||
$aLanguageCodes[$sLangCode] = $aInfo['description'].' ('.$aInfo['localized_description'].')';
|
||||
}
|
||||
|
||||
// N°6462 This should be sorted directly in \Dict during the compilation but we can't for 2 reasons:
|
||||
// - Additional languages can be added on the fly even though it is not recommended
|
||||
// - Formatting is done at run time (just above)
|
||||
natcasesort($aLanguageCodes);
|
||||
|
||||
$aParams["allowed_values"] = new ValueSetEnum($aLanguageCodes);
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
@@ -4215,7 +4128,7 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (utils::IsNullOrEmptyString($sValue))
|
||||
if (strlen($sValue) == 0)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
@@ -4534,6 +4447,7 @@ class AttributeText extends AttributeString
|
||||
$sStyle = '';
|
||||
if (count($aStyles) > 0)
|
||||
{
|
||||
$aStyles[] = 'overflow:auto';
|
||||
$sStyle = 'style="'.implode(';', $aStyles).'"';
|
||||
}
|
||||
|
||||
@@ -5982,14 +5896,6 @@ class AttributeEnum extends AttributeString
|
||||
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
|
||||
}
|
||||
|
||||
// Sort by label only if necessary
|
||||
// See N°1646 and {@see \MFCompiler::CompileAttributeEnumValues()} for complete information as for why sort on labels is done at runtime while other sorting are done at compile time
|
||||
/** @var \ValueSetEnum $oValueSetDef */
|
||||
$oValueSetDef = $this->GetValuesDef();
|
||||
if ($oValueSetDef->IsSortedByValues()) {
|
||||
asort($aLocalizedValues);
|
||||
}
|
||||
|
||||
return $aLocalizedValues;
|
||||
}
|
||||
|
||||
@@ -6265,7 +6171,7 @@ class AttributeDateTime extends AttributeDBField
|
||||
/**
|
||||
* Load the 3 settings: date format, time format and data_time format from the configuration
|
||||
*/
|
||||
public static function LoadFormatFromConfig()
|
||||
protected static function LoadFormatFromConfig()
|
||||
{
|
||||
$aFormats = MetaModel::GetConfig()->Get('date_and_time_format');
|
||||
$sLang = Dict::GetUserLanguage();
|
||||
@@ -6513,11 +6419,6 @@ class AttributeDateTime extends AttributeDBField
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param int|string $proposedValue timestamp ({@see DateTime::getTimestamp()) or date as string, following the {@see GetInternalFormat} format.
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue))
|
||||
@@ -7252,27 +7153,14 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (MetaModel::IsValidObject($proposedValue)) {
|
||||
if (MetaModel::IsValidObject($proposedValue))
|
||||
{
|
||||
return $proposedValue->GetKey();
|
||||
}
|
||||
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
|
||||
/** @inheritdoc @since 3.1 */
|
||||
public function WriteExternalValues(DBObject $oHostObject): void
|
||||
{
|
||||
$sTargetKey = $oHostObject->Get($this->GetCode());
|
||||
$oFilter = DBSearch::FromOQL('SELECT `'.TemporaryObjectDescriptor::class.'` WHERE item_class=:class AND item_id=:id');
|
||||
$oSet = new DBObjectSet($oFilter, [], ['class' => $this->GetTargetClass(), 'id' => $sTargetKey]);
|
||||
while ($oTemporaryObjectDescriptor = $oSet->Fetch()) {
|
||||
$oTemporaryObjectDescriptor->Set('host_class', get_class($oHostObject));
|
||||
$oTemporaryObjectDescriptor->Set('host_id', $oHostObject->GetKey());
|
||||
$oTemporaryObjectDescriptor->Set('host_att_code', $this->GetCode());
|
||||
$oTemporaryObjectDescriptor->DBUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public function GetMaximumComboLength()
|
||||
{
|
||||
return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length'));
|
||||
@@ -7336,7 +7224,6 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
|
||||
if ($oFormField === null) {
|
||||
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
@@ -7367,12 +7254,19 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$oSearch = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
|
||||
$oSearch->SetInternalParams(array('this' => $oObject));
|
||||
$oFormField->SetSearch($oSearch);
|
||||
}
|
||||
|
||||
// If ExtKey is mandatory, we add a validator to ensure that the value 0 is not selected
|
||||
if ($oObject->GetAttributeFlags($this->GetCode()) & OPT_ATT_MANDATORY)
|
||||
{
|
||||
$oFormField->AddValidator(new NotEmptyExtKeyValidator());
|
||||
}
|
||||
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
@@ -7998,17 +7892,6 @@ class AttributeExternalField extends AttributeDefinition
|
||||
return $oExtAttDef->MakeRealValue($proposedValue, $oHostObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.1.0 N°6271 Delegate to remote attribute to ensure cascading computed values
|
||||
*/
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetSQLValues($value);
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
// This one could be used in case of filtering only
|
||||
@@ -8307,9 +8190,9 @@ class AttributeBlob extends AttributeDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param string $proposedValue Can be an URL (including an URL to iTop itself), or a local path (CSV import)
|
||||
* Users can provide the document from an URL (including an URL on iTop itself)
|
||||
* for CSV import. Administrators can even provide the path to a local file
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @see AttributeDefinition::MakeRealValue()
|
||||
*/
|
||||
@@ -8417,7 +8300,7 @@ class AttributeBlob extends AttributeDefinition
|
||||
$aValues[$this->GetCode().'_data'] = '';
|
||||
$aValues[$this->GetCode().'_mimetype'] = '';
|
||||
$aValues[$this->GetCode().'_filename'] = '';
|
||||
$aValues[$this->GetCode().'_downloads_count'] = ormDocument::DEFAULT_DOWNLOADS_COUNT;
|
||||
$aValues[$this->GetCode().'_downloads_count'] = ''; // Note: Should this be set to \ormDocument::DEFAULT_DOWNLOADS_COUNT ?
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
@@ -8550,7 +8433,7 @@ class AttributeBlob extends AttributeDefinition
|
||||
$sFingerprint = '';
|
||||
if ($value instanceOf ormDocument)
|
||||
{
|
||||
$sFingerprint = $value->GetSignature();
|
||||
$sFingerprint = md5($value->GetData());
|
||||
}
|
||||
|
||||
return $sFingerprint;
|
||||
@@ -8607,22 +8490,6 @@ class AttributeBlob extends AttributeDefinition
|
||||
return utils::IsNotNullOrEmptyString($proposedValue->GetData()) && utils::IsNotNullOrEmptyString($proposedValue->GetFileName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @param \ormDocument $original
|
||||
* @param \ormDocument $value
|
||||
* @since N°6502
|
||||
*/
|
||||
public function RecordAttChange(DBObject $oObject, $original, $value): void
|
||||
{
|
||||
// N°6502 Don't record history if only the download count has changed
|
||||
if ((null !== $original) && (null !== $value) && $original->EqualsExceptDownloadsCount($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::RecordAttChange($oObject, $original, $value);
|
||||
}
|
||||
|
||||
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
|
||||
{
|
||||
if (is_null($original)) {
|
||||
@@ -8740,7 +8607,7 @@ class AttributeImage extends AttributeBlob
|
||||
$sCssClasses .= ' '.(($bIsCustomImage) ? 'attribute-image-custom' : 'attribute-image-default');
|
||||
|
||||
// Important: If you change this, mind updating edit_image.js as well
|
||||
return '<div class="'.$sCssClasses.'" style="max-width: min('.$sMaxWidthPx.',100%); max-height: min('.$sMaxHeightPx.',100%); aspect-ratio: '.$iMaxWidth.' / '.$iMaxHeight.'">'.$sRet.'</div>';
|
||||
return '<div class="'.$sCssClasses.'" style="max-width: '.$sMaxWidthPx.'; max-height: '.$sMaxHeightPx.'; aspect-ratio: '.$iMaxWidth.' / '.$iMaxHeight.'">'.$sRet.'</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8754,7 +8621,7 @@ class AttributeImage extends AttributeBlob
|
||||
* @since 2.7.0 change visibility to protected
|
||||
*/
|
||||
protected function GetHtmlForImageUrl($sUrl, $iMaxWidthPx, $iMaxHeightPx) {
|
||||
return '<img src="'.$sUrl.'" style="max-width: min('.$iMaxWidthPx.',100%); max-height: min('.$iMaxHeightPx.',100%)">';
|
||||
return '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'; max-height: '.$iMaxHeightPx.'">';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -8782,7 +8649,7 @@ class AttributeImage extends AttributeBlob
|
||||
return 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
|
||||
}
|
||||
|
||||
return $value->GetDisplayURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
|
||||
return $value->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
@@ -8809,11 +8676,8 @@ class AttributeImage extends AttributeBlob
|
||||
}
|
||||
else
|
||||
{
|
||||
$oDefaultImage = $this->Get('default_image');
|
||||
if (is_object($oDefaultImage) && !$oDefaultImage->IsEmpty()) {
|
||||
$oFormField->SetDownloadUrl($oDefaultImage);
|
||||
$oFormField->SetDisplayUrl($oDefaultImage);
|
||||
}
|
||||
$oFormField->SetDownloadUrl($this->Get('default_image'));
|
||||
$oFormField->SetDisplayUrl($this->Get('default_image'));
|
||||
}
|
||||
|
||||
return $oFormField;
|
||||
@@ -11392,9 +11256,6 @@ class AttributeClassAttCodeSet extends AttributeSet
|
||||
}
|
||||
$aAllowedAttributes[$sAttCode] = $sLabel;
|
||||
}
|
||||
// N°6460 Always sort on the labels, not on the datamodel definition order
|
||||
natcasesort($aAllowedAttributes);
|
||||
|
||||
return $aAllowedAttributes;
|
||||
}
|
||||
|
||||
@@ -11508,11 +11369,6 @@ class AttributeClassAttCodeSet extends AttributeSet
|
||||
}
|
||||
return '<span class="'.implode(' ', $this->aCSSClasses).'">'.$value.'</span>';
|
||||
}
|
||||
|
||||
public function IsNull($proposedValue)
|
||||
{
|
||||
return (empty($proposedValue));
|
||||
}
|
||||
}
|
||||
|
||||
class AttributeQueryAttCodeSet extends AttributeSet
|
||||
@@ -13192,7 +13048,6 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
public function GetHandler($aValues = null)
|
||||
{
|
||||
$sHandlerClass = $this->Get('handler_class');
|
||||
/** @var \TemplateFieldsHandler $oHandler */
|
||||
$oHandler = new $sHandlerClass($this->GetCode());
|
||||
if (!is_null($aValues))
|
||||
{
|
||||
@@ -13217,50 +13072,36 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
/**
|
||||
* Makes the string representation out of the values given by the form defined in GetDisplayForm
|
||||
*/
|
||||
public function ReadValueFromPostedForm($oHostObject, $sFormPrefix) {
|
||||
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'), true);
|
||||
public function ReadValueFromPostedForm($oHostObject, $sFormPrefix)
|
||||
{
|
||||
$aRawData = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->GetCode()}", '{}', 'raw_data'), true);
|
||||
if ($aRawData != null) {
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRawData);
|
||||
}
|
||||
else {
|
||||
} else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObject) {
|
||||
if (is_object($proposedValue) && ($proposedValue instanceof ormCustomFieldsValue)) {
|
||||
if (false === $oHostObject->IsNew()) {
|
||||
// In that case we need additional keys : see \TemplateFieldsHandler::DoBuildForm
|
||||
$aRequestTemplateValues = $proposedValue->GetValues();
|
||||
if (false === array_key_exists('current_template_id', $aRequestTemplateValues)) {
|
||||
$aRequestTemplateValues['current_template_id'] = $aRequestTemplateValues['template_id'];
|
||||
$aRequestTemplateValues['current_template_data'] = $aRequestTemplateValues['template_data'];
|
||||
$proposedValue = new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRequestTemplateValues);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($proposedValue->GetHostObject())) {
|
||||
// the object might not be set : for example in \AttributeCustomFields::FromJSONToValue we don't have the object available :(
|
||||
$proposedValue->SetHostObject($oHostObject);
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObject)
|
||||
{
|
||||
if (is_object($proposedValue) && ($proposedValue instanceof ormCustomFieldsValue))
|
||||
{
|
||||
return $proposedValue;
|
||||
}
|
||||
|
||||
if (is_string($proposedValue)) {
|
||||
elseif (is_string($proposedValue))
|
||||
{
|
||||
$aValues = json_decode($proposedValue, true);
|
||||
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aValues);
|
||||
}
|
||||
|
||||
if (is_array($proposedValue)) {
|
||||
elseif (is_array($proposedValue))
|
||||
{
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode(), $proposedValue);
|
||||
}
|
||||
|
||||
if (is_null($proposedValue)) {
|
||||
elseif (is_null($proposedValue))
|
||||
{
|
||||
return new ormCustomFieldsValue($oHostObject, $this->GetCode());
|
||||
}
|
||||
|
||||
throw new Exception('Unexpected type for the value of a custom fields attribute: '.gettype($proposedValue));
|
||||
}
|
||||
|
||||
@@ -13385,11 +13226,27 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
*/
|
||||
public function CheckValue(DBObject $oHostObject, $value)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
$oHandler = $this->GetHandler($value->GetValues());
|
||||
$oHandler->BuildForm($oHostObject, '');
|
||||
$ret = $oHandler->Validate($oHostObject);
|
||||
} catch (Exception $e) {
|
||||
$oForm = $oHandler->GetForm();
|
||||
$oForm->Validate();
|
||||
if ($oForm->GetValid())
|
||||
{
|
||||
$ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aMessages = array();
|
||||
foreach($oForm->GetErrorMessages() as $sFieldId => $aFieldMessages)
|
||||
{
|
||||
$aMessages[] = $sFieldId.': '.implode(', ', $aFieldMessages);
|
||||
}
|
||||
$ret = 'Invalid value: '.implode(', ', $aMessages);
|
||||
}
|
||||
} catch (Exception $e)
|
||||
{
|
||||
$ret = $e->getMessage();
|
||||
}
|
||||
|
||||
@@ -13531,13 +13388,11 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @return ?\ormCustomFieldsValue with empty host object as we don't have it here (most consumers don't have an object in their context, for example in \RestUtils::GetObjectSetFromKey)
|
||||
* The host object will be set in {@see MakeRealValue}
|
||||
* All the necessary checks will be done in {@see CheckValue}
|
||||
* @return ?\ormCustomFieldsValue
|
||||
*/
|
||||
public function FromJSONToValue($json)
|
||||
{
|
||||
return ormCustomFieldsValue::FromJSONToValue($json, $this);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function Equals($val1, $val2)
|
||||
|
||||
@@ -35,7 +35,6 @@ MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php');
|
||||
MetaModel::IncludeModule('core/backgroundtask.class.inc.php');
|
||||
MetaModel::IncludeModule('core/inlineimage.class.inc.php');
|
||||
MetaModel::IncludeModule('core/counter.class.inc.php');
|
||||
MetaModel::IncludeModule('core/TemporaryObjectDescriptor.php');
|
||||
|
||||
MetaModel::IncludeModule('webservices/webservices.basic.php');
|
||||
|
||||
|
||||
@@ -32,57 +32,23 @@ abstract class CellChangeSpec
|
||||
return $this->m_proposedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function GetCLIValue(bool $bLocalizedValues = false): string
|
||||
public function GetDisplayableValue()
|
||||
{
|
||||
if (is_object($this->m_proposedValue)) {
|
||||
if ($this->m_proposedValue instanceof ReportValue) {
|
||||
return $this->m_proposedValue->GetAsCSV($bLocalizedValues, ',', '"');
|
||||
}
|
||||
throw new Exception('Unexpected class : '. get_class($this->m_proposedValue));
|
||||
}
|
||||
return $this->m_proposedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public function GetHTMLValue(bool $bLocalizedValues = false): string
|
||||
{
|
||||
if (is_object($this->m_proposedValue)) {
|
||||
if ($this->m_proposedValue instanceof ReportValue) {
|
||||
return $this->m_proposedValue->GetAsHTML($bLocalizedValues);
|
||||
}
|
||||
throw new Exception('Unexpected class : '. get_class($this->m_proposedValue));
|
||||
}
|
||||
return utils::EscapeHtml($this->m_proposedValue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
*/
|
||||
public function SetDisplayableValue(string $sDisplayableValue)
|
||||
{
|
||||
$this->m_proposedValue = $sDisplayableValue;
|
||||
}
|
||||
|
||||
public function GetOql()
|
||||
{
|
||||
return $this->m_sOql;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.2.0
|
||||
* @since 3.1.0 N°5305
|
||||
*/
|
||||
public function GetCLIValueAndDescription(): string
|
||||
public function GetDisplayableValueAndDescription(): string
|
||||
{
|
||||
return sprintf("%s%s",
|
||||
$this->GetCLIValue(),
|
||||
$this->GetDisplayableValue(),
|
||||
$this->GetDescription()
|
||||
);
|
||||
}
|
||||
@@ -131,25 +97,13 @@ class CellStatus_Issue extends CellStatus_Modify
|
||||
parent::__construct($proposedValue, $previousValue);
|
||||
}
|
||||
|
||||
public function GetCLIValue(bool $bLocalizedValues = false): string
|
||||
{
|
||||
if (is_null($this->m_proposedValue)) {
|
||||
return Dict::Format('UI:CSVReport-Value-SetIssue');
|
||||
}
|
||||
return Dict::Format('UI:CSVReport-Value-ChangeIssue',$this->m_proposedValue);
|
||||
}
|
||||
|
||||
public function GetHTMLValue(bool $bLocalizedValues = false): string
|
||||
public function GetDisplayableValue()
|
||||
{
|
||||
if (is_null($this->m_proposedValue))
|
||||
{
|
||||
return Dict::Format('UI:CSVReport-Value-SetIssue');
|
||||
}
|
||||
if ($this->m_proposedValue instanceof ReportValue)
|
||||
{
|
||||
return Dict::Format('UI:CSVReport-Value-ChangeIssue', $this->m_proposedValue->GetAsHTML($bLocalizedValues));
|
||||
}
|
||||
return Dict::Format('UI:CSVReport-Value-ChangeIssue',utils::EscapeHtml($this->m_proposedValue));
|
||||
return Dict::Format('UI:CSVReport-Value-ChangeIssue', \utils::EscapeHtml($this->m_proposedValue));
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
@@ -157,12 +111,12 @@ class CellStatus_Issue extends CellStatus_Modify
|
||||
return $this->m_sReason;
|
||||
}
|
||||
/*
|
||||
* @since 3.2.0
|
||||
* @since 3.1.0 N°5305
|
||||
*/
|
||||
public function GetCLIValueAndDescription(): string
|
||||
public function GetDisplayableValueAndDescription(): string
|
||||
{
|
||||
return sprintf("%s. %s",
|
||||
$this->GetCLIValue(),
|
||||
$this->GetDisplayableValue(),
|
||||
$this->GetDescription()
|
||||
);
|
||||
}
|
||||
@@ -182,12 +136,6 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
/** @var string|null $m_sTargetClass */
|
||||
private $m_sTargetClass;
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
* @var string $sAllowedValuesSearch
|
||||
*/
|
||||
private $sAllowedValuesSearch;
|
||||
|
||||
/**
|
||||
* CellStatus_SearchIssue constructor.
|
||||
* @since 3.1.0 N°5305
|
||||
@@ -196,18 +144,16 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
* @param string $sReason : main message
|
||||
* @param null $sClass : used for additional message that provides allowed values for current class $sClass
|
||||
* @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)
|
||||
{
|
||||
parent::__construct(null, null, $sReason);
|
||||
$this->sSerializedSearch = $sSerializedSearch;
|
||||
$this->m_sAllowedValues = $sAllowedValues;
|
||||
$this->m_sTargetClass = $sClass;
|
||||
$this->sAllowedValuesSearch = $sAllowedValuesSearch;
|
||||
}
|
||||
|
||||
public function GetCLIValue(bool $bLocalizedValues = false): string
|
||||
public function GetDisplayableValue()
|
||||
{
|
||||
if (null === $this->m_sReason) {
|
||||
return Dict::Format('UI:CSVReport-Value-NoMatch', '');
|
||||
@@ -216,15 +162,6 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
return $this->m_sReason;
|
||||
}
|
||||
|
||||
public function GetHTMLValue(bool $bLocalizedValues = false): string
|
||||
{
|
||||
if (null === $this->m_sReason) {
|
||||
return Dict::Format('UI:CSVReport-Value-NoMatch', '');
|
||||
}
|
||||
|
||||
return utils::EscapeHtml($this->m_sReason);
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
if (\utils::IsNullOrEmptyString($this->m_sAllowedValues) ||
|
||||
@@ -242,18 +179,7 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
public function GetSearchLinkUrl()
|
||||
{
|
||||
return sprintf("UI.php?operation=search&filter=%s",
|
||||
rawurlencode($this->sSerializedSearch ?? "")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
* @return null|string
|
||||
*/
|
||||
public function GetAllowedValuesLinkUrl(): ?string
|
||||
{
|
||||
return sprintf("UI.php?operation=search&filter=%s",
|
||||
rawurlencode($this->sAllowedValuesSearch ?? "")
|
||||
rawurlencode($this->sSerializedSearch)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -271,42 +197,6 @@ class CellStatus_NullIssue extends CellStatus_Issue
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class to differ formatting depending on the caller
|
||||
*/
|
||||
class ReportValue
|
||||
{
|
||||
protected DBObject $oObject;
|
||||
protected string $sAttCode;
|
||||
protected bool $bOriginal;
|
||||
|
||||
/**
|
||||
* @param DBObject $oObject
|
||||
* @param string $sAttCode
|
||||
* @param bool $bOriginal
|
||||
*/
|
||||
public function __construct(DBObject $oObject, string $sAttCode, bool $bOriginal)
|
||||
{
|
||||
$this->oObject = $oObject;
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->bOriginal = $bOriginal;
|
||||
}
|
||||
|
||||
public function GetAsHTML(bool $bLocalizedValues)
|
||||
{
|
||||
if ($this->bOriginal) {
|
||||
return $this->oObject->GetOriginalAsHTML($this->sAttCode, $bLocalizedValues);
|
||||
}
|
||||
return $this->oObject->GetAsHTML($this->sAttCode, $bLocalizedValues);
|
||||
}
|
||||
public function GetAsCSV (bool $bLocalizedValues, string $sCsvSep, string $sCsvDelimiter) {
|
||||
if ($this->bOriginal) {
|
||||
return $this->oObject->GetOriginalAsCSV($this->sAttCode, $sCsvSep, $sCsvDelimiter, $bLocalizedValues);
|
||||
}
|
||||
return $this->oObject->GetAsCSV($this->sAttCode, $sCsvSep, $sCsvDelimiter, $bLocalizedValues);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class CellStatus_Ambiguous extends CellStatus_Issue
|
||||
{
|
||||
@@ -345,7 +235,7 @@ class CellStatus_Ambiguous extends CellStatus_Issue
|
||||
public function GetSearchLinkUrl()
|
||||
{
|
||||
return sprintf("UI.php?operation=search&filter=%s",
|
||||
rawurlencode($this->sSerializedSearch ?? "")
|
||||
rawurlencode($this->sSerializedSearch)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -493,6 +383,22 @@ class BulkChange
|
||||
$this->m_aExtKeysMappingCache = array();
|
||||
}
|
||||
|
||||
protected $m_bReportHtml = false;
|
||||
protected $m_sReportCsvSep = ',';
|
||||
protected $m_sReportCsvDelimiter = '"';
|
||||
|
||||
public function SetReportHtml()
|
||||
{
|
||||
$this->m_bReportHtml = true;
|
||||
}
|
||||
|
||||
public function SetReportCsv($sSeparator = ',', $sDelimiter = '"')
|
||||
{
|
||||
$this->m_bReportHtml = false;
|
||||
$this->m_sReportCsvSep = $sSeparator;
|
||||
$this->m_sReportCsvDelimiter = $sDelimiter;
|
||||
}
|
||||
|
||||
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
|
||||
{
|
||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
@@ -510,7 +416,7 @@ class BulkChange
|
||||
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
|
||||
}
|
||||
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
|
||||
}
|
||||
|
||||
$oExtObjects = new CMDBObjectSet($oReconFilter);
|
||||
@@ -534,7 +440,7 @@ class BulkChange
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oTargetObj
|
||||
* @param \DBObject $oTargetObj
|
||||
* @param array $aRowData
|
||||
* @param array $aErrors
|
||||
*
|
||||
@@ -597,7 +503,7 @@ class BulkChange
|
||||
}
|
||||
$aCacheKeys[] = $value;
|
||||
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
|
||||
}
|
||||
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
|
||||
$iForeignKey = null;
|
||||
@@ -666,7 +572,7 @@ class BulkChange
|
||||
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
|
||||
{
|
||||
// Report the change on reconciliation values as well
|
||||
$aResults[$iCol] = new CellStatus_Modify($aRowData[$iCol]);
|
||||
$aResults[$iCol] = new CellStatus_Modify(utils::HtmlEntities($aRowData[$iCol]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -744,32 +650,50 @@ class BulkChange
|
||||
// Reporting on fields
|
||||
//
|
||||
$aChangedFields = $oTargetObj->ListChanges();
|
||||
foreach ($this->m_aAttList as $sAttCode => $iCol) {
|
||||
if ($sAttCode == 'id') {
|
||||
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
|
||||
foreach ($this->m_aAttList as $sAttCode => $iCol)
|
||||
{
|
||||
if ($sAttCode == 'id')
|
||||
{
|
||||
$aResults[$iCol]= new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
|
||||
}
|
||||
else {
|
||||
$sCurValue = new ReportValue($oTargetObj, $sAttCode, false);
|
||||
$sOrigValue = new ReportValue($oTargetObj, $sAttCode, true);
|
||||
if (isset($aErrors[$sAttCode])) {
|
||||
$aResults[$iCol]= new CellStatus_Issue($aRowData[$iCol], $sOrigValue, $aErrors[$sAttCode]);
|
||||
else
|
||||
{
|
||||
if ($this->m_bReportHtml)
|
||||
{
|
||||
$sCurValue = $oTargetObj->GetAsHTML($sAttCode, $this->m_bLocalizedValues);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsHTML($sAttCode, $this->m_bLocalizedValues);
|
||||
}
|
||||
elseif (array_key_exists($sAttCode, $aChangedFields)){
|
||||
if ($oTargetObj->IsNew()) {
|
||||
else
|
||||
{
|
||||
$sCurValue = $oTargetObj->GetAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
|
||||
}
|
||||
if (isset($aErrors[$sAttCode]))
|
||||
{
|
||||
$aResults[$iCol]= new CellStatus_Issue(utils::HtmlEntities($aRowData[$iCol]), $sOrigValue, $aErrors[$sAttCode]);
|
||||
}
|
||||
elseif (array_key_exists($sAttCode, $aChangedFields))
|
||||
{
|
||||
if ($oTargetObj->IsNew())
|
||||
{
|
||||
$aResults[$iCol]= new CellStatus_Void($sCurValue);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
$aResults[$iCol]= new CellStatus_Modify($sCurValue, $sOrigValue);
|
||||
}
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
// By default... nothing happens
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeDateTime) {
|
||||
if ($oAttDef instanceof AttributeDateTime)
|
||||
{
|
||||
$aResults[$iCol]= new CellStatus_Void($oAttDef->GetFormat()->Format($aRowData[$iCol]));
|
||||
}
|
||||
else {
|
||||
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
|
||||
else
|
||||
{
|
||||
$aResults[$iCol]= new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -821,7 +745,6 @@ class BulkChange
|
||||
$oDbSearchWithoutAnyCondition->AllowAllData(false);
|
||||
$oExtObjectSetWithCurrentUserPermissions = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
|
||||
$iCurrentUserRightsObjectCount = $oExtObjectSetWithCurrentUserPermissions->Count();
|
||||
$sAllowedValuesOql = $oDbSearchWithoutAnyCondition->serialize();
|
||||
|
||||
if ($iCurrentUserRightsObjectCount === 0){
|
||||
// No objects visible by current user
|
||||
@@ -834,7 +757,7 @@ class BulkChange
|
||||
// Possibles values are displayed to UI user. we have to limit the amount of displayed values
|
||||
$oExtObjectSetWithCurrentUserPermissions->SetLimit(4);
|
||||
for($i = 0; $i < 3; $i++){
|
||||
/** @var DBObject $oVisibleObject */
|
||||
/** @var \DBObject $oVisibleObject */
|
||||
$oVisibleObject = $oExtObjectSetWithCurrentUserPermissions->Fetch();
|
||||
if (is_null($oVisibleObject)){
|
||||
break;
|
||||
@@ -862,7 +785,7 @@ class BulkChange
|
||||
if ($iAllowAllDataObjectCount != $iCurrentUserRightsObjectCount) {
|
||||
// No match and some objects NOT visible by current user. including current search maybe...
|
||||
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser', $oDbSearchWithConditions->GetClass());
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues, $sAllowedValuesOql);
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
|
||||
}
|
||||
|
||||
// No match. This is not linked to any right issue
|
||||
@@ -873,7 +796,7 @@ class BulkChange
|
||||
}
|
||||
$value =implode(" ", $aCurrentValueFields);
|
||||
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch', $value);
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues, $sAllowedValuesOql);
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
|
||||
}
|
||||
|
||||
protected function PrepareMissingObject(&$oTargetObj, &$aErrors)
|
||||
@@ -1201,7 +1124,7 @@ class BulkChange
|
||||
if (!preg_match($sRegExp, $sValue))
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
$aResult[$iRow][$iCol] = new CellStatus_Issue($sValue, null, $sErrorMsg);
|
||||
$aResult[$iRow][$iCol] = new CellStatus_Issue(utils::HtmlEntities($sValue), null, $sErrorMsg);
|
||||
|
||||
}
|
||||
else
|
||||
@@ -1214,7 +1137,6 @@ class BulkChange
|
||||
}
|
||||
else
|
||||
{
|
||||
// almost impossible ti reproduce since even incorrect dates with correct formats are formated and $oDate will not be false
|
||||
// Leave the cell unchanged
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
$aResult[$iRow][$iCol] = new CellStatus_Issue($sValue, null, $sErrorMsg);
|
||||
@@ -1354,7 +1276,7 @@ class BulkChange
|
||||
{
|
||||
if (!array_key_exists($iCol, $aResult[$iRow]))
|
||||
{
|
||||
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
$aResult[$iRow][$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
|
||||
}
|
||||
}
|
||||
foreach($this->m_aExtKeys as $sAttCode => $aForeignAtts)
|
||||
@@ -1368,7 +1290,7 @@ class BulkChange
|
||||
if (!array_key_exists($iCol, $aResult[$iRow]))
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
$aResult[$iRow][$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,9 +149,7 @@ abstract class BulkExport
|
||||
$this->oSearch = null;
|
||||
$this->iChunkSize = 0;
|
||||
$this->sFormatCode = null;
|
||||
$this->aStatusInfo = [
|
||||
'show_obsolete_data' => utils::ShowObsoleteData(),
|
||||
];
|
||||
$this->aStatusInfo = array();
|
||||
$this->oBulkExportResult = null;
|
||||
$this->sTmpFile = '';
|
||||
$this->bLocalizeOutput = false;
|
||||
@@ -205,17 +203,15 @@ abstract class BulkExport
|
||||
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
|
||||
{
|
||||
$sFormatCode = $oInfo->Get('format');
|
||||
$aStatusInfo = json_decode($oInfo->Get('status_info'),true);
|
||||
|
||||
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
|
||||
$oSearch->SetShowObsoleteData($aStatusInfo['show_obsolete_data']);
|
||||
|
||||
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
|
||||
if ($oBulkExporter)
|
||||
{
|
||||
$oBulkExporter->SetFormat($sFormatCode);
|
||||
$oBulkExporter->SetObjectList($oSearch);
|
||||
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
|
||||
$oBulkExporter->SetStatusInfo($aStatusInfo);
|
||||
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
|
||||
|
||||
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
|
||||
|
||||
@@ -293,7 +289,6 @@ abstract class BulkExport
|
||||
*/
|
||||
public function SetObjectList(DBSearch $oSearch)
|
||||
{
|
||||
$oSearch->SetShowObsoleteData($this->aStatusInfo['show_obsolete_data']);
|
||||
$this->oSearch = $oSearch;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,17 +19,17 @@ class CMDBChange extends DBObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "date",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_change",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "date",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_change",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
'indexes' => array(
|
||||
'indexes' => array(
|
||||
array('origin'),
|
||||
),
|
||||
)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
|
||||
@@ -52,17 +52,17 @@ class CMDBChangeOp extends DBObject implements iCMDBChangeOp
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "optype",
|
||||
'indexes' => array(
|
||||
'indexes' => array(
|
||||
array('objclass', 'objkey'),
|
||||
),
|
||||
)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
@@ -121,13 +121,13 @@ class CMDBChangeOpCreate extends CMDBChangeOp
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_create",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_create",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -157,13 +157,13 @@ class CMDBChangeOpDelete extends CMDBChangeOp
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_delete",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_delete",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -198,13 +198,13 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -231,13 +231,13 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_scalar",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_scalar",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -290,14 +290,14 @@ class CMDBChangeOpSetAttributeTagSet extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_tagset",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_tagset",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
@@ -350,7 +350,7 @@ class CMDBChangeOpSetAttributeURL extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
@@ -419,13 +419,13 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_data",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_data",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -498,13 +498,13 @@ class CMDBChangeOpSetAttributeOneWayPassword extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_pwd",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_pwd",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -561,13 +561,13 @@ class CMDBChangeOpSetAttributeEncrypted extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_encrypted",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_encrypted",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -627,13 +627,13 @@ class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_text",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_text",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -695,13 +695,13 @@ class CMDBChangeOpSetAttributeLongText extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_longtext",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_longtext",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -760,13 +760,13 @@ class CMDBChangeOpSetAttributeHTML extends CMDBChangeOpSetAttributeLongText
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_html",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_html",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -824,13 +824,13 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_log",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_log",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -903,13 +903,13 @@ class CMDBChangeOpPlugin extends CMDBChangeOp
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_plugin",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_plugin",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -944,13 +944,13 @@ abstract class CMDBChangeOpSetAttributeLinks extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_links",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_links",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -977,13 +977,13 @@ class CMDBChangeOpSetAttributeLinksAddRemove extends CMDBChangeOpSetAttributeLin
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_links_addremove",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_links_addremove",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -1044,13 +1044,13 @@ class CMDBChangeOpSetAttributeLinksTune extends CMDBChangeOpSetAttributeLinks
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_links_tune",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_links_tune",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -1134,13 +1134,13 @@ class CMDBChangeOpSetAttributeCustomFields extends CMDBChangeOpSetAttribute
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb, grant_by_profile",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_custfields",
|
||||
"db_key_field" => "id",
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt_custfields",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
|
||||
@@ -335,7 +335,7 @@ abstract class CMDBObject extends DBObject
|
||||
$oMyChangeOp->Set("objclass", MetaModel::GetRootClass(get_class($this)));
|
||||
$oMyChangeOp->Set("objkey", $objkey);
|
||||
$oMyChangeOp->Set("fclass", get_class($this));
|
||||
$oMyChangeOp->SetTrim("fname", $this->GetRawName()); // Protect against very long friendly names
|
||||
$oMyChangeOp->Set("fname", substr($this->GetRawName(), 0, 255)); // Protect against very long friendly names
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
|
||||
@@ -438,8 +438,7 @@ abstract class CMDBObject extends DBObject
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.0 N°5232 N°6966 simply use {@see DBObject::DBClone()} instead, that will automatically create and persist a CMDBChange object.
|
||||
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
|
||||
* @deprecated 3.1.0 N°5232 not used
|
||||
*/
|
||||
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
|
||||
{
|
||||
@@ -447,9 +446,6 @@ abstract class CMDBObject extends DBObject
|
||||
$this->DBCloneTracked_Internal($newKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.1 3.2.0 N°6966 We will have only one DBClone method in the future
|
||||
*/
|
||||
protected function DBCloneTracked_Internal($newKey = null)
|
||||
{
|
||||
$newKey = parent::DBClone($newKey);
|
||||
@@ -473,14 +469,21 @@ abstract class CMDBObject extends DBObject
|
||||
*/
|
||||
public function DBDelete(&$oDeletionPlan = null)
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
$oDeletionPlan = parent::DBDelete($oDeletionPlan);
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
return $oDeletionPlan;
|
||||
return $this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.1 3.2.0 N°6967 We will have only one DBDelete method in the future
|
||||
* @param null $oDeletionPlan
|
||||
*
|
||||
* @return \DeletionPlan|null
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreCannotSaveObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DeleteException
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
@@ -42,12 +42,6 @@ class CMDBSource
|
||||
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
|
||||
const ENUM_DB_VENDOR_PERCONA = 'Percona';
|
||||
|
||||
/**
|
||||
* @since 2.7.10 3.0.4 3.1.2 3.0.2 N°6889 constant creation
|
||||
* @internal will be removed in a future version
|
||||
*/
|
||||
const MYSQL_DEFAULT_PORT = 3306;
|
||||
|
||||
/**
|
||||
* Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
|
||||
* Message: Lock wait timeout exceeded; try restarting transaction
|
||||
@@ -218,19 +212,16 @@ class CMDBSource
|
||||
/**
|
||||
* @param string $sDbHost initial value ("p:domain:port" syntax)
|
||||
* @param string $sServer server variable to update
|
||||
* @param int|null $iPort port variable to update, will return null if nothing is specified in $sDbHost
|
||||
*
|
||||
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°6889 will return null in $iPort if port isn't present in $sDbHost. Use {@see MYSQL_DEFAULT_PORT} if needed
|
||||
*
|
||||
* @link http://php.net/manual/en/mysqli.persistconns.php documentation for the "p:" prefix (persistent connexion)
|
||||
* @param int $iPort port variable to update
|
||||
*/
|
||||
public static function InitServerAndPort($sDbHost, &$sServer, &$iPort)
|
||||
{
|
||||
$aConnectInfo = explode(':', $sDbHost);
|
||||
|
||||
$bUsePersistentConnection = false;
|
||||
if (strcasecmp($aConnectInfo[0], 'p') === 0)
|
||||
if (strcasecmp($aConnectInfo[0], 'p') == 0)
|
||||
{
|
||||
// we might have "p:" prefix to use persistent connections (see http://php.net/manual/en/mysqli.persistconns.php)
|
||||
$bUsePersistentConnection = true;
|
||||
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
|
||||
}
|
||||
@@ -248,6 +239,10 @@ class CMDBSource
|
||||
{
|
||||
$iPort = (int)($aConnectInfo[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iPort = 3306;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,7 +380,7 @@ class CMDBSource
|
||||
public static function GetDBVendor()
|
||||
{
|
||||
$sDBVendor = static::ENUM_DB_VENDOR_MYSQL;
|
||||
|
||||
|
||||
$sVersionComment = static::GetServerVariable('version') . ' - ' . static::GetServerVariable('version_comment');
|
||||
if(preg_match('/mariadb/i', $sVersionComment) === 1)
|
||||
{
|
||||
@@ -395,7 +390,7 @@ class CMDBSource
|
||||
{
|
||||
$sDBVendor = static::ENUM_DB_VENDOR_PERCONA;
|
||||
}
|
||||
|
||||
|
||||
return $sDBVendor;
|
||||
}
|
||||
|
||||
@@ -436,7 +431,6 @@ class CMDBSource
|
||||
{
|
||||
self::$m_sDBName = '';
|
||||
}
|
||||
self::_TablesInfoCacheReset(); // reset the table info cache!
|
||||
}
|
||||
|
||||
public static function CreateTable($sQuery)
|
||||
@@ -549,9 +543,10 @@ class CMDBSource
|
||||
/**
|
||||
* @param string $sSQLQuery
|
||||
*
|
||||
* @return mysqli_result|null
|
||||
* @throws MySQLException
|
||||
* @throws MySQLHasGoneAwayException
|
||||
* @return \mysqli_result|null
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 2.7.0 N°679 handles nested transactions
|
||||
*/
|
||||
@@ -612,9 +607,8 @@ class CMDBSource
|
||||
{
|
||||
self::LogDeadLock($e, true);
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
} finally {
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
}
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
if ($oResult === false) {
|
||||
$aContext = array('query' => $sSql);
|
||||
|
||||
@@ -632,24 +626,18 @@ class CMDBSource
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Exception $e
|
||||
* @param \Exception $e
|
||||
* @param bool $bForQuery to get the proper DB connection
|
||||
* @param bool $bCheckMysqliErrno if false won't try to check for mysqli::errno value
|
||||
*
|
||||
* @since 2.7.1
|
||||
* @since 3.0.0 N°4325 add new optional parameter to use the correct DB connection
|
||||
* @since 3.0.4 3.1.1 3.2.0 N°6643 new bCheckMysqliErrno parameter as a workaround for mysqli::errno cannot be mocked
|
||||
*/
|
||||
private static function LogDeadLock(Exception $e, $bForQuery = false, $bCheckMysqliErrno = true)
|
||||
private static function LogDeadLock(Exception $e, $bForQuery = false)
|
||||
{
|
||||
// checks MySQL error code
|
||||
if ($bCheckMysqliErrno) {
|
||||
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
|
||||
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$iMySqlErrorNo = "N/A";
|
||||
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
|
||||
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get error info
|
||||
@@ -676,10 +664,7 @@ class CMDBSource
|
||||
);
|
||||
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
|
||||
|
||||
IssueLog::Error($sMessage, LogChannels::DEADLOCK, [
|
||||
'exception.class' => get_class($e),
|
||||
'exception.message' => $e->getMessage(),
|
||||
]);
|
||||
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -938,7 +923,7 @@ class CMDBSource
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
|
||||
while ($aRow = $oResult->fetch_array($iMode))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
@@ -1092,7 +1077,7 @@ class CMDBSource
|
||||
if (!array_key_exists($iKey, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$iKey];
|
||||
if (!array_key_exists("Key", $aFieldData)) return false;
|
||||
return ($aFieldData["Key"] == "PRI");
|
||||
return ($aFieldData["Key"] == "PRI");
|
||||
}
|
||||
|
||||
public static function IsAutoIncrement($sTable, $sField)
|
||||
@@ -1103,7 +1088,7 @@ class CMDBSource
|
||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
||||
if (!array_key_exists("Extra", $aFieldData)) return false;
|
||||
//MyHelpers::debug_breakpoint($aFieldData);
|
||||
return (strstr($aFieldData["Extra"], "auto_increment"));
|
||||
return (strstr($aFieldData["Extra"], "auto_increment"));
|
||||
}
|
||||
|
||||
public static function IsField($sTable, $sField)
|
||||
@@ -1169,8 +1154,8 @@ class CMDBSource
|
||||
*/
|
||||
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
|
||||
{
|
||||
[$sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||
[$sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||
|
||||
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
|
||||
{
|
||||
@@ -1370,13 +1355,13 @@ class CMDBSource
|
||||
public static function GetTableFieldsList($sTable)
|
||||
{
|
||||
assert(!empty($sTable));
|
||||
|
||||
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return array(); // #@# or an error ?
|
||||
|
||||
return array_keys($aTableInfo["Fields"]);
|
||||
}
|
||||
|
||||
|
||||
// Cache the information about existing tables, and their fields
|
||||
private static $m_aTablesInfo = array();
|
||||
private static function _TablesInfoCacheReset($sTableName = null)
|
||||
@@ -1509,7 +1494,7 @@ class CMDBSource
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
|
||||
$aRows = array();
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
|
||||
{
|
||||
@@ -1518,7 +1503,7 @@ class CMDBSource
|
||||
$oResult->free();
|
||||
return $aRows;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value of the specified server variable
|
||||
* @param string $sVarName Name of the server variable
|
||||
@@ -1534,7 +1519,7 @@ class CMDBSource
|
||||
/**
|
||||
* Returns the privileges of the current user
|
||||
* @return string privileges in a raw format
|
||||
*/
|
||||
*/
|
||||
public static function GetRawPrivileges()
|
||||
{
|
||||
try
|
||||
@@ -1560,8 +1545,8 @@ class CMDBSource
|
||||
|
||||
/**
|
||||
* Determine the slave status of the server
|
||||
* @return bool true if the server is slave
|
||||
*/
|
||||
* @return bool true if the server is slave
|
||||
*/
|
||||
public static function IsSlaveServer()
|
||||
{
|
||||
try
|
||||
@@ -1603,19 +1588,7 @@ class CMDBSource
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function GetClusterNb()
|
||||
{
|
||||
$result = 0;
|
||||
$sSql = "SHOW STATUS LIKE 'wsrep_cluster_size';";
|
||||
$aRows = self::QueryToArray($sSql);
|
||||
if (count($aRows) > 0)
|
||||
{
|
||||
$result = $aRows[0]['Value'];
|
||||
}
|
||||
return intval($result);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
|
||||
* @return string query to upgrade database charset and collation if needed, null if not
|
||||
* @throws \MySQLException
|
||||
|
||||
@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||
*
|
||||
* @see ITOP_CORE_VERSION to get iTop core version
|
||||
*/
|
||||
define('ITOP_VERSION', '3.1.1-dev');
|
||||
define('ITOP_VERSION', '3.1.0-dev');
|
||||
|
||||
define('ITOP_VERSION_NAME', 'Fullmoon');
|
||||
define('ITOP_REVISION', 'svn');
|
||||
@@ -137,7 +137,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'log_purge.max_keep_days' => [
|
||||
'log_purge.max_keep_days' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Optional purge number of days to keep logs.',
|
||||
'default' => 365,
|
||||
@@ -145,7 +145,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'event_service.debug.filter_events' => [
|
||||
'event_service.debug.filter_events' => [
|
||||
'type' => 'array',
|
||||
'description' => 'List of events name to filter Event Service debug messages',
|
||||
'default' => [],
|
||||
@@ -153,7 +153,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'event_service.debug.filter_sources' => [
|
||||
'event_service.debug.filter_sources' => [
|
||||
'type' => 'array',
|
||||
'description' => 'List of event sources to filter Event Service debug messages',
|
||||
'default' => '',
|
||||
@@ -161,38 +161,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'temporary_object.force_creation' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, all the objects created by the external key are temporary',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'temporary_object.lifetime' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Seconds for temporary objects created',
|
||||
'default' => 300,
|
||||
'value' => 300,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'temporary_object.watchdog_interval' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Seconds between watchdog signals',
|
||||
'default' => 60,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'temporary_object.garbage_interval' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Seconds between garbage collections',
|
||||
'default' => 60,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'app_env_label' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
|
||||
@@ -217,7 +185,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'db_host' => [
|
||||
'db_host' => [
|
||||
'type' => 'string',
|
||||
'default' => null,
|
||||
'value' => '',
|
||||
@@ -515,7 +483,7 @@ class Config
|
||||
'synchro_obsolete_replica_locks_object' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Obsolete synchro replicas prevent object modification by any mean (eg. anonymization)',
|
||||
'default' => true,
|
||||
'default' => 'true',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
@@ -656,22 +624,22 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'email_transport_smtp.allow_self_signed' => [
|
||||
'email_transport_smtp.allow_self_signed' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Allow self signed peer certificates',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'email_transport_smtp.verify_peer' => [
|
||||
),
|
||||
'email_transport_smtp.verify_peer' => array(
|
||||
'type' => 'bool',
|
||||
'description' => 'Verify peer certificate',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
),
|
||||
'email_css' => [
|
||||
'type' => 'string',
|
||||
'description' => 'CSS that will override the standard stylesheet used for the notifications',
|
||||
@@ -978,14 +946,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'lifecycle.transitions_sort_type' => [
|
||||
'type' => 'string',
|
||||
'description' => 'How transitions will be sorted in the GUI. Possible values are "xml", "alphabetical", "fixed" or "relative"',
|
||||
'default' => DBObject::DEFAULT_TRANSITIONS_SORT_TYPE,
|
||||
'value' => DBObject::DEFAULT_TRANSITIONS_SORT_TYPE,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'url_validation_pattern' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)',
|
||||
@@ -1069,14 +1029,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'log_kpi_generate_legacy_report' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Generate the legacy KPI report (kpi.html)',
|
||||
'default' => true,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'max_linkset_output' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',
|
||||
@@ -1193,30 +1145,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'sessions_tracking.enabled' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not the whole mechanism to track active sessions is enabled. See PHP session.gc_maxlifetime setting to configure session expiration.',
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'sessions_tracking.gc_threshold' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'fallback in case cron is not active: probability in percent that session files are cleanup during any itop request (100 means always)',
|
||||
'default' => 1,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'sessions_tracking.gc_duration_in_seconds' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'fallback in case cron is not active: when a cleanup is triggered cleanup duration will not exceed this duration (in seconds).',
|
||||
'default' => 1,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'transaction_storage' => [
|
||||
'type' => 'string',
|
||||
'description' => 'The type of mechanism to use for storing the unique identifiers for transactions (Session|File).',
|
||||
@@ -1595,6 +1523,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'ormcaselog_extension_classes' => [
|
||||
'type' => 'array',
|
||||
'description' => 'Sorted list of enabled iOrmCaseLogExtension implementation classes',
|
||||
'default' => [],
|
||||
'value' => [],
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'regenerate_session_id_enabled' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true then session id will be regenerated on each login, to prevent session fixation.',
|
||||
@@ -1643,14 +1579,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.enable_header_xcontent_type_options' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If set to false, iTop will stop sending the X-Content-Type-Options HTTP header. This header could trigger CORB protection on certain resources (JSON, XML, HTML, text) therefore blocking them.',
|
||||
'default' => true,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.disable_inline_documents_sandbox' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true then the sandbox for documents displayed in a browser tab will be disabled; enabling scripts and other interactive content. Note that setting this to true will open the application to potential XSS attacks!',
|
||||
@@ -1710,8 +1638,8 @@ class Config
|
||||
'audit.enable_selection_landing_page' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true audit categories must be selected before results are computed (use this setting in case of a lot of audit categories)',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
@@ -2321,24 +2249,6 @@ class Config
|
||||
$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.11 N°7085
|
||||
* Add login mode if not configured already
|
||||
* @param string $sLoginMode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddAllowedLoginTypes($sLoginMode)
|
||||
{
|
||||
$aAllowedLoginTypes = $this->GetAllowedLoginTypes();
|
||||
if (in_array($sLoginMode, $aAllowedLoginTypes)){
|
||||
return;
|
||||
}
|
||||
|
||||
$aAllowedLoginTypes[] = $sLoginMode;
|
||||
$this->SetAllowedLoginTypes($aAllowedLoginTypes);
|
||||
}
|
||||
|
||||
public function SetExternalAuthenticationVariable($sExtAuthVariable)
|
||||
{
|
||||
$this->m_sExtAuthVariable = $sExtAuthVariable;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
@@ -58,16 +58,6 @@ class ContextTag
|
||||
public const TAG_SETUP = 'Setup';
|
||||
public const TAG_SYNCHRO = 'Synchro';
|
||||
public const TAG_REST = 'REST/JSON';
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°6047
|
||||
*/
|
||||
public const TAG_IMPORT = 'Import';
|
||||
/**
|
||||
* @since 3.1.0 N°6047
|
||||
*/
|
||||
public const TAG_EXPORT = 'Export';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0 N°3200
|
||||
@@ -85,7 +75,7 @@ class ContextTag
|
||||
{
|
||||
static::$aStack[] = $sTag;
|
||||
}
|
||||
|
||||
|
||||
public static function AddContext($sTag)
|
||||
{
|
||||
static::$aStack[] = $sTag;
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is only here for compatibility reasons.
|
||||
* It will be removed in future iTop versions (N°6533)
|
||||
* This file is only here for compatibility issues. Will be removed in iTop 3.1.0 (N°3664)
|
||||
*
|
||||
* @deprecated 3.0.0 N°3663 Exception classes were moved to `/application/exceptions`, use autoloader instead of require !
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../approot.inc.php';
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions and can be used directly with the autoloader');
|
||||
require_once '../approot.inc.php';
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions');
|
||||
|
||||
@@ -188,8 +188,8 @@ final class ItopCounter
|
||||
|
||||
if (!$hDBLink)
|
||||
{
|
||||
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
|
||||
}
|
||||
throw new Exception("Could not connect to the DB server (host=$sDBHost, user=$sDBUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
}
|
||||
|
||||
return $hDBLink;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ use Combodo\iTop\Application\UI\Base\Component\Input\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;
|
||||
|
||||
/**
|
||||
* Bulk export: CSV export
|
||||
@@ -115,7 +114,6 @@ class CSVBulkExport extends TabularBulkExport
|
||||
|
||||
case 'csv_options':
|
||||
$oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:CSVOptions'));
|
||||
$oPanel->AddSubBlock(ExportHelper::GetAlertForExcelMaliciousInjection());
|
||||
|
||||
$oMulticolumn = MultiColumnUIBlockFactory::MakeStandard();
|
||||
$oPanel->AddSubBlock($oMulticolumn);
|
||||
@@ -139,7 +137,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.utils::EscapeHtml($sOtherSeparator).'"/>';
|
||||
|
||||
foreach ($aSep as $sVal => $sLabel) {
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", $sVal, $sLabel, "radio");
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", utils::EscapeHtml($sVal), $sLabel, "radio");
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
|
||||
$oRadio->SetBeforeInput(false);
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
|
||||
@@ -165,8 +163,8 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.utils::EscapeHtml($sOtherQualifier).'"/>';
|
||||
|
||||
foreach ($aQualifiers as $sVal => $sLabel) {
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", $sVal, $sLabel, "radio");
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawQualifier));
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", utils::EscapeHtml($sVal), $sLabel, "radio");
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
|
||||
$oRadio->SetBeforeInput(false);
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
|
||||
@@ -23,22 +23,10 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
abstract class CustomFieldsHandler {
|
||||
/** @var string $sAttCode */
|
||||
abstract class CustomFieldsHandler
|
||||
{
|
||||
protected $sAttCode;
|
||||
/** @var array{
|
||||
* legacy: int,
|
||||
* extradata_id: string,
|
||||
* _template_name: string,
|
||||
* template_id: string,
|
||||
* template_data: string,
|
||||
* user_data: array<string, mixed>,
|
||||
* current_template_id: string,
|
||||
* current_template_data: string,
|
||||
* } $aValues same as {@see \ormCustomFieldsValue::$aCurrentValues}
|
||||
*/
|
||||
protected $aValues;
|
||||
/** @var \Combodo\iTop\Form\Form $oForm */
|
||||
protected $oForm;
|
||||
|
||||
/**
|
||||
@@ -47,47 +35,20 @@ abstract class CustomFieldsHandler {
|
||||
*
|
||||
* @param $sAttCode
|
||||
*/
|
||||
final public function __construct($sAttCode) {
|
||||
final public function __construct($sAttCode)
|
||||
{
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->aValues = null;
|
||||
}
|
||||
|
||||
abstract public function BuildForm(DBObject $oHostObject, $sFormId);
|
||||
|
||||
/**
|
||||
* @returns true|string true if no error found, error message otherwise
|
||||
* @throws \ApplicationException if {@link static::$oForm} attribute not initialized yet
|
||||
* @since 3.1.0 N°6322 N°1150 Add template_id checks
|
||||
*/
|
||||
public function Validate(DBObject $oHostObject) {
|
||||
if (false === isset($this->oForm)) {
|
||||
throw new ApplicationException('oForm attribute not init yet. You must call BuildForm before this method !');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->oForm->Validate();
|
||||
if ($this->oForm->GetValid()) {
|
||||
$ret = true;
|
||||
}
|
||||
else {
|
||||
$aMessages = array();
|
||||
foreach ($this->oForm->GetErrorMessages() as $sFieldId => $aFieldMessages) {
|
||||
$aMessages[] = $sFieldId.': '.implode(', ', $aFieldMessages);
|
||||
}
|
||||
$ret = 'Invalid value: '.implode(', ', $aMessages);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$ret = $e->getMessage();
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return \Combodo\iTop\Form\Form
|
||||
*/
|
||||
public function GetForm() {
|
||||
public function GetForm()
|
||||
{
|
||||
return $this->oForm;
|
||||
}
|
||||
|
||||
@@ -96,14 +57,16 @@ abstract class CustomFieldsHandler {
|
||||
$this->aValues = $aValues;
|
||||
}
|
||||
|
||||
public static function GetPrerequisiteAttributes($sClass = null) {
|
||||
static public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* List the available verbs for 'GetForTemplate'
|
||||
*/
|
||||
public static function EnumTemplateVerbs() {
|
||||
static public function EnumTemplateVerbs()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
@@ -155,21 +118,6 @@ abstract class CustomFieldsHandler {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \stdClass|null $json
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @return \ormCustomFieldsValue|null
|
||||
*
|
||||
* @since 3.1.0 N°1150 Method creation
|
||||
*/
|
||||
public function FromJSONToValue(?stdClass $json, string $sAttCode): ?ormCustomFieldsValue
|
||||
{
|
||||
// Default impl doing nothing, to avoid errors on children not having this method
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param DBObject $oHostObject
|
||||
*
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>addon/userrights,grant_by_profile</category>
|
||||
<is_link>1</is_link>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="userid" xsi:type="AttributeExternalKey">
|
||||
@@ -219,19 +218,6 @@
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="AuditDomain" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>application, grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="icon" xsi:type="AttributeImage"/>
|
||||
<field id="categories_list" xsi:type="AttributeLinkedSet"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="Query" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
@@ -496,24 +482,6 @@
|
||||
<type>boolean</type>
|
||||
<default>true</default>
|
||||
</property>
|
||||
<property id="with_php_constraint">
|
||||
<php_param>with_php_constraint</php_param>
|
||||
<mandatory>false</mandatory>
|
||||
<type>boolean</type>
|
||||
<default>false</default>
|
||||
</property>
|
||||
<property id="with_php_computation">
|
||||
<php_param>with_php_computation</php_param>
|
||||
<mandatory>false</mandatory>
|
||||
<type>boolean</type>
|
||||
<default>false</default>
|
||||
</property>
|
||||
<property id="create_temporary_object">
|
||||
<php_param>create_temporary_object</php_param>
|
||||
<mandatory>false</mandatory>
|
||||
<type>boolean</type>
|
||||
<default>false</default>
|
||||
</property>
|
||||
<property id="on_target_delete">
|
||||
<php_param>on_target_delete</php_param>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -400,7 +400,7 @@ class DBObjectSearch extends DBSearch
|
||||
}
|
||||
|
||||
/**
|
||||
* Important: If you need to add a condition on the same $sFilterCode several times with different $value values; do not use this method as the previous $value occurrences will be replaced by the last. Instead use:
|
||||
* Important: If you need to add a condition on the same $sFilterCode several times with different $value values; do not use this method as the previous $value occurences will be replaced by the last. Instead use:
|
||||
* * {@see \DBObjectSearch::AddConditionExpression()} in loops to add conditions one by one
|
||||
* * {@see \DBObjectSearch::AddConditionForInOperatorUsingParam()} for IN/NOT IN queries with lots of params at once
|
||||
*
|
||||
|
||||
@@ -767,10 +767,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
|
||||
try
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->m_oSQLResult = CMDBSource::Query($sSQL);
|
||||
$sOQL = $this->GetPseudoOQL($this->m_oFilter, $this->GetRealSortOrder(), $this->m_iLimitCount, $this->m_iLimitStart, false);
|
||||
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||
} catch (MySQLException $e)
|
||||
{
|
||||
// 1116 = ER_TOO_MANY_TABLES
|
||||
@@ -850,11 +847,8 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), 0, 0, true);
|
||||
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||
if (!$resQuery) return 0;
|
||||
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
@@ -865,42 +859,6 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBSearch $oFilter
|
||||
* @param array $aOrder
|
||||
* @param int $iLimitCount
|
||||
* @param int $iLimitStart
|
||||
* @param bool $bCount
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function GetPseudoOQL($oFilter, $aOrder, $iLimitCount, $iLimitStart, $bCount)
|
||||
{
|
||||
$sOQL = '';
|
||||
if ($bCount) {
|
||||
$sOQL .= 'COUNT ';
|
||||
}
|
||||
$sOQL .= $oFilter->ToOQL();
|
||||
|
||||
if ($iLimitCount > 0) {
|
||||
$sOQL .= ' LIMIT ';
|
||||
if ($iLimitStart > 0) {
|
||||
$sOQL .= "$iLimitStart, ";
|
||||
}
|
||||
$sOQL .= "$iLimitCount";
|
||||
}
|
||||
|
||||
if (count($aOrder) > 0) {
|
||||
$sOQL .= ' ORDER BY ';
|
||||
$aOrderBy = [];
|
||||
foreach ($aOrder as $sAttCode => $bAsc) {
|
||||
$aOrderBy[] = $sAttCode.' '.($bAsc ? 'ASC' : 'DESC');
|
||||
}
|
||||
$sOQL .= implode(', ', $aOrderBy);
|
||||
}
|
||||
return $sOQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the count exceeds a given limit
|
||||
*
|
||||
@@ -917,11 +875,8 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
|
||||
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||
if ($resQuery)
|
||||
{
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
@@ -932,7 +887,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
@@ -957,11 +912,8 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
|
||||
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||
if ($resQuery)
|
||||
{
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
@@ -972,7 +924,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
|
||||
@@ -851,11 +851,11 @@ abstract class DBSearch
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($aColumns) == 0)
|
||||
{
|
||||
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
|
||||
// Add the standard id (as first column)
|
||||
array_unshift($aColumns, 'id');
|
||||
if (count($aColumns) == 0)
|
||||
{
|
||||
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
|
||||
// Add the standard id (as first column)
|
||||
array_unshift($aColumns, 'id');
|
||||
}
|
||||
|
||||
$aQueryCols = CMDBSource::GetColumns($resQuery, $sSQL);
|
||||
@@ -885,55 +885,6 @@ abstract class DBSearch
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a column ($sAttCode) from the specified class ($sClassAlias - default main class) of the DBsearch object and gives the result as an array
|
||||
* @param string $sAttCode
|
||||
* @param string|null $sClassAlias
|
||||
*
|
||||
* @return array
|
||||
* @throws ConfigException
|
||||
* @throws CoreException
|
||||
* @throws MissingQueryArgument
|
||||
* @throws MySQLException
|
||||
* @throws MySQLHasGoneAwayException
|
||||
*/
|
||||
public function SelectAttributeToArray(string $sAttCode, ?string $sClassAlias = null):array
|
||||
{
|
||||
if(is_null($sClassAlias)) {
|
||||
$sClassAlias = $this->GetClassAlias();
|
||||
}
|
||||
|
||||
$sClass = $this->GetClass();
|
||||
if($sAttCode === 'id'){
|
||||
$aAttToLoad[$sClassAlias]=[];
|
||||
} else {
|
||||
$aAttToLoad[$sClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
}
|
||||
|
||||
$sSQL = $this->MakeSelectQuery([], [], $aAttToLoad);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$sColName = $sClassAlias.$sAttCode;
|
||||
|
||||
$aRes = [];
|
||||
while ($aRow = CMDBSource::FetchArray($resQuery))
|
||||
{
|
||||
$aMappedRow = array();
|
||||
if($sAttCode === 'id') {
|
||||
$aMappedRow[$sAttCode] = $aRow[$sColName];
|
||||
} else {
|
||||
$aMappedRow[$sAttCode] = $aAttToLoad[$sClassAlias][$sAttCode]->FromSQLToValue($aRow, $sColName);
|
||||
}
|
||||
$aRes[] = $aMappedRow;
|
||||
}
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Construction of the SQL queries
|
||||
|
||||
@@ -224,10 +224,6 @@ class DBUnionSearch extends DBSearch
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the selected classes for this query.
|
||||
* The selected classes can be either in the selected classes of all the queries,
|
||||
* or they should exist in all the sub-queries of the union.
|
||||
*
|
||||
* @param array $aSelectedClasses array of aliases
|
||||
*
|
||||
* @throws \Exception
|
||||
@@ -240,31 +236,22 @@ class DBUnionSearch extends DBSearch
|
||||
$aAliasesToColumn = array_flip(array_keys($oFirstSearch->GetSelectedClasses()));
|
||||
foreach ($aSelectedClasses as $sSelectedAlias) {
|
||||
if (!isset($aAliasesToColumn[$sSelectedAlias])) {
|
||||
// The selected class is not in the selected classes of the union,
|
||||
// try to delegate the feature to the sub-queries
|
||||
$aSelectedColumns = [];
|
||||
break;
|
||||
throw new CoreException("SetSelectedClasses: Invalid class alias $sSelectedAlias");
|
||||
}
|
||||
$aSelectedColumns[] = $aAliasesToColumn[$sSelectedAlias];
|
||||
}
|
||||
|
||||
// 1 - change for each search
|
||||
foreach ($this->aSearches as $iPos => $oSearch) {
|
||||
foreach ($this->aSearches as $iPos => $oSearch)
|
||||
{
|
||||
$aCurrentSelectedAliases = [];
|
||||
if (count($aSelectedColumns) === 0) {
|
||||
// Default to the list of aliases given
|
||||
$aCurrentSelectedAliases = $aSelectedClasses;
|
||||
} else {
|
||||
// Map the aliases for each query
|
||||
foreach ($aSelectedColumns as $iColumn) {
|
||||
$aCurrentSelectedAliases[] = $this->aColumnToAliases[$iColumn][$iPos];
|
||||
}
|
||||
foreach ($aSelectedColumns as $iColumn) {
|
||||
$aCurrentSelectedAliases[] = $this->aColumnToAliases[$iColumn][$iPos];
|
||||
}
|
||||
|
||||
// Throws an exception if not valid
|
||||
$oSearch->SetSelectedClasses($aCurrentSelectedAliases);
|
||||
}
|
||||
|
||||
// 2 - update the lowest common ancestors
|
||||
$this->ComputeSelectedClasses();
|
||||
}
|
||||
|
||||
@@ -28,13 +28,8 @@ namespace Combodo\iTop;
|
||||
|
||||
use DOMDocument;
|
||||
use DOMFormatException;
|
||||
use DOMNode;
|
||||
use DOMNodeList;
|
||||
use DOMXPath;
|
||||
use Exception;
|
||||
use IssueLog;
|
||||
use LogAPI;
|
||||
use MFElement;
|
||||
use utils;
|
||||
|
||||
/**
|
||||
@@ -46,11 +41,6 @@ use utils;
|
||||
*/
|
||||
class DesignDocument extends DOMDocument
|
||||
{
|
||||
|
||||
/** To fix DOMNode::getLineNo() ref https://www.php.net/manual/en/domnode.getlineno.php */
|
||||
public const XML_PARSE_BIG_LINES = 4194304;
|
||||
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
@@ -79,12 +69,10 @@ class DesignDocument extends DOMDocument
|
||||
*/
|
||||
public function load($filename, $options = null)
|
||||
{
|
||||
if (is_file($filename)) {
|
||||
libxml_clear_errors();
|
||||
if (parent::load($filename, LIBXML_NOBLANKS | LIBXML_BIGLINES | LIBXML_PARSEHUGE | self::XML_PARSE_BIG_LINES) === false) {
|
||||
$aErrors = libxml_get_errors();
|
||||
IssueLog::Error("Error loading $filename", LogAPI::CHANNEL_DEFAULT, $aErrors);
|
||||
}
|
||||
libxml_clear_errors();
|
||||
if (parent::load($filename, LIBXML_NOBLANKS) === false) {
|
||||
$aErrors = libxml_get_errors();
|
||||
IssueLog::Error("Error loading $filename", LogAPI::CHANNEL_DEFAULT, $aErrors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,56 +227,6 @@ class DesignElement extends \DOMElement
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility with PHP8.0
|
||||
*
|
||||
* @return \DOMElement|null
|
||||
*
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public function GetFirstElementChild()
|
||||
{
|
||||
if (property_exists($this, 'firstElementChild')) {
|
||||
return $this->firstElementChild;
|
||||
}
|
||||
|
||||
$oChildNode = $this->firstChild;
|
||||
while (!is_null($oChildNode)) {
|
||||
if ($oChildNode instanceof \DOMElement) {
|
||||
return $oChildNode;
|
||||
}
|
||||
$oChildNode = $oChildNode->nextSibling;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility with PHP8.0
|
||||
*
|
||||
* @return \DOMElement|null
|
||||
*
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public function GetNextElementSibling()
|
||||
{
|
||||
if (property_exists($this, 'nextElementSibling')) {
|
||||
return $this->nextElementSibling;
|
||||
}
|
||||
|
||||
$oSibling = $this->nextSibling;
|
||||
while (!is_null($oSibling)) {
|
||||
if ($oSibling instanceof \DOMElement) {
|
||||
return $oSibling;
|
||||
}
|
||||
$oSibling = $oSibling->nextSibling;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node directly under the given node
|
||||
* @param $sTagName
|
||||
@@ -369,146 +307,4 @@ class DesignElement extends \DOMElement
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the current node is actually a class node, under classes
|
||||
* @since 3.1.2 3.2.0 N°6974
|
||||
*/
|
||||
public function IsClassNode(): bool
|
||||
{
|
||||
if ($this->tagName == 'class') {
|
||||
// Beware: classes/class also exists in the group definition
|
||||
if (($this->parentNode->tagName == 'classes') && ($this->parentNode->parentNode->tagName == 'itop_design')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the node is contained in a _delta="merge" tree
|
||||
* @return bool
|
||||
*/
|
||||
public function IsInSpecifiedMerge(): bool
|
||||
{
|
||||
// Iterate through the parents: reset the flag if any of them has a flag set
|
||||
for ($oParent = $this; $oParent instanceof MFElement; $oParent = $oParent->parentNode) {
|
||||
$sDeltaSpec = $oParent->getAttribute('_delta');
|
||||
if ($sDeltaSpec === 'merge') {
|
||||
return true;
|
||||
}
|
||||
if (in_array($sDeltaSpec, ['define', 'define_if_not_exists', 'force', 'redefine'])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the child node matching the given node.
|
||||
* UNSAFE: may return nodes marked as _alteration="removed"
|
||||
* A method with the same signature MUST exist in MFDocument for the recursion to work fine
|
||||
*
|
||||
* @param DesignElement $oRefNode The node to search for
|
||||
* @param null|string $sSearchId substitutes to the value of the 'id' attribute
|
||||
*
|
||||
* @return DesignElement|null
|
||||
* @throws \Exception
|
||||
* @since 3.1.2 3.2.0 N°6974
|
||||
*/
|
||||
public function _FindChildNode(DesignElement $oRefNode, $sSearchId = null): ?DesignElement
|
||||
{
|
||||
return self::_FindNode($this, $oRefNode, $sSearchId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the child node matching the given node.
|
||||
* UNSAFE: may return nodes marked as _alteration="removed"
|
||||
* A method with the same signature MUST exist in MFDocument for the recursion to work fine
|
||||
*
|
||||
* @param DesignElement $oRefNode The node to search for
|
||||
*
|
||||
* @return DesignElement|null
|
||||
* @throws \Exception
|
||||
* @since 3.1.2 3.2.0 N°6974
|
||||
*/
|
||||
public function _FindChildNodes(DesignElement $oRefNode): ?DesignElement
|
||||
{
|
||||
return self::_FindNodes($this, $oRefNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the child node matching the given node under the specified parent.
|
||||
* UNSAFE: may return nodes marked as _alteration="removed"
|
||||
*
|
||||
* @param \DOMNode $oParent
|
||||
* @param DesignElement $oRefNode
|
||||
* @param string|null $sSearchId
|
||||
*
|
||||
* @return DesignElement|null
|
||||
* @throws Exception
|
||||
* @since 3.1.2 3.2.0 N°6974
|
||||
*/
|
||||
public static function _FindNode(DOMNode $oParent, DesignElement $oRefNode, string $sSearchId = null): ?DesignElement
|
||||
{
|
||||
$oNodes = self::_FindNodes($oParent, $oRefNode, $sSearchId);
|
||||
if ($oNodes instanceof DOMNodeList) {
|
||||
/** @var DesignElement $oNode */
|
||||
$oNode = $oNodes->item(0);
|
||||
|
||||
return $oNode;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the child node matching the given node under the specified parent.
|
||||
* UNSAFE: may return nodes marked as _alteration="removed"
|
||||
*
|
||||
* @param \DOMNode $oParent
|
||||
* @param DesignElement $oRefNode
|
||||
* @param string|null $sSearchId
|
||||
*
|
||||
* @return \DOMNodeList|false|mixed
|
||||
* @since 3.1.2 3.2.0 N°6974
|
||||
*/
|
||||
public static function _FindNodes(DOMNode $oParent, DesignElement $oRefNode, string $sSearchId = null)
|
||||
{
|
||||
if ($oParent instanceof DOMDocument)
|
||||
{
|
||||
$oDoc = $oParent->firstChild->ownerDocument;
|
||||
$oRoot = $oParent;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oDoc = $oParent->ownerDocument;
|
||||
$oRoot = $oParent;
|
||||
}
|
||||
|
||||
$oXPath = new DOMXPath($oDoc);
|
||||
if ($oRefNode->hasAttribute('id'))
|
||||
{
|
||||
// Find the elements having the same tag name and id
|
||||
if (!$sSearchId)
|
||||
{
|
||||
$sSearchId = $oRefNode->getAttribute('id');
|
||||
}
|
||||
$sXPath = './'.$oRefNode->tagName."[@id='$sSearchId']";
|
||||
|
||||
$oRes = $oXPath->query($sXPath, $oRoot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the elements having the same tag name
|
||||
$sXPath = './'.$oRefNode->tagName;
|
||||
|
||||
$oRes = $oXPath->query($sXPath, $oRoot);
|
||||
}
|
||||
|
||||
return $oRes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
@@ -56,11 +56,10 @@ class Dict
|
||||
* @param $sLanguageCode
|
||||
*
|
||||
* @throws \DictExceptionUnknownLanguage
|
||||
* @since 3.0.4 3.1.1 3.2.0 Param $sLanguageCode becomes nullable
|
||||
*/
|
||||
public static function SetUserLanguage($sLanguageCode = null)
|
||||
public static function SetUserLanguage($sLanguageCode)
|
||||
{
|
||||
if (!is_null($sLanguageCode) && !array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
{
|
||||
throw new DictExceptionUnknownLanguage($sLanguageCode);
|
||||
}
|
||||
@@ -107,59 +106,42 @@ class Dict
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localised string from the dictionary
|
||||
* Returns a localised string from the dictonary
|
||||
*
|
||||
* @param string $sStringCode The code identifying the dictionary entry
|
||||
* @param string $sDefault Default value if there is no match in the dictionary, if no default provided, returns $sStringCode unchanged
|
||||
* @param string $sDefault Default value if there is no match in the dictionary
|
||||
* @param bool $bUserLanguageOnly False to allow the use of the default language as a fallback, true otherwise
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
{
|
||||
$aInfo = self::GetLabelAndLangCode($sStringCode, $sDefault, $bUserLanguageOnly);
|
||||
return $aInfo['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localised string from the dictonary with its associated lang code
|
||||
*
|
||||
* @param string $sStringCode The code identifying the dictionary entry
|
||||
* @param string $sDefault Default value if there is no match in the dictionary
|
||||
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
|
||||
*
|
||||
* @return array{
|
||||
* lang: string, label: string
|
||||
* } with localized label string and used lang code
|
||||
*/
|
||||
private static function GetLabelAndLangCode($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
{
|
||||
// Attempt to find the string in the user language
|
||||
//
|
||||
$sLangCode = self::GetUserLanguage();
|
||||
self::InitLangIfNeeded($sLangCode);
|
||||
|
||||
if (! array_key_exists($sLangCode, self::$m_aData))
|
||||
if (!array_key_exists($sLangCode, self::$m_aData))
|
||||
{
|
||||
IssueLog::Warning("Cannot find $sLangCode in all registered dictionaries.");
|
||||
IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed");
|
||||
// It may happen, when something happens before the dictionaries get loaded
|
||||
return [ 'label' => $sStringCode, 'lang' => $sLangCode ];
|
||||
return $sStringCode;
|
||||
}
|
||||
$aCurrentDictionary = self::$m_aData[$sLangCode];
|
||||
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
|
||||
{
|
||||
return [ 'label' => $aCurrentDictionary[$sStringCode], 'lang' => $sLangCode ];
|
||||
return $aCurrentDictionary[$sStringCode];
|
||||
}
|
||||
if (!$bUserLanguageOnly)
|
||||
{
|
||||
// Attempt to find the string in the default language
|
||||
//
|
||||
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
||||
|
||||
|
||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => self::$m_sDefaultLanguage ];
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
// Attempt to find the string in english
|
||||
//
|
||||
@@ -168,17 +150,17 @@ class Dict
|
||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => 'EN US' ];
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
}
|
||||
// Could not find the string...
|
||||
//
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
return [ 'label' => $sStringCode, 'lang' => null ];
|
||||
return $sStringCode;
|
||||
}
|
||||
|
||||
return [ 'label' => $sDefault, 'lang' => null ];
|
||||
return $sDefault;
|
||||
}
|
||||
|
||||
|
||||
@@ -194,25 +176,19 @@ class Dict
|
||||
*/
|
||||
public static function Format($sFormatCode /*, ... arguments ... */)
|
||||
{
|
||||
['label' => $sLocalizedFormat, 'lang' => $sLangCode] = self::GetLabelAndLangCode($sFormatCode);
|
||||
|
||||
$sLocalizedFormat = self::S($sFormatCode);
|
||||
$aArguments = func_get_args();
|
||||
array_shift($aArguments);
|
||||
|
||||
|
||||
if ($sLocalizedFormat == $sFormatCode)
|
||||
{
|
||||
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
}
|
||||
|
||||
try{
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
} catch(\Throwable $e){
|
||||
\IssueLog::Error("Cannot format dict key", null, ["sFormatCode" => $sFormatCode, "sLangCode" => $sLangCode, 'exception_msg' => $e->getMessage() ]);
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
}
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a the entries for a given language (replaces the former Add() method)
|
||||
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
|
||||
@@ -222,7 +198,7 @@ class Dict
|
||||
{
|
||||
self::$m_aData[$sLanguageCode] = $aEntries;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the list of available languages
|
||||
* @param hash $aLanguagesList
|
||||
@@ -283,7 +259,7 @@ class Dict
|
||||
{
|
||||
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
|
||||
require_once($sDictFile);
|
||||
|
||||
|
||||
if (self::GetApcService()->function_exists('apc_store')
|
||||
&& (self::$m_sApplicationPrefix !== null))
|
||||
{
|
||||
@@ -293,7 +269,7 @@ class Dict
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable caching (cached using APC)
|
||||
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
|
||||
@@ -336,14 +312,14 @@ class Dict
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
|
||||
{
|
||||
$aMissing = array(); // Strings missing for the target language
|
||||
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
|
||||
$aNotTranslated = array(); // Strings having the same value in both dictionaries
|
||||
$aOK = array(); // Strings having different values in both dictionaries
|
||||
|
||||
|
||||
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
|
||||
{
|
||||
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
|
||||
@@ -351,7 +327,7 @@ class Dict
|
||||
$aMissing[$sStringCode] = $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
|
||||
{
|
||||
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
|
||||
@@ -374,7 +350,7 @@ class Dict
|
||||
}
|
||||
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
|
||||
}
|
||||
|
||||
|
||||
public static function Dump()
|
||||
{
|
||||
MyHelpers::var_dump_html(self::$m_aData);
|
||||
@@ -397,7 +373,7 @@ class Dict
|
||||
// No need to actually load the strings since it's only used to know the list of languages
|
||||
// at setup time !!
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Export all the dictionary entries - of the given language - whose code matches the given prefix
|
||||
* missing entries in the current language will be replaced by entries in the default language
|
||||
@@ -410,7 +386,7 @@ class Dict
|
||||
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
||||
$aEntries = array();
|
||||
$iLength = strlen($sStartingWith);
|
||||
|
||||
|
||||
// First prefill the array with entries from the default language
|
||||
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
|
||||
{
|
||||
@@ -419,7 +395,7 @@ class Dict
|
||||
$aEntries[$sCode] = $sEntry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now put (overwrite) the entries for the user language
|
||||
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
|
||||
{
|
||||
|
||||
@@ -16,11 +16,8 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
use Combodo\iTop\Application\Helper\WebResourcesHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\Panel;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
|
||||
/**
|
||||
@@ -62,7 +59,7 @@ class DisplayableNode extends GraphNode
|
||||
|
||||
public function GetWidth()
|
||||
{
|
||||
return max(32, 5 * mb_strlen($this->GetProperty('label'))); // approximation of the text's bounding box
|
||||
return max(32, 5*strlen($this->GetProperty('label'))); // approximation of the text's bounding box
|
||||
}
|
||||
|
||||
public function GetHeight()
|
||||
@@ -491,7 +488,7 @@ class DisplayableNode extends GraphNode
|
||||
if ($bNoLabel)
|
||||
{
|
||||
// simulate a fake label with the approximate same size as the true label
|
||||
$sLabel = str_repeat('x', mb_strlen($this->GetProperty('label', $this->GetId())));
|
||||
$sLabel = str_repeat('x',strlen($this->GetProperty('label', $this->GetId())));
|
||||
$sDot = 'label="'.$sLabel.'"';
|
||||
}
|
||||
else
|
||||
@@ -1415,8 +1412,6 @@ class DisplayableGraph extends SimpleGraph
|
||||
/**
|
||||
* Display the graph inside the given page, with the "filter" drawer above it
|
||||
*
|
||||
* @deprecated 3.1.1 3.2.0 N°3767 Use \DisplayableGraph::DisplayFilterBox() and \DisplayableGraph::DisplayGraph() instead
|
||||
*
|
||||
* @param WebPage $oP
|
||||
* @param array $aResults
|
||||
* @param string $sRelation
|
||||
@@ -1430,35 +1425,10 @@ class DisplayableGraph extends SimpleGraph
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*
|
||||
*/
|
||||
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array(), bool $bLazyLoading = false)
|
||||
{
|
||||
$oP->AddSubBlock($this->DisplayFilterBox($oP, $aResults, $bLazyLoading));
|
||||
$this->DisplayGraph($oP, $sRelation, $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams, $bLazyLoading);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display only the graph inside the given page, with the parameters of filter box draw with DisplayFilterBox
|
||||
*
|
||||
* @param WebPage $oP
|
||||
* @param string $sRelation
|
||||
* @param ApplicationContext $oAppContext
|
||||
* @param array $aExcludedObjects
|
||||
* @param string $sObjClass
|
||||
* @param int $iObjKey
|
||||
* @param string $sContextKey
|
||||
* @param array $aContextParams
|
||||
* @param bool $bLazyLoading
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*
|
||||
* @since 3.1.1 3.2.0 N°3767
|
||||
*/
|
||||
function DisplayGraph(WebPage $oP, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array(), bool $bLazyLoading = false): void
|
||||
{
|
||||
list($aExcludedByClass, $aAdditionalContexts) = $this->GetFilteringData($sContextKey, $aContextParams, $aExcludedObjects);
|
||||
list($aExcludedByClass, $aAdditionalContexts) = $this->DisplayFiltering($sContextKey, $aContextParams, $aExcludedObjects, $oP, $aResults, $bLazyLoading);
|
||||
|
||||
$iGroupingThreshold = utils::ReadParam('g', 5);
|
||||
|
||||
@@ -1543,10 +1513,12 @@ class DisplayableGraph extends SimpleGraph
|
||||
$oP->add_ready_script(" $('#$sId').simple_graph(".json_encode($aParams).");");
|
||||
} else {
|
||||
$oP->add_script("function Load(){var aExcluded = []; $('input[name^=excluded]').each( function() {if (!$(this).prop('checked')) { aExcluded.push($(this).val()); }} ); var params= $.extend(".json_encode($aParams).", {excluded_classes: aExcluded}); $('#$sId').simple_graph(params);}");
|
||||
$oP->add_ready_script("$('#graph').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_objects_lists').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_groups').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');");
|
||||
$oP->add_ready_script("$('#impacted_objects_lists').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_groups').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');");
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
catch(Exception $e)
|
||||
{
|
||||
$oP->add('<div>'.$e->getMessage().'</div>');
|
||||
}
|
||||
$oP->add_script(
|
||||
@@ -1591,41 +1563,23 @@ EOF
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*
|
||||
* @deprecated 3.1.1 3.2.0 N°3767 Use \DisplayableGraph::DisplayFilterBox() and \DisplayableGraph::GetFilteringData() instead
|
||||
*/
|
||||
public function DisplayFiltering(string $sContextKey, array $aContextParams, array $aExcludedObjects, WebPage $oP, array $aResults, bool $bLazyLoading = false): array
|
||||
{
|
||||
$oP->Add($this->DisplayFilterBox($oP, $aResults, $bLazyLoading));
|
||||
|
||||
return $this->GetFilteringData($sContextKey, $aContextParams, $aExcludedObjects);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oP
|
||||
* @param array $aResults
|
||||
* @param bool $bLazyLoading
|
||||
*
|
||||
* @return UIContentBlock
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \ReflectionException
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*
|
||||
* @since 3.1.1 3.2.0 N°3767
|
||||
*/
|
||||
public function DisplayFilterBox(WebPage $oP, array $aResults, bool $bLazyLoading = false): UIContentBlock
|
||||
{
|
||||
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
|
||||
$aExcludedByClass = array();
|
||||
foreach ($aExcludedObjects as $oObj) {
|
||||
if (!array_key_exists(get_class($oObj), $aExcludedByClass)) {
|
||||
$aExcludedByClass[get_class($oObj)] = array();
|
||||
}
|
||||
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
|
||||
}
|
||||
$sSftShort = Dict::S('UI:ElementsDisplayed');
|
||||
$oBlock = UIContentBlockUIBlockFactory::MakeStandard(null, ['not-printable']);
|
||||
|
||||
$oP->add("<div class=\"not-printable\">\n");
|
||||
$oUiSearchBlock = new Panel($sSftShort, [], Panel::ENUM_COLOR_SCHEME_CYAN, 'dh_flash');
|
||||
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"])
|
||||
->SetIsCollapsible(true);
|
||||
|
||||
$oUiHtmlBlock = new Html(
|
||||
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
|
||||
$oUiSearchBlock->SetIsCollapsible(true);
|
||||
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
|
||||
<<<EOF
|
||||
|
||||
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
|
||||
@@ -1672,23 +1626,11 @@ EOF
|
||||
$oUiHtmlBlock->AddHtml("<button type=\"button\" id=\"ReloadMovieBtn\" class=\"ibo-button ibo-is-neutral ibo-is-regular\" onClick=\"$sOnCLick\">".Dict::S('UI:Button:Refresh')."</button></div></form>");
|
||||
}
|
||||
$oUiHtmlBlock->AddHtml("</div>\n");
|
||||
$oUiHtmlBlock->AddHtml("</div>\n"); // class="not-printable"
|
||||
|
||||
$oUiSearchBlock->AddSubBlock($oUiHtmlBlock);
|
||||
$oBlock->AddSubBlock($oUiSearchBlock);
|
||||
$oP->AddUiBlock($oUiSearchBlock);
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
|
||||
public function GetFilteringData(string $sContextKey, array $aContextParams, array $aExcludedObjects): array
|
||||
{
|
||||
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
|
||||
$aExcludedByClass = array();
|
||||
foreach ($aExcludedObjects as $oObj) {
|
||||
if (!array_key_exists(get_class($oObj), $aExcludedByClass)) {
|
||||
$aExcludedByClass[get_class($oObj)] = array();
|
||||
}
|
||||
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
|
||||
}
|
||||
$aAdditionalContexts = array();
|
||||
foreach ($aContextDefs as $sKey => $aDefinition) {
|
||||
$aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes')));
|
||||
|
||||
@@ -242,33 +242,44 @@ class EventIssue extends Event
|
||||
{
|
||||
if (is_string($sValue))
|
||||
{
|
||||
if (mb_strlen($sValue) < 256) {
|
||||
if (strlen($sValue) < 256)
|
||||
{
|
||||
$aPost[$sKey] = $sValue;
|
||||
} else {
|
||||
$aPost[$sKey] = "!long string: ".mb_strlen($sValue)." chars";
|
||||
}
|
||||
} else {
|
||||
else
|
||||
{
|
||||
$aPost[$sKey] = "!long string: ".strlen($sValue). " chars";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a string (avoid warnings in case the value cannot be easily casted into a string)
|
||||
$aPost[$sKey] = @(string)$sValue;
|
||||
$aPost[$sKey] = @(string) $sValue;
|
||||
}
|
||||
}
|
||||
$this->Set('arguments_post', $aPost);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->Set('arguments_post', array());
|
||||
}
|
||||
$sLength = mb_strlen($this->Get('issue'));
|
||||
if ($sLength > 255) {
|
||||
$this->Set('issue', mb_substr($this->Get('issue'), 0, 210)." -truncated ($sLength chars)");
|
||||
|
||||
$sLength = strlen($this->Get('issue'));
|
||||
if ($sLength > 255)
|
||||
{
|
||||
$this->Set('issue', substr($this->Get('issue'), 0, 200)." -truncated ($sLength chars)");
|
||||
}
|
||||
|
||||
$sLength = mb_strlen($this->Get('impact'));
|
||||
if ($sLength > 255) {
|
||||
$this->Set('impact', mb_substr($this->Get('impact'), 0, 210)." -truncated ($sLength chars)");
|
||||
$sLength = strlen($this->Get('impact'));
|
||||
if ($sLength > 255)
|
||||
{
|
||||
$this->Set('impact', substr($this->Get('impact'), 0, 200)." -truncated ($sLength chars)");
|
||||
}
|
||||
|
||||
$sLength = mb_strlen($this->Get('page'));
|
||||
if ($sLength > 255) {
|
||||
$this->Set('page', mb_substr($this->Get('page'), 0, 210)." -truncated ($sLength chars)");
|
||||
$sLength = strlen($this->Get('page'));
|
||||
if ($sLength > 255)
|
||||
{
|
||||
$this->Set('page', substr($this->Get('page'), 0, 200)." -truncated ($sLength chars)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ 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;
|
||||
|
||||
require_once(APPROOT.'application/xlsxwriter.class.php');
|
||||
|
||||
@@ -83,7 +82,6 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
|
||||
case 'xlsx_options':
|
||||
$oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:XLSXOptions'));
|
||||
$oPanel->AddSubBlock(ExportHelper::GetAlertForExcelMaliciousInjection());
|
||||
|
||||
$oMulticolumn = MultiColumnUIBlockFactory::MakeStandard();
|
||||
$oPanel->AddSubBlock($oMulticolumn);
|
||||
|
||||
@@ -101,7 +101,7 @@ EOF;
|
||||
EOF;
|
||||
|
||||
SetupUtils::builddir(dirname($sFilePath));
|
||||
file_put_contents($sFilePath, $content, LOCK_EX);
|
||||
file_put_contents($sFilePath, $content);
|
||||
}
|
||||
}
|
||||
Dict::SetUserLanguage($sUserLang);
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Core\Kpi\KpiLogData;
|
||||
use Combodo\iTop\Service\Module\ModuleService;
|
||||
// Copyright (C) 2010-2023 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Measures operations duration, memory usage, etc. (and some other KPIs)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class ExecutionKPI
|
||||
@@ -18,8 +30,6 @@ class ExecutionKPI
|
||||
static protected $m_bEnabled_Memory = false;
|
||||
static protected $m_bBlameCaller = false;
|
||||
static protected $m_sAllowedUser = '*';
|
||||
static protected $m_bGenerateLegacyReport = true;
|
||||
static protected $m_fSlowQueries = 0;
|
||||
|
||||
static protected $m_aStats = []; // Recurrent operations
|
||||
static protected $m_aExecData = []; // One shot operations
|
||||
@@ -76,39 +86,14 @@ class ExecutionKPI
|
||||
return false;
|
||||
}
|
||||
|
||||
static public function SetGenerateLegacyReport($bReportExtensionsOnly)
|
||||
{
|
||||
self::$m_bGenerateLegacyReport = $bReportExtensionsOnly;
|
||||
}
|
||||
|
||||
static public function SetSlowQueries($fSlowQueries)
|
||||
{
|
||||
self::$m_fSlowQueries = $fSlowQueries;
|
||||
}
|
||||
|
||||
static public function GetDescription()
|
||||
{
|
||||
$aFeatures = array();
|
||||
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
|
||||
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
|
||||
$sFeatures = 'Measures: '.implode(', ', $aFeatures);
|
||||
$sFeatures = implode(', ', $aFeatures);
|
||||
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
|
||||
$sSlowQueries = '';
|
||||
if (self::$m_fSlowQueries > 0) {
|
||||
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
|
||||
}
|
||||
|
||||
$aExtensions = [];
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$aExtensions[] = ModuleService::GetInstance()->GetModuleNameFromObject($oExtensionInstance);
|
||||
}
|
||||
$sExtensions = '';
|
||||
if (count($aExtensions) > 0) {
|
||||
$sExtensions = '. KPI Extensions: ['.implode(', ', $aExtensions).']';
|
||||
}
|
||||
|
||||
return "KPI logging is active for $sFor. $sFeatures$sSlowQueries$sExtensions";
|
||||
return "KPI logging is active for $sFor. Measures: $sFeatures";
|
||||
}
|
||||
|
||||
static public function ReportStats()
|
||||
@@ -116,28 +101,7 @@ class ExecutionKPI
|
||||
if (!self::IsEnabled()) return;
|
||||
|
||||
global $fItopStarted;
|
||||
global $iItopInitialMemory;
|
||||
$sExecId = microtime(); // id to differentiate the hrefs!
|
||||
$sRequest = $_SERVER['REQUEST_URI'].' ('.$_SERVER['REQUEST_METHOD'].')';
|
||||
if (isset($_POST['operation'])) {
|
||||
$sRequest .= ' operation: '.$_POST['operation'];
|
||||
}
|
||||
|
||||
$fStop = MyHelpers::getmicrotime();
|
||||
if (($fStop - $fItopStarted) > self::$m_fSlowQueries) {
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
$iCurrentMemory = self::memory_get_usage();
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$oKPILogData = new KpiLogData(KpiLogData::TYPE_REQUEST, 'Page', $sRequest, $fItopStarted, $fStop, '', $iItopInitialMemory, $iCurrentMemory, $iPeakMemory);
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
|
||||
if (!self::$m_bGenerateLegacyReport) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aBeginTimes = array();
|
||||
foreach (self::$m_aExecData as $aOpStats)
|
||||
@@ -150,9 +114,9 @@ class ExecutionKPI
|
||||
|
||||
$sHtml = "<hr/>";
|
||||
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
|
||||
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>";
|
||||
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>";
|
||||
$oStarted = DateTime::createFromFormat('U.u', $fItopStarted);
|
||||
$sHtml .= '<p>'.$oStarted->format('Y-m-d H:i:s.u').'</p>';
|
||||
$sHtml .= "<p>".$oStarted->format('Y-m-d H:i:s.u')."</p>";
|
||||
$sHtml .= "<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>";
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
@@ -293,7 +257,7 @@ class ExecutionKPI
|
||||
$sTotalInter = round($fTotalInter, 3);
|
||||
$sMinInter = round($fMinInter, 3);
|
||||
$sMaxInter = round($fMaxInter, 3);
|
||||
if (($fTotalInter >= self::$m_fSlowQueries))
|
||||
if (($fTotalInter >= $fSlowQueries))
|
||||
{
|
||||
if ($bDisplayHeader)
|
||||
{
|
||||
@@ -321,19 +285,37 @@ class ExecutionKPI
|
||||
self::Report($sHtml);
|
||||
}
|
||||
|
||||
public static function InitStats()
|
||||
{
|
||||
// Invoke extensions to initialize the KPI statistics
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$oExtensionInstance->InitStats();
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ResetCounters();
|
||||
}
|
||||
self::Push($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack executions to remove children duration from stats
|
||||
*
|
||||
* @param \ExecutionKPI $oExecutionKPI
|
||||
*/
|
||||
private static function Push(ExecutionKPI $oExecutionKPI)
|
||||
{
|
||||
self::$m_aExecutionStack[] = $oExecutionKPI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop current child and count its duration in its parent
|
||||
*
|
||||
* @param float|int $fChildDuration
|
||||
*/
|
||||
private static function Pop(float $fChildDuration = 0)
|
||||
{
|
||||
array_pop(self::$m_aExecutionStack);
|
||||
// Update the parent's children duration
|
||||
$oPrevExecutionKPI = end(self::$m_aExecutionStack);
|
||||
if ($oPrevExecutionKPI) {
|
||||
$oPrevExecutionKPI->m_fChildrenDuration += $fChildDuration;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the duration since startup, and reset the counter for the next measure
|
||||
//
|
||||
@@ -341,15 +323,9 @@ class ExecutionKPI
|
||||
{
|
||||
global $fItopStarted;
|
||||
|
||||
if (!self::IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aNewEntry = null;
|
||||
|
||||
$fStarted = $this->m_fStarted;
|
||||
$fStopped = $this->m_fStarted;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$aNewEntry = array(
|
||||
'op' => $sOperationDesc,
|
||||
@@ -360,9 +336,6 @@ class ExecutionKPI
|
||||
$this->m_fStarted = $fStopped;
|
||||
}
|
||||
|
||||
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||
$iCurrentMemory = 0;
|
||||
$iPeakMemory = 0;
|
||||
if (self::$m_bEnabled_Memory)
|
||||
{
|
||||
$iCurrentMemory = self::memory_get_usage();
|
||||
@@ -372,118 +345,40 @@ class ExecutionKPI
|
||||
}
|
||||
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
|
||||
$aNewEntry['mem_end'] = $iCurrentMemory;
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
$aNewEntry['mem_peak'] = $iPeakMemory;
|
||||
if (function_exists('memory_get_peak_usage'))
|
||||
{
|
||||
$aNewEntry['mem_peak'] = memory_get_peak_usage();
|
||||
}
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
$this->m_iInitialMemory = $iCurrentMemory;
|
||||
}
|
||||
|
||||
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_REPORT,
|
||||
'Step',
|
||||
$sOperationDesc,
|
||||
$fStarted,
|
||||
$fStopped,
|
||||
$sExtension,
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory);
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($aNewEntry) && self::$m_bGenerateLegacyReport)
|
||||
if (!is_null($aNewEntry))
|
||||
{
|
||||
self::$m_aExecData[] = $aNewEntry;
|
||||
}
|
||||
$this->ResetCounters();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute statistics for a call to an extension
|
||||
* Note: not working in dev mode (with links to env-production)
|
||||
*
|
||||
* @param object|string $object object called
|
||||
* @param string $sMethod method called on the object
|
||||
* @param string $sMessage additional message
|
||||
*
|
||||
* @return bool true if an extension was found for this object::method
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function ComputeStatsForExtension($object, string $sMethod, string $sMessage = ''): bool
|
||||
{
|
||||
if (!self::IsEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
|
||||
if (utils::StartsWith($sSignature, '[')) {
|
||||
$this->ComputeStats('Extension', "$sSignature $sMessage");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function ComputeStats($sOperation, $sArguments)
|
||||
{
|
||||
if (!self::IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fDuration = 0;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
$aCallstack = [];
|
||||
if (self::$m_bGenerateLegacyReport) {
|
||||
if (self::$m_bBlameCaller) {
|
||||
$aCallstack = MyHelpers::get_callstack(1);
|
||||
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||
'time' => $fDuration,
|
||||
'callers' => $aCallstack,
|
||||
];
|
||||
} else {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||
'time' => $fDuration
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||
$iCurrentMemory = 0;
|
||||
$iPeakMemory = 0;
|
||||
if (self::$m_bEnabled_Memory)
|
||||
{
|
||||
$iCurrentMemory = self::memory_get_usage();
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
}
|
||||
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_STATS,
|
||||
$sOperation,
|
||||
$sArguments,
|
||||
$this->m_fStarted,
|
||||
$fStopped,
|
||||
$sExtension,
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory,
|
||||
$aCallstack);
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
$fSelfDuration = $fDuration - $this->m_fChildrenDuration;
|
||||
if (self::$m_bBlameCaller) {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
||||
'time' => $fSelfDuration,
|
||||
'callers' => MyHelpers::get_callstack(1),
|
||||
);
|
||||
} else {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
||||
'time' => $fSelfDuration,
|
||||
);
|
||||
}
|
||||
}
|
||||
self::Pop($fDuration);
|
||||
}
|
||||
|
||||
protected function ResetCounters()
|
||||
@@ -513,7 +408,35 @@ class ExecutionKPI
|
||||
|
||||
static protected function memory_get_usage()
|
||||
{
|
||||
return memory_get_usage(true);
|
||||
if (function_exists('memory_get_usage'))
|
||||
{
|
||||
return memory_get_usage(true);
|
||||
}
|
||||
|
||||
// Copied from the PHP manual
|
||||
//
|
||||
//If its Windows
|
||||
//Tested on Win XP Pro SP2. Should work on Win 2003 Server too
|
||||
//Doesn't work for 2000
|
||||
//If you need it to work for 2000 look at http://us2.php.net/manual/en/function.memory-get-usage.php#54642
|
||||
if (substr(PHP_OS,0,3) == 'WIN')
|
||||
{
|
||||
$output = array();
|
||||
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST', $output);
|
||||
|
||||
return preg_replace( '/[\D]/', '', $output[5] ) * 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
//We now assume the OS is UNIX
|
||||
//Tested on Mac OS X 10.4.6 and Linux Red Hat Enterprise 4
|
||||
//This should work on most UNIX systems
|
||||
$pid = getmypid();
|
||||
exec("ps -eo%mem,rss,pid | grep $pid", $output);
|
||||
$output = explode(" ", $output[0]);
|
||||
//rss is given in 1024 byte units
|
||||
return $output[1] * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
static public function memory_get_peak_usage($bRealUsage = false)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
@@ -576,11 +576,6 @@ class LogChannels
|
||||
public const DATATABLE = 'Datatable';
|
||||
|
||||
public const DEADLOCK = 'DeadLock';
|
||||
/**
|
||||
* @var string Everything related to PHP sessions tracking
|
||||
* @since 3.1.1 3.2.0 N°6901
|
||||
*/
|
||||
public const SESSIONTRACKER = 'SessionTracker';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the datamodel CRUD
|
||||
@@ -588,12 +583,6 @@ class LogChannels
|
||||
*/
|
||||
public const DM_CRUD = 'DMCRUD';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the datamodel CRUD
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const WEB_REQUEST = 'WebRequest';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the event service
|
||||
* @since 3.1.0
|
||||
@@ -617,8 +606,6 @@ class LogChannels
|
||||
|
||||
public const PORTAL = 'portal';
|
||||
|
||||
public const TEMPORARY_OBJECTS = 'TemporaryObjects';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
@@ -1143,13 +1130,9 @@ class DeprecatedCallsLog extends LogAPI
|
||||
parent::Enable($sTargetFile);
|
||||
|
||||
if (
|
||||
(
|
||||
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME))
|
||||
|| (defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME) && (constant(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME) !== true))
|
||||
)
|
||||
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME))
|
||||
&& static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)
|
||||
) {
|
||||
IssueLog::Trace('Setting '.static::class.' error handler to catch DEPRECATED', static::ENUM_CHANNEL_PHP_LIBMETHOD);
|
||||
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler'], E_DEPRECATED | E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
@@ -1247,9 +1230,7 @@ class DeprecatedCallsLog extends LogAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.1 3.1.0 N°4725 silently handles ConfigException
|
||||
* @since 3.0.4 3.1.0 N°4725 remove forgotten throw PHPDoc annotation
|
||||
*
|
||||
* @throws \ConfigException
|
||||
* @link https://www.php.net/debug_backtrace
|
||||
* @uses \debug_backtrace()
|
||||
*/
|
||||
@@ -1301,7 +1282,19 @@ class DeprecatedCallsLog extends LogAPI
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
||||
$sMessage = self::GetMessageFromStack($aStack);
|
||||
$iStackDeprecatedMethodLevel = 1; // level 0 = current method, level 1 = method containing the `NotifyDeprecatedPhpMethod` call
|
||||
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
|
||||
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
|
||||
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
|
||||
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
|
||||
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
|
||||
|
||||
$iStackCallerMethodLevel = $iStackDeprecatedMethodLevel + 1; // level 2 = caller of the deprecated method
|
||||
if (array_key_exists($iStackCallerMethodLevel, $aStack)) {
|
||||
$sCallerObject = $aStack[$iStackCallerMethodLevel]['class'];
|
||||
$sCallerMethod = $aStack[$iStackCallerMethodLevel]['function'];
|
||||
$sMessage .= " ({$sCallerObject}::{$sCallerMethod})";
|
||||
}
|
||||
|
||||
if (!is_null($sAdditionalMessage)) {
|
||||
$sMessage .= ' : '.$sAdditionalMessage;
|
||||
@@ -1310,45 +1303,6 @@ class DeprecatedCallsLog extends LogAPI
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_METHOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aDebugBacktrace data from {@see debug_backtrace()}
|
||||
*
|
||||
* @return string message to print to the log
|
||||
*/
|
||||
private static function GetMessageFromStack(array $aDebugBacktrace): string
|
||||
{
|
||||
// level 0 = current method
|
||||
// level 1 = deprecated method, containing the `NotifyDeprecatedPhpMethod` call
|
||||
$sMessage = 'Call'.self::GetMessageForCurrentStackLevel($aDebugBacktrace[1], " to ");
|
||||
|
||||
// level 2 = caller of the deprecated method
|
||||
if (array_key_exists(2, $aDebugBacktrace)) {
|
||||
$sMessage .= ' (from ';
|
||||
$sMessage .= self::GetMessageForCurrentStackLevel($aDebugBacktrace[2]);
|
||||
$sMessage .= ')';
|
||||
}
|
||||
|
||||
return $sMessage;
|
||||
}
|
||||
|
||||
private static function GetMessageForCurrentStackLevel(array $aCurrentLevelDebugTrace, ?string $sPrefix = ""): string
|
||||
{
|
||||
$sMessage = "";
|
||||
if (array_key_exists('class', $aCurrentLevelDebugTrace)) {
|
||||
$sDeprecatedObject = $aCurrentLevelDebugTrace['class'];
|
||||
$sDeprecatedMethod = $aCurrentLevelDebugTrace['function'] ?? "";
|
||||
$sMessage = "{$sPrefix}{$sDeprecatedObject}::{$sDeprecatedMethod} in ";
|
||||
}
|
||||
|
||||
if (array_key_exists('file', $aCurrentLevelDebugTrace)) {
|
||||
$sCallerFile = $aCurrentLevelDebugTrace['file'];
|
||||
$sCallerLine = $aCurrentLevelDebugTrace['line'] ?? "";
|
||||
$sMessage .= "{$sCallerFile}#L{$sCallerLine}";
|
||||
}
|
||||
|
||||
return $sMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sAdditionalMessage
|
||||
* @since 3.1.0
|
||||
@@ -1450,7 +1404,7 @@ class LogFileRotationProcess implements iScheduledProcess
|
||||
$iMaxDays = MetaModel::GetConfig()->Get(LogAPI::ENUM_CONFIG_PARAM_PURGE_MAX_KEEP_DAYS);
|
||||
|
||||
// Files iterator (*.*)
|
||||
$oIterator = new \GlobIterator(APPROOT.'log'.DIRECTORY_SEPARATOR.'*.*');
|
||||
$oIterator = new \GlobIterator(APPROOT.'log'.DIRECTORY_SEPARATOR.'/*.*');
|
||||
$aLogFiles = iterator_to_array($oIterator);
|
||||
|
||||
// Reference date
|
||||
@@ -1462,11 +1416,6 @@ class LogFileRotationProcess implements iScheduledProcess
|
||||
// File real path
|
||||
$sFileRealPath = $oLogFile->getRealPath();
|
||||
|
||||
// Check file extension
|
||||
if(!in_array($oLogFile->getExtension(), ['log','sql','xml'])){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute number of days since last modification
|
||||
$oDateFileLastModification = new DateTime();
|
||||
$oDateFileLastModification->setTimestamp($oLogFile->getMTime());
|
||||
@@ -1707,8 +1656,6 @@ class ExceptionLog extends LogAPI
|
||||
*/
|
||||
private static function GetLastEventIssue()
|
||||
{
|
||||
$oRet = self::$oLastEventIssue;
|
||||
self::$oLastEventIssue = null;
|
||||
return $oRet;
|
||||
return self::$oLastEventIssue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
|
||||
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
|
||||
use Combodo\iTop\Service\Events\EventData;
|
||||
use Combodo\iTop\Service\Events\EventService;
|
||||
|
||||
require_once APPROOT.'core/modulehandler.class.inc.php';
|
||||
require_once APPROOT.'core/querymodifier.class.inc.php';
|
||||
@@ -1241,7 +1238,7 @@ abstract class MetaModel
|
||||
}
|
||||
$sTable = self::DBGetTable($sClass);
|
||||
|
||||
// Could be completed later with all the classes that are using a given table
|
||||
// Could be completed later with all the classes that are using a given table
|
||||
if (!array_key_exists($sTable, $aTables)) {
|
||||
$aTables[$sTable] = array();
|
||||
}
|
||||
@@ -1445,10 +1442,8 @@ abstract class MetaModel
|
||||
*
|
||||
* @return AttributeDefinition[]
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @see GetAttributesList for attcode list
|
||||
*/
|
||||
final public static function ListAttributeDefs($sClass)
|
||||
final static public function ListAttributeDefs($sClass)
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
return self::$m_aAttribDefs[$sClass];
|
||||
@@ -1461,10 +1456,8 @@ abstract class MetaModel
|
||||
* @param string[] $aDesiredAttTypes Array of AttributeDefinition classes to filter the list on
|
||||
* @param string|null $sListCode If provided, attributes will be limited to those in this zlist
|
||||
*
|
||||
* @return string[] list of attcodes
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @see ListAttributeDefs to get AttributeDefinition array instead
|
||||
*/
|
||||
final public static function GetAttributesList(string $sClass, array $aDesiredAttTypes = [], ?string $sListCode = null)
|
||||
{
|
||||
@@ -2931,7 +2924,52 @@ abstract class MetaModel
|
||||
}
|
||||
|
||||
self::$m_sTablePrefix = $sTablePrefix;
|
||||
self::InitExtensions();
|
||||
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = [
|
||||
'iApplicationUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iLoginFSMExtension',
|
||||
'iLoginUIExtension',
|
||||
'iLogoutExtension',
|
||||
'iQueryModifier',
|
||||
'iOnClassInitialization',
|
||||
'iPopupMenuExtension',
|
||||
'iPageUIExtension',
|
||||
'iPageUIBlockExtension',
|
||||
'iBackofficeLinkedScriptsExtension',
|
||||
'iBackofficeEarlyScriptExtension',
|
||||
'iBackofficeScriptExtension',
|
||||
'iBackofficeInitScriptExtension',
|
||||
'iBackofficeReadyScriptExtension',
|
||||
'iBackofficeLinkedStylesheetsExtension',
|
||||
'iBackofficeStyleExtension',
|
||||
'iBackofficeDictEntriesExtension',
|
||||
'iBackofficeDictEntriesPrefixesExtension',
|
||||
'iPortalUIExtension',
|
||||
'ModuleHandlerApiInterface',
|
||||
'iNewsroomProvider',
|
||||
'iModuleExtension',
|
||||
];
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
self::$m_aExtensionClassNames[$sInterface] = array();
|
||||
}
|
||||
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
$oExtensionInstance = null;
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable())
|
||||
{
|
||||
self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the classes (declared attributes, etc.)
|
||||
//
|
||||
@@ -3526,7 +3564,7 @@ abstract class MetaModel
|
||||
}
|
||||
|
||||
// Set the "host class" as soon as possible, since HierarchicalKeys use it for their 'target class' as well
|
||||
// and this needs to be know early (for Init_IsKnowClass 19 lines below)
|
||||
// and this needs to be know early (for Init_IsKnowClass 19 lines below)
|
||||
$oAtt->SetHostClass($sTargetClass);
|
||||
|
||||
// Some attributes could refer to a class
|
||||
@@ -3568,7 +3606,7 @@ abstract class MetaModel
|
||||
|
||||
self::$m_aAttribDefs[$sTargetClass][$oAtt->GetCode()] = $oAtt;
|
||||
self::$m_aAttribOrigins[$sTargetClass][$oAtt->GetCode()] = $sTargetClass;
|
||||
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
|
||||
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3580,7 +3618,8 @@ abstract class MetaModel
|
||||
{
|
||||
MyHelpers::CheckKeyInArray('list code', $sListCode, self::$m_aListInfos);
|
||||
|
||||
if (!$sTargetClass) {
|
||||
if (!$sTargetClass)
|
||||
{
|
||||
$sTargetClass = self::GetCallersPHPClass("Init");
|
||||
}
|
||||
|
||||
@@ -3768,7 +3807,7 @@ abstract class MetaModel
|
||||
self::$m_aStimuli[$sTargetClass][$oStimulus->GetCode()] = $oStimulus;
|
||||
|
||||
// I wanted to simplify the syntax of the declaration of objects in the biz model
|
||||
// Therefore, the reference to the host class is set there
|
||||
// Therefore, the reference to the host class is set there
|
||||
$oStimulus->SetHostClass($sTargetClass);
|
||||
}
|
||||
|
||||
@@ -4223,78 +4262,40 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCurrentUser = [];
|
||||
$aCurrentContact = [];
|
||||
$aCurrentUser = array();
|
||||
$aCurrentContact = array();
|
||||
foreach ($aExpectedArgs as $expression)
|
||||
{
|
||||
$aName = explode('->', $expression->GetName());
|
||||
if ($aName[0] == 'current_contact_id') {
|
||||
$aPlaceholders['current_contact_id'] = UserRights::GetContactId();
|
||||
} else if ($aName[0] == 'current_user') {
|
||||
}
|
||||
if ($aName[0] == 'current_user') {
|
||||
array_push($aCurrentUser, $aName[1]);
|
||||
} else if ($aName[0] == 'current_contact') {
|
||||
}
|
||||
if ($aName[0] == 'current_contact') {
|
||||
array_push($aCurrentContact, $aName[1]);
|
||||
}
|
||||
}
|
||||
if (count($aCurrentUser) > 0) {
|
||||
static::FillObjectPlaceholders($aPlaceholders, 'current_user', UserRights::GetUserObject(), $aCurrentUser);
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$aPlaceholders['current_user->object()'] = $oUser;
|
||||
foreach ($aCurrentUser as $sField) {
|
||||
$aPlaceholders['current_user->'.$sField] = $oUser->Get($sField);
|
||||
}
|
||||
}
|
||||
if (count($aCurrentContact) > 0) {
|
||||
static::FillObjectPlaceholders($aPlaceholders, 'current_contact', UserRights::GetContactObject(), $aCurrentContact);
|
||||
$oPerson = UserRights::GetContactObject();
|
||||
$aPlaceholders['current_contact->object()'] = $oPerson;
|
||||
foreach ($aCurrentContact as $sField) {
|
||||
$aPlaceholders['current_contact->'.$sField] = $oPerson->Get($sField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aPlaceholders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.1 N°6824
|
||||
* @param array $aPlaceholders
|
||||
* @param string $sPlaceHolderPrefix
|
||||
* @param ?\DBObject $oObject
|
||||
* @param array $aCurrentUser
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*/
|
||||
private static function FillObjectPlaceholders(array &$aPlaceholders, string $sPlaceHolderPrefix, ?\DBObject $oObject, array $aCurrentUser) : void {
|
||||
$sPlaceHolderKey = $sPlaceHolderPrefix."->object()";
|
||||
if (is_null($oObject)){
|
||||
$aContext = [
|
||||
"current_user_id" => UserRights::GetUserId(),
|
||||
"null object type" => $sPlaceHolderPrefix,
|
||||
"fields" => $aCurrentUser,
|
||||
];
|
||||
IssueLog::Warning("Unresolved placeholders due to null object in current context", null,
|
||||
$aContext);
|
||||
$aPlaceholders[$sPlaceHolderKey] = Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
|
||||
foreach ($aCurrentUser as $sField) {
|
||||
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
|
||||
$aPlaceholders[$sPlaceHolderKey] = Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
|
||||
}
|
||||
} else {
|
||||
$aPlaceholders[$sPlaceHolderKey] = $oObject;
|
||||
foreach ($aCurrentUser as $sField) {
|
||||
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
|
||||
// Mind that the "id" is not viewed as a valid att. code by \MetaModel::IsValidAttCode() so we have to test it manually
|
||||
if ($sField !== "id" && false === MetaModel::IsValidAttCode(get_class($oObject), $sField)){
|
||||
$aContext = [
|
||||
"current_user_id" => UserRights::GetUserId(),
|
||||
"obj_class" => get_class($oObject),
|
||||
"placeholder" => $sPlaceHolderKey,
|
||||
"invalid_field" => $sField,
|
||||
];
|
||||
IssueLog::Warning("Unresolved placeholder due to invalid attribute", null,
|
||||
$aContext);
|
||||
$aPlaceholders[$sPlaceHolderKey] = Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
|
||||
continue;
|
||||
}
|
||||
|
||||
$aPlaceholders[$sPlaceHolderKey] = $oObject->Get($sField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBSearch $oFilter
|
||||
*
|
||||
@@ -5156,7 +5157,7 @@ abstract class MetaModel
|
||||
*/
|
||||
protected static function DBCreateTables($aCallback = null)
|
||||
{
|
||||
[$aErrors, $aSugFix, $aCondensedQueries] = self::DBCheckFormat();
|
||||
list($aErrors, $aSugFix, $aCondensedQueries) = self::DBCheckFormat();
|
||||
|
||||
//$sSQL = implode('; ', $aCondensedQueries); Does not work - multiple queries not allowed
|
||||
foreach($aCondensedQueries as $sQuery)
|
||||
@@ -5178,7 +5179,7 @@ abstract class MetaModel
|
||||
*/
|
||||
protected static function DBCreateViews()
|
||||
{
|
||||
[$aErrors, $aSugFix] = self::DBCheckViews();
|
||||
list($aErrors, $aSugFix) = self::DBCheckViews();
|
||||
|
||||
foreach($aSugFix as $sClass => $aTarget)
|
||||
{
|
||||
@@ -6340,46 +6341,38 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = 'production')
|
||||
{
|
||||
// Startup on a new environment is not supported
|
||||
static $bStarted = false;
|
||||
if ($bStarted) {
|
||||
return;
|
||||
}
|
||||
$bStarted = true;
|
||||
|
||||
self::$m_sEnvironment = $sEnvironment;
|
||||
|
||||
try {
|
||||
if (!defined('MODULESROOT')) {
|
||||
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
|
||||
if (!defined('MODULESROOT'))
|
||||
{
|
||||
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
|
||||
|
||||
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
|
||||
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
|
||||
|
||||
// $config can be either a filename, or a Configuration object (volatile!)
|
||||
if ($config instanceof Config) {
|
||||
self::LoadConfig($config, $bAllowCache);
|
||||
} else {
|
||||
self::LoadConfig(new Config($config), $bAllowCache);
|
||||
}
|
||||
|
||||
if ($bModelOnly) {
|
||||
return;
|
||||
}
|
||||
// $config can be either a filename, or a Configuration object (volatile!)
|
||||
if ($config instanceof Config)
|
||||
{
|
||||
self::LoadConfig($config, $bAllowCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::LoadConfig(new Config($config), $bAllowCache);
|
||||
}
|
||||
|
||||
CMDBSource::SelectDB(self::$m_sDBName);
|
||||
|
||||
foreach (MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass) {
|
||||
$oPHPClass::OnMetaModelStarted();
|
||||
if ($bModelOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ExpressionCache::Warmup();
|
||||
}
|
||||
finally {
|
||||
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
|
||||
EventService::InitService();
|
||||
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
|
||||
}
|
||||
CMDBSource::SelectDB(self::$m_sDBName);
|
||||
|
||||
foreach(MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass)
|
||||
{
|
||||
$oPHPClass::OnMetaModelStarted();
|
||||
}
|
||||
|
||||
ExpressionCache::Warmup();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6425,9 +6418,7 @@ abstract class MetaModel
|
||||
|
||||
ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration'));
|
||||
ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory'));
|
||||
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
|
||||
ExecutionKPI::SetGenerateLegacyReport(self::$m_oConfig->Get('log_kpi_generate_legacy_report'));
|
||||
ExecutionKPI::SetSlowQueries(self::$m_oConfig->Get('log_kpi_slow_queries'));
|
||||
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
|
||||
|
||||
self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write');
|
||||
self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys');
|
||||
@@ -6521,7 +6512,7 @@ abstract class MetaModel
|
||||
$aCache['m_aExtensionClassNames'] = self::$m_aExtensionClassNames;
|
||||
$aCache['m_Category2Class'] = self::$m_Category2Class;
|
||||
$aCache['m_aRootClasses'] = self::$m_aRootClasses; // array of "classname" => "rootclass"
|
||||
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
|
||||
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
|
||||
$aCache['m_aChildClasses'] = self::$m_aChildClasses; // array of ("classname" => array of "childclass")
|
||||
$aCache['m_aClassParams'] = self::$m_aClassParams; // array of ("classname" => array of class information)
|
||||
$aCache['m_aAttribDefs'] = self::$m_aAttribDefs; // array of ("classname" => array of attributes)
|
||||
@@ -6546,7 +6537,6 @@ abstract class MetaModel
|
||||
|
||||
CMDBSource::InitFromConfig(self::$m_oConfig);
|
||||
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
|
||||
ExecutionKPI::InitStats();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6578,19 +6568,6 @@ abstract class MetaModel
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Used for resetting the configuration during automated tests
|
||||
|
||||
* @param \Config $oConfiguration
|
||||
*
|
||||
* @return void
|
||||
* @since 3.0.4 3.1.1 3.2.0
|
||||
*/
|
||||
public static function SetConfig(Config $oConfiguration)
|
||||
{
|
||||
self::$m_oConfig = $oConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Config
|
||||
*/
|
||||
@@ -6779,13 +6756,7 @@ abstract class MetaModel
|
||||
|
||||
if ($bMustBeFound && empty($aRow))
|
||||
{
|
||||
$sNotFoundErrorMessage = "No result for the single row query";
|
||||
IssueLog::Info($sNotFoundErrorMessage, LogChannels::CMDB_SOURCE, [
|
||||
'class' => $sClass,
|
||||
'key' => $iKey,
|
||||
'sql_query' => $sSQL,
|
||||
]);
|
||||
throw new CoreException($sNotFoundErrorMessage);
|
||||
throw new CoreException("No result for the single row query: '$sSQL'");
|
||||
}
|
||||
|
||||
return $aRow;
|
||||
@@ -6846,15 +6817,24 @@ abstract class MetaModel
|
||||
$sClass = $aRow[$sClassAlias."finalclass"];
|
||||
}
|
||||
|
||||
// if an object is already being updated, then this method will return this object instead of recreating a new one.
|
||||
// At this point the method DBUpdate of a new object with the same class and id won't do anything due to reentrance protection,
|
||||
// so to ensure that the potential modifications are correctly saved, the object currently being updated is returned.
|
||||
// DBUpdate() method then will take care that all the modifications will be saved.
|
||||
if (array_key_exists($sClassAlias.'id', $aRow)) {
|
||||
$iKey = $aRow[$sClassAlias."id"];
|
||||
$oObject = self::GetReentranceObject($sClass, $iKey);
|
||||
if ($oObject !== false) {
|
||||
return $oObject;
|
||||
}
|
||||
}
|
||||
|
||||
return new $sClass($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate an object already persisted to the Database.
|
||||
*
|
||||
* Note that LinkedSet attributes are not loaded.
|
||||
* DBObject::Reload() will be called when getting a LinkedSet attribute
|
||||
*
|
||||
* @api
|
||||
* @see MetaModel::GetObjectWithArchive to get object even if it's archived
|
||||
* @see utils::PushArchiveMode() to enable search on archived objects
|
||||
@@ -6869,21 +6849,25 @@ abstract class MetaModel
|
||||
* $bMustBeFound=false)
|
||||
* @throws CoreException if no result found and $bMustBeFound=true
|
||||
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
|
||||
* @throws \Exception
|
||||
*
|
||||
*/
|
||||
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
|
||||
{
|
||||
$oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
|
||||
|
||||
if (empty($oObject)) {
|
||||
if (empty($oObject))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!utils::IsArchiveMode() && $oObject->IsArchived()) {
|
||||
if (!utils::IsArchiveMode() && $oObject->IsArchived())
|
||||
{
|
||||
if ($bMustBeFound) {
|
||||
throw new ArchivedObjectException("The object $sClass::$iKey is archived");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $oObject;
|
||||
@@ -6918,22 +6902,6 @@ abstract class MetaModel
|
||||
return $iCount === 1;
|
||||
}
|
||||
|
||||
public static function GetFinalClassName(string $sClass, int $iKey): string
|
||||
{
|
||||
if (MetaModel::IsStandaloneClass($sClass)) {
|
||||
return $sClass;
|
||||
}
|
||||
|
||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||
$sTable = MetaModel::DBGetTable($sRootClass);
|
||||
$sKeyCol = MetaModel::DBGetKey($sRootClass);
|
||||
$sEscapedKey = CMDBSource::Quote($iKey);
|
||||
$sFinalClassField = Metamodel::DBGetClassField($sRootClass);
|
||||
|
||||
$sQuery = "SELECT `{$sFinalClassField}` FROM `{$sTable}` WHERE `{$sKeyCol}` = {$sEscapedKey}";
|
||||
return CMDBSource::QueryToScalar($sQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the specified class and id. If the object is archived it will be returned anyway (this is for pre-2.4
|
||||
* module compatibility, see N.1108)
|
||||
@@ -7671,57 +7639,6 @@ abstract class MetaModel
|
||||
unset(self::$m_aReentranceProtection[get_class($oObject)][$oObject->GetKey()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For test purpose
|
||||
* @throws \ReflectionException
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public static function InitExtensions()
|
||||
{
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = [
|
||||
'iLoginFSMExtension',
|
||||
'iLogoutExtension',
|
||||
'iLoginUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationUIExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iPopupMenuExtension',
|
||||
'iPageUIExtension',
|
||||
'iPageUIBlockExtension',
|
||||
'iBackofficeLinkedScriptsExtension',
|
||||
'iBackofficeEarlyScriptExtension',
|
||||
'iBackofficeScriptExtension',
|
||||
'iBackofficeInitScriptExtension',
|
||||
'iBackofficeReadyScriptExtension',
|
||||
'iBackofficeLinkedStylesheetsExtension',
|
||||
'iBackofficeStyleExtension',
|
||||
'iBackofficeDictEntriesExtension',
|
||||
'iBackofficeDictEntriesPrefixesExtension',
|
||||
'iPortalUIExtension',
|
||||
'iQueryModifier',
|
||||
'iOnClassInitialization',
|
||||
'iModuleExtension',
|
||||
'iKPILoggerExtension',
|
||||
'ModuleHandlerApiInterface',
|
||||
'iNewsroomProvider',
|
||||
];
|
||||
foreach ($aInterfaces as $sInterface) {
|
||||
self::$m_aExtensionClassNames[$sInterface] = array();
|
||||
}
|
||||
|
||||
foreach (get_declared_classes() as $sPHPClass) {
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
$oExtensionInstance = null;
|
||||
foreach ($aInterfaces as $sInterface) {
|
||||
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) {
|
||||
self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -22,9 +22,7 @@
|
||||
* A class to serialize the execution of some code sections
|
||||
* Emulates the API of PECL Mutex class
|
||||
* Relies on MySQL locks because the API sem_get is not always present in the
|
||||
* installed PHP.
|
||||
*
|
||||
* @link https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html MySQL locking functions documentation
|
||||
* installed PHP.
|
||||
*
|
||||
* @copyright Copyright (C) 2013-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
@@ -259,7 +257,7 @@ class iTopMutex
|
||||
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
|
||||
|
||||
if (!$this->hDBLink) {
|
||||
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
|
||||
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
}
|
||||
|
||||
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
|
||||
|
||||
@@ -3526,7 +3526,6 @@ class CharConcatWSExpression extends CharConcatExpression
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
// TODO: Seems weird, this should rather be $aRes[] = $oExpr->Evaluate($aArgs);
|
||||
$aRes .= $oExpr->Evaluate($aArgs);
|
||||
}
|
||||
return implode($this->m_separator, $aRes);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
@@ -25,10 +25,11 @@ use Combodo\iTop\Renderer\BlockRenderer;
|
||||
define('CASELOG_VISIBLE_ITEMS', 2);
|
||||
define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\n\n");
|
||||
|
||||
require_once('ormcaselogservice.inc.php');
|
||||
|
||||
/**
|
||||
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
||||
*
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
@@ -47,19 +48,22 @@ class ormCaseLog {
|
||||
protected $m_sLog;
|
||||
protected $m_aIndex;
|
||||
protected $m_bModified;
|
||||
|
||||
protected \ormCaseLogService $oOrmCaseLogService;
|
||||
|
||||
/**
|
||||
* Initializes the log with the first (initial) entry
|
||||
* @param $sLog string The text of the whole case log
|
||||
* @param $aIndex array The case log index
|
||||
*/
|
||||
public function __construct($sLog = '', $aIndex = array())
|
||||
public function __construct($sLog = '', $aIndex = [], \ormCaseLogService $oOrmCaseLogService=null)
|
||||
{
|
||||
$this->m_sLog = $sLog;
|
||||
$this->m_aIndex = $aIndex;
|
||||
$this->m_bModified = false;
|
||||
$this->oOrmCaseLogService = (is_null($oOrmCaseLogService)) ? new \ormCaseLogService() : $oOrmCaseLogService;
|
||||
$this->RebuildIndex();
|
||||
}
|
||||
|
||||
|
||||
public function GetText($bConvertToPlainText = false)
|
||||
{
|
||||
if ($bConvertToPlainText)
|
||||
@@ -72,7 +76,7 @@ class ormCaseLog {
|
||||
return $this->m_sLog;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function FromJSON($oJson)
|
||||
{
|
||||
if (!isset($oJson->items))
|
||||
@@ -88,8 +92,8 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a value that will be further JSON encoded
|
||||
*/
|
||||
* Return a value that will be further JSON encoded
|
||||
*/
|
||||
public function GetForJSON()
|
||||
{
|
||||
// Order by ascending date
|
||||
@@ -169,7 +173,7 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (utils::StrLen($this->m_sLog) - 1))
|
||||
if ($iPos < (strlen($this->m_sLog) - 1))
|
||||
{
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
|
||||
@@ -199,9 +203,9 @@ class ormCaseLog {
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $aData['date'], $aData['user_login'], $aData['user_id']);
|
||||
$sPlainText .= $sSeparator.$aData['message'];
|
||||
}
|
||||
return $sPlainText;
|
||||
return $sPlainText;
|
||||
}
|
||||
|
||||
|
||||
public function GetIndex()
|
||||
{
|
||||
return $this->m_aIndex;
|
||||
@@ -227,7 +231,7 @@ class ormCaseLog {
|
||||
{
|
||||
return count($this->m_aIndex);
|
||||
}
|
||||
|
||||
|
||||
public function ClearModifiedFlag()
|
||||
{
|
||||
$this->m_bModified = false;
|
||||
@@ -235,7 +239,7 @@ class ormCaseLog {
|
||||
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used within an email
|
||||
*/
|
||||
*/
|
||||
public function GetAsEmailHtml()
|
||||
{
|
||||
$sStyleCaseLogHeader = '';
|
||||
@@ -292,7 +296,7 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (utils::StrLen($this->m_sLog) - 1)) {
|
||||
if ($iPos < (strlen($this->m_sLog) - 1)) {
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
|
||||
|
||||
@@ -312,10 +316,10 @@ class ormCaseLog {
|
||||
$sHtml .= '</td></tr></table>';
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Produces an HTML representation, aimed at being used to produce a PDF with TCPDF (no table)
|
||||
*/
|
||||
*/
|
||||
public function GetAsSimpleHtml($aTransfoHandler = null)
|
||||
{
|
||||
$sStyleCaseLogEntry = '';
|
||||
@@ -338,7 +342,7 @@ class ormCaseLog {
|
||||
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry, true /* wiki "links" only */);
|
||||
}
|
||||
$sTextEntry = InlineImage::FixUrls($sTextEntry);
|
||||
}
|
||||
}
|
||||
$iPos += $aIndex[$index]['text_length'];
|
||||
|
||||
$sEntry = '<li>';
|
||||
@@ -373,7 +377,7 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (utils::StrLen($this->m_sLog) - 1)) {
|
||||
if ($iPos < (strlen($this->m_sLog) - 1)) {
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
|
||||
|
||||
@@ -396,7 +400,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)
|
||||
{
|
||||
$bPrintableVersion = (utils::ReadParam('printable', '0') == '1');
|
||||
@@ -467,7 +471,7 @@ class ormCaseLog {
|
||||
$oBlock->AddSubBlock($oCollapsibleBlock);
|
||||
}
|
||||
// Process the case of an eventual remainder (quick migration of AttributeText fields)
|
||||
if ($iPos < (utils::StrLen($this->m_sLog) - 1)) {
|
||||
if ($iPos < (strlen($this->m_sLog) - 1)) {
|
||||
// In this case the format is always "text"
|
||||
$sTextEntry = substr($this->m_sLog, $iPos);
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
|
||||
@@ -519,7 +523,7 @@ class ormCaseLog {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
@@ -534,14 +538,17 @@ class ormCaseLog {
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*
|
||||
*
|
||||
* @since 3.0.0 New $iOnBehalfOfId parameter
|
||||
* @since 3.0.0 May throw \ArchivedObjectException exception
|
||||
*/
|
||||
public function AddLogEntry(string $sText, $sOnBehalfOf = '', $iOnBehalfOfId = null)
|
||||
{
|
||||
$sText = HTMLSanitizer::Sanitize($sText);
|
||||
//date/time ops moved here for test stability
|
||||
$iNow = time();
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat());
|
||||
|
||||
$sText = HTMLSanitizer::Sanitize($sText);
|
||||
if ($sOnBehalfOf == '' && $iOnBehalfOfId === null) {
|
||||
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
||||
$iUserId = UserRights::GetUserId();
|
||||
@@ -580,16 +587,20 @@ class ormCaseLog {
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'date' => $iNow,
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => static::ENUM_FORMAT_HTML,
|
||||
);
|
||||
$this->RebuildIndex();
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
||||
{
|
||||
//date/time ops moved here for test stability
|
||||
$iNow = time();
|
||||
|
||||
if (isset($oJson->user_id))
|
||||
{
|
||||
if (!UserRights::IsAdministrator())
|
||||
@@ -620,7 +631,7 @@ class ormCaseLog {
|
||||
$iUserId = UserRights::GetUserId();
|
||||
$sOnBehalfOf = UserRights::GetUserFriendlyName();
|
||||
}
|
||||
|
||||
|
||||
if (isset($oJson->date))
|
||||
{
|
||||
$oDate = new DateTime($oJson->date);
|
||||
@@ -628,7 +639,7 @@ class ormCaseLog {
|
||||
}
|
||||
else
|
||||
{
|
||||
$iDate = time();
|
||||
$iDate = $iNow;
|
||||
}
|
||||
if (isset($oJson->format))
|
||||
{
|
||||
@@ -653,14 +664,14 @@ class ormCaseLog {
|
||||
$iTextlength = strlen($sText);
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => $iDate,
|
||||
'text_length' => $iTextlength,
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => $iNow,
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => $sFormat,
|
||||
);
|
||||
|
||||
$this->RebuildIndex();
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
@@ -716,7 +727,7 @@ class ormCaseLog {
|
||||
$iLast = end($aKeys); // Strict standards: the parameter passed to 'end' must be a variable since it is passed by reference
|
||||
return $iLast;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the text string corresponding to the given entry in the log (zero based index, older entries first)
|
||||
* @param integer $iIndex
|
||||
@@ -736,4 +747,17 @@ class ormCaseLog {
|
||||
$sText = substr($this->m_sLog, $iPos, $this->m_aIndex[$index]['text_length']);
|
||||
return InlineImage::FixUrls($sText);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°6275
|
||||
*/
|
||||
public function RebuildIndex(): void
|
||||
{
|
||||
$oNewOrmCaseLog = $this->oOrmCaseLogService->Rebuild($this->m_sLog, $this->m_aIndex);
|
||||
if (! is_null($oNewOrmCaseLog)) {
|
||||
$this->m_aIndex = $oNewOrmCaseLog->m_aIndex;
|
||||
$this->m_sLog = $oNewOrmCaseLog->m_sLog;
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
67
core/ormcaselogservice.inc.php
Normal file
67
core/ormcaselogservice.inc.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Service dedicated to ormCaseLog rebuild
|
||||
*
|
||||
* @since 3.1.0 N°6275
|
||||
*/
|
||||
class ormCaseLogService
|
||||
{
|
||||
/**
|
||||
* Array of "providers" of welcome popup messages
|
||||
* @var iOrmCaseLogExtension[]
|
||||
*/
|
||||
protected $aOrmCaseLogExtensions = null;
|
||||
|
||||
public function __construct(array $aOrmCaseLogExtensions=null)
|
||||
{
|
||||
$this->aOrmCaseLogExtensions = $aOrmCaseLogExtensions;
|
||||
}
|
||||
|
||||
protected function LoadCaseLogExtensions($aClassesForInterfaceOrmCaseLog=null) : array
|
||||
{
|
||||
if ($this->aOrmCaseLogExtensions !== null) return $this->aOrmCaseLogExtensions;
|
||||
|
||||
if ($aClassesForInterfaceOrmCaseLog === null) {
|
||||
$aClassesForInterfaceOrmCaseLog = \utils::GetClassesForInterface(iOrmCaseLogExtension::class, '',
|
||||
array('[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]', '[\\\\/]tests[\\\\/]'));
|
||||
}
|
||||
|
||||
$aConfiguredOrmCaseLogExtensionClasses = MetaModel::GetConfig()->Get('ormcaselog_extension_classes');
|
||||
$this->aOrmCaseLogExtensions = [];
|
||||
foreach ($aConfiguredOrmCaseLogExtensionClasses as $sConfiguredOrmCaseLogExtensionClass) {
|
||||
if (in_array($sConfiguredOrmCaseLogExtensionClass, $aClassesForInterfaceOrmCaseLog)){
|
||||
$this->aOrmCaseLogExtensions[] = new $sConfiguredOrmCaseLogExtensionClass();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->aOrmCaseLogExtensions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sLog
|
||||
* @param array|null $aIndex
|
||||
*
|
||||
* @return \ormCaseLog|null: returns rebuilt ormCaseLog. null if not touched
|
||||
*/
|
||||
public function Rebuild($sLog, $aIndex) : ?\ormCaseLog
|
||||
{
|
||||
$this->LoadCaseLogExtensions();
|
||||
|
||||
$bTouched = false;
|
||||
foreach ($this->aOrmCaseLogExtensions as $oOrmCaseLogExtension){
|
||||
/** var iOrmCaseLogExtension $oOrmCaseLogExtension */
|
||||
$bTouched = $bTouched || $oOrmCaseLogExtension->Rebuild($sLog, $aIndex);
|
||||
}
|
||||
|
||||
if ($bTouched){
|
||||
return new \ormCaseLog($sLog, $aIndex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -35,11 +35,10 @@ class ormCustomFieldsValue
|
||||
* _template_name: string,
|
||||
* template_id: string,
|
||||
* template_data: string,
|
||||
* user_data: array<string, mixed>,
|
||||
* user_data: string,
|
||||
* current_template_id: string,
|
||||
* current_template_data: string,
|
||||
* } $aCurrentValues Containing JSON encoded strings in template_data/current_template_data.
|
||||
* The user_data key contains an array with field code as key and field value as value
|
||||
* } $aCurrentValues Containing JSON encoded strings in template_data/current_template_data, user_data.
|
||||
* Warning, current_* are mandatory for data to be saved in a DBUpdate() call !
|
||||
*/
|
||||
protected $aCurrentValues;
|
||||
@@ -49,31 +48,13 @@ class ormCustomFieldsValue
|
||||
* @param string $sAttCode
|
||||
* @param array $aCurrentValues
|
||||
*/
|
||||
public function __construct(?DBObject $oHostObject, $sAttCode, $aCurrentValues = null)
|
||||
public function __construct(DBObject $oHostObject, $sAttCode, $aCurrentValues = null)
|
||||
{
|
||||
$this->oHostObject = $oHostObject;
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->aCurrentValues = $aCurrentValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DBObject|null
|
||||
*/
|
||||
public function GetHostObject(): ?DBObject
|
||||
{
|
||||
return $this->oHostObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject|null $oHostObject
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function SetHostObject(?DBObject $oHostObject): void
|
||||
{
|
||||
$this->oHostObject = $oHostObject;
|
||||
}
|
||||
|
||||
public function GetValues()
|
||||
{
|
||||
return $this->aCurrentValues;
|
||||
@@ -81,7 +62,6 @@ class ormCustomFieldsValue
|
||||
|
||||
/**
|
||||
* Wrapper used when the only thing you have is the value...
|
||||
*
|
||||
* @return \Combodo\iTop\Form\Form
|
||||
*/
|
||||
public function GetForm($sFormPrefix = null)
|
||||
@@ -116,19 +96,6 @@ class ormCustomFieldsValue
|
||||
return $this->GetHandler()->GetAsJSON($this->aCurrentValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $json
|
||||
* @param \AttributeDefinition $oAttDef
|
||||
*
|
||||
* @return \ormCustomFieldsValue
|
||||
*
|
||||
* @since 3.1.0 N°1150 Method creation
|
||||
*/
|
||||
public static function FromJSONToValue(?stdClass $json, AttributeCustomFields $oAttDef)
|
||||
{
|
||||
return $oAttDef->GetHandler()->FromJSONToValue($json, $oAttDef->GetCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \CustomFieldsHandler
|
||||
* @throws \Exception
|
||||
@@ -136,7 +103,6 @@ class ormCustomFieldsValue
|
||||
*/
|
||||
final protected function GetHandler()
|
||||
{
|
||||
/** @var \AttributeCustomFields $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this->oHostObject), $this->sAttCode);
|
||||
|
||||
return $oAttDef->GetHandler($this->GetValues());
|
||||
|
||||
@@ -86,33 +86,6 @@ class ormDocument
|
||||
{
|
||||
return ($this->m_data == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ormDocument $oCompared
|
||||
*
|
||||
* @return bool True if the current ormDocument is equals to $oCompared EXCEPT for its download count. False if any other property is different OR if count is the same.
|
||||
* @since 3.1.0 N°6502
|
||||
*/
|
||||
public function EqualsExceptDownloadsCount(ormDocument $oCompared): bool
|
||||
{
|
||||
// First checking equality on others properties
|
||||
if ($oCompared->GetData() !== $this->GetData()) {
|
||||
return false;
|
||||
}
|
||||
if ($oCompared->GetMimeType() !== $this->GetMimeType()) {
|
||||
return false;
|
||||
}
|
||||
if ($oCompared->GetFileName() !== $this->GetFileName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally check equality of the download count
|
||||
if ($oCompared->GetDownloadsCount() === $this->GetDownloadsCount()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetMimeType()
|
||||
{
|
||||
@@ -343,6 +316,6 @@ class ormDocument
|
||||
*/
|
||||
public function GetSignature(): string
|
||||
{
|
||||
return md5($this->GetData() ?? '');
|
||||
return md5($this->GetData());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,14 +98,4 @@ class PluginManager
|
||||
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* For test purpose
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected static function ResetPlugins()
|
||||
{
|
||||
self::$m_aExtensionClasses = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +43,6 @@
|
||||
|
||||
class SimpleCrypt
|
||||
{
|
||||
/**
|
||||
* @var \SimpleCrypt
|
||||
* @since 3.1.0 N°5388
|
||||
*/
|
||||
protected $oEngine;
|
||||
|
||||
public static function GetNewDefaultParams()
|
||||
{
|
||||
if(function_exists('sodium_crypto_secretbox_open') && function_exists('random_bytes')){
|
||||
@@ -68,7 +62,6 @@ class SimpleCrypt
|
||||
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
|
||||
return $sEngineName::GetNewDefaultParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt, Sodium or OpenSSL
|
||||
|
||||
@@ -537,7 +537,7 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('graphviz not found');
|
||||
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
@@ -592,7 +592,7 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('graphviz not found');
|
||||
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
@@ -218,8 +218,8 @@ class SQLObjectQueryBuilder
|
||||
continue;
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
// Skip this attribute if not made of SQL columns nor in current table
|
||||
if (count($oAttDef->GetSQLExpressions()) == 0 || $oAttDef->IsExternalField())
|
||||
// Skip this attribute if not made of SQL columns
|
||||
if (count($oAttDef->GetSQLExpressions()) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list",
|
||||
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
|
||||
$aTags = ContextTag::GetTags();
|
||||
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags, true), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
|
||||
// "complement" is a computed field, fed by Trigger sub-classes, in general in ComputeValues method, for eg. the TriggerOnObject fed it with target_class info
|
||||
MetaModel::Init_AddAttribute(new AttributeString("complement", array("allowed_values" => null, "sql" => "complement", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
|
||||
|
||||
@@ -121,9 +121,7 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
$oAction = MetaModel::GetObject('Action', $iActionId);
|
||||
if ($oAction->IsActive())
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oAction->DoExecute($this, $aContextArgs);
|
||||
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,38 +254,6 @@ abstract class TriggerOnObject extends Trigger
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate trigger based on attribute list given instead of changed attributes
|
||||
*
|
||||
* @param array $aContextArgs
|
||||
* @param array|null $aAttributes if null default to changed attributes
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
* @since 3.1.1 3.2.0 N°6228
|
||||
*/
|
||||
public function DoActivateForSpecificAttributes(array $aContextArgs, ?array $aAttributes)
|
||||
{
|
||||
if (isset($aContextArgs['this->object()']))
|
||||
{
|
||||
/** @var \DBObject $oObject */
|
||||
$oObject = $aContextArgs['this->object()'];
|
||||
if (is_null($aAttributes)) {
|
||||
$aChanges = $oObject->ListPreviousValuesForUpdatedAttributes();
|
||||
} else {
|
||||
$aChanges = array_fill_keys($aAttributes, true);
|
||||
}
|
||||
if (false === $this->IsTargetObject($oObject->GetKey(), $aChanges)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
parent::DoActivate($aContextArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $iObjectId
|
||||
* @param array $aChanges
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user