Compare commits
5 Commits
feature/47
...
feature/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6cfaebfbcf | ||
|
|
ab2b1a18b0 | ||
|
|
d241bc94bd | ||
|
|
1d3e7ee7cb | ||
|
|
4b05c80416 |
@@ -23,7 +23,7 @@
|
||||
|
||||
$(document).ready(function() {
|
||||
$("#viewer").iviewer({src: '{{ path('graphs/classes.svg') }}', zoom_animation: false});
|
||||
$('#viewer img').on('dragstart', function(event){
|
||||
$('#viewer img').bind('dragstart', function(event){
|
||||
event.preventDefault();
|
||||
});
|
||||
$(window).resize();
|
||||
|
||||
@@ -25,7 +25,7 @@ if (false === file_exists($sTcPdfRootFolder)) {
|
||||
echo $sCurrentScriptFileName.": No TCPDF lib detected, exiting !\n";
|
||||
return;
|
||||
}
|
||||
$sTcPdfFontsFolder = $sTcPdfRootFolder.'/fonts/';
|
||||
$sTcPdfFontsFolder = $sTcPdfRootFolder.'/Fonts/';
|
||||
|
||||
|
||||
/**
|
||||
|
||||
26
README.md
@@ -1,9 +1,5 @@
|
||||
<p align="center"><a href="https://combodo.com" target="_blank">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="/images/logos/logo-itop-baseline-light.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="/images/logos/logo-itop-baseline-dark.svg">
|
||||
<img src="/images/logos/logo-itop-baseline-light.svg" width="350" alt="Logo iTop with baseline" />
|
||||
</picture>
|
||||
<p align="center"><a href="https://www.combodo.com/itop-193" target="_blank">
|
||||
<img src="https://www.combodo.com/logos/logo-itop-baseline.svg" width=350>
|
||||
</a></p>
|
||||
|
||||
|
||||
@@ -80,9 +76,7 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
|
||||
### Names
|
||||
|
||||
- Al Hallak, Amr (a.k.a [@v4yne1](https://github.com/v4yne1))
|
||||
- Alves, David
|
||||
- Audon, Florian
|
||||
- Beck, Pedro
|
||||
- Beer, Christian (a.k.a [@ChristianBeer](https://www.github.com/ChristianBeer))
|
||||
- Bilger, Jean-François
|
||||
@@ -94,26 +88,20 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Colantoni, Maria Laura
|
||||
- Couronné, Guy
|
||||
- Dejin, Bie (a.k.a [@bdejin](https://github.com/bdejin))
|
||||
- Delicado, Elodie
|
||||
- Dvořák, Lukáš
|
||||
- Goethals, Stefan
|
||||
- Giuva, Vincenzo Katriel (a.k.a [@DarkNight97boss](https://github.com/DarkNight97boss))
|
||||
- Gumble, David
|
||||
- Heloir, Arthur
|
||||
- Janssens, Jelle (a.k.a [@janssensjelle](https://github.com/janssensjelle))
|
||||
- Ji, Leeb (冀利斌) (a.k.a [@chileeb](https://github.com/chileeb))
|
||||
- Kaltefleiter, Lars (a.k.a [@larhip](https://www.github.com/larhip))
|
||||
- Khamit, Shamil
|
||||
- Kincel, Martin
|
||||
- Konečný, Kamil
|
||||
- Kunin, Vladimir
|
||||
- Lassiter, Denis (a.k.a [@delassiter](https://github.com/delassiter))
|
||||
- Lassiter, Dennis
|
||||
- Lazcano, Federico
|
||||
- Lucas, Jonathan
|
||||
- Malik, Remie
|
||||
- Mantel, Ina
|
||||
- Martin, Pierre (a.k.a [@Worty](https://github.com/worty-syn))
|
||||
- Melchiorre, Romain
|
||||
- Mindêllo de Andrade, Lucas (a.k.a [@rokam](https://www.github.com/rokam))
|
||||
- Mozart de Oliveira, Eduardo (a.k.a [@eduardomozart](https://github.com/eduardomozart))
|
||||
- Raenker, Martin
|
||||
@@ -125,12 +113,10 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Seki, Shoji
|
||||
- Shilov, Vladimir
|
||||
- Stetina, Pavel (a.k.a [@Stetinac](https://github.com/Stetinac))
|
||||
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya-stukalov))
|
||||
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
|
||||
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
|
||||
- Toraya, Chairat (a.k.a [@Kyokito1412](https://github.com/Kyokito1412))
|
||||
- Tulio, Marco
|
||||
- Turrubiates, Miguel
|
||||
- Višnjić, Aldin (a.k.a[@viliald](https://github.com/viliald))
|
||||
- Vlk, Karel (a.k.a [@vlk-charles](https://www.github.com/vlk-charles))
|
||||
|
||||
### Aliases
|
||||
@@ -152,6 +138,4 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- [ITOMIG](https://www.itomig.de/)
|
||||
- [Pimkie](https://www.pimkie.com/)
|
||||
- [Super-Visions](https://www.super-visions.com/)
|
||||
- [Defence Tech Cyber Security - Malware Lab](https://github.com/DefenceTechSecurity)
|
||||
- Orange Cyberdefense
|
||||
- MipihSIB
|
||||
|
||||
|
||||
362
addons/userrights/userrightsmatrix.class.inc.php
Normal file
@@ -0,0 +1,362 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2024 Combodo SAS
|
||||
//
|
||||
// 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/>
|
||||
|
||||
/**
|
||||
* UserRightsMatrix (User management Module)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
class UserRightsMatrixClassGrant extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_ur_matrixclasses",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("class", array("allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
}
|
||||
|
||||
class UserRightsMatrixClassStimulusGrant extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_ur_matrixclassesstimulus",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("class", array("allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
}
|
||||
|
||||
class UserRightsMatrixAttributeGrant extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_ur_matrixattributes",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("class", array("allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class UserRightsMatrix extends UserRightsAddOnAPI
|
||||
{
|
||||
static public $m_aActionCodes = array(
|
||||
UR_ACTION_READ => 'read',
|
||||
UR_ACTION_MODIFY => 'modify',
|
||||
UR_ACTION_DELETE => 'delete',
|
||||
UR_ACTION_BULK_READ => 'bulk read',
|
||||
UR_ACTION_BULK_MODIFY => 'bulk modify',
|
||||
UR_ACTION_BULK_DELETE => 'bulk delete',
|
||||
);
|
||||
|
||||
// Installation: create the very first user
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
// Maybe we should check that no other user with userid == 0 exists
|
||||
$oUser = new UserLocal();
|
||||
$oUser->Set('login', $sAdminUser);
|
||||
$oUser->Set('password', $sAdminPwd);
|
||||
$oUser->Set('contactid', 1); // one is for root !
|
||||
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
|
||||
|
||||
// Now record the admin user object
|
||||
$iUserId = $oUser->DBInsertNoReload();
|
||||
$this->SetupUser($iUserId, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsAdministrator($oUser)
|
||||
{
|
||||
return ($oUser->GetKey() == 1);
|
||||
}
|
||||
|
||||
public function IsPortalUser($oUser)
|
||||
{
|
||||
return ($oUser->GetKey() == 1);
|
||||
}
|
||||
|
||||
// Deprecated - create a new module !
|
||||
public function Setup()
|
||||
{
|
||||
// Users must be added manually
|
||||
// This procedure will then update the matrix when a new user is found or a new class/attribute appears
|
||||
$oUserSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT User"));
|
||||
while ($oUser = $oUserSet->Fetch())
|
||||
{
|
||||
$this->SetupUser($oUser->GetKey());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function SetupUser($iUserId, $bNewUser = false)
|
||||
{
|
||||
foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory)
|
||||
{
|
||||
foreach (MetaModel::GetClasses($sCategory) as $sClass)
|
||||
{
|
||||
foreach (self::$m_aActionCodes as $iActionCode => $sAction)
|
||||
{
|
||||
if ($bNewUser)
|
||||
{
|
||||
$bAddCell = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND userid = $iUserId"));
|
||||
$bAddCell = ($oSet->Count() < 1);
|
||||
}
|
||||
if ($bAddCell)
|
||||
{
|
||||
// Create a new entry
|
||||
$oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassGrant");
|
||||
$oMyClassGrant->Set("userid", $iUserId);
|
||||
$oMyClassGrant->Set("class", $sClass);
|
||||
$oMyClassGrant->Set("action", $sAction);
|
||||
$oMyClassGrant->Set("permission", "yes");
|
||||
$iId = $oMyClassGrant->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus)
|
||||
{
|
||||
if ($bNewUser)
|
||||
{
|
||||
$bAddCell = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND userid = $iUserId"));
|
||||
$bAddCell = ($oSet->Count() < 1);
|
||||
}
|
||||
if ($bAddCell)
|
||||
{
|
||||
// Create a new entry
|
||||
$oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassStimulusGrant");
|
||||
$oMyClassGrant->Set("userid", $iUserId);
|
||||
$oMyClassGrant->Set("class", $sClass);
|
||||
$oMyClassGrant->Set("stimulus", $sStimulusCode);
|
||||
$oMyClassGrant->Set("permission", "yes");
|
||||
$iId = $oMyClassGrant->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode)
|
||||
{
|
||||
if ($bNewUser)
|
||||
{
|
||||
$bAddCell = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND userid = $iUserId"));
|
||||
$bAddCell = ($oSet->Count() < 1);
|
||||
}
|
||||
if ($bAddCell)
|
||||
{
|
||||
foreach (array('read', 'modify') as $sAction)
|
||||
{
|
||||
// Create a new entry
|
||||
$oMyAttGrant = MetaModel::NewObject("UserRightsMatrixAttributeGrant");
|
||||
$oMyAttGrant->Set("userid", $iUserId);
|
||||
$oMyAttGrant->Set("class", $sClass);
|
||||
$oMyAttGrant->Set("attcode", $sAttCode);
|
||||
$oMyAttGrant->Set("action", $sAction);
|
||||
$oMyAttGrant->Set("permission", "yes");
|
||||
$iId = $oMyAttGrant->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
// Create the "My Bookmarks" menu item (parent_id = 0, rank = 6)
|
||||
if ($bNewUser)
|
||||
{
|
||||
$bAddMenu = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT menuNode WHERE type = 'user' AND parent_id = 0 AND user_id = $iUserId"));
|
||||
$bAddMenu = ($oSet->Count() < 1);
|
||||
}
|
||||
if ($bAddMenu)
|
||||
{
|
||||
$oMenu = MetaModel::NewObject('menuNode');
|
||||
$oMenu->Set('type', 'user');
|
||||
$oMenu->Set('parent_id', 0); // It's a toplevel entry
|
||||
$oMenu->Set('rank', 6); // Located just above the Admin Tools section (=7)
|
||||
$oMenu->Set('name', 'My Bookmarks');
|
||||
$oMenu->Set('label', 'My Favorite Items');
|
||||
$oMenu->Set('hyperlink', 'UI.php');
|
||||
$oMenu->Set('template', '<p></p><p></p><p style="text-align:center; font-family:Georgia, Times, serif; font-size:32px;">My bookmarks</p><p style="text-align:center; font-family:Georgia, Times, serif; font-size:14px;"><i>This section contains my most favorite search results</i></p>');
|
||||
$oMenu->Set('user_id', $iUserId);
|
||||
$oMenu->DBInsert();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
public function Init()
|
||||
{
|
||||
// Could be loaded in a shared memory (?)
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
|
||||
{
|
||||
$oNullFilter = new DBObjectSearch($sClass);
|
||||
return $oNullFilter;
|
||||
}
|
||||
|
||||
public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
|
||||
{
|
||||
if (!array_key_exists($iActionCode, self::$m_aActionCodes))
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND userid = '{$oUser->GetKey()}'"));
|
||||
if ($oSet->Count() < 1)
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
|
||||
$oGrantRecord = $oSet->Fetch();
|
||||
switch ($oGrantRecord->Get('permission'))
|
||||
{
|
||||
case 'yes':
|
||||
$iRetCode = UR_ALLOWED_YES;
|
||||
break;
|
||||
case 'no':
|
||||
default:
|
||||
$iRetCode = UR_ALLOWED_NO;
|
||||
break;
|
||||
}
|
||||
return $iRetCode;
|
||||
}
|
||||
|
||||
public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null)
|
||||
{
|
||||
if (!array_key_exists($iActionCode, self::$m_aActionCodes))
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND action = '$sAction' AND userid = '{$oUser->GetKey()}'"));
|
||||
if ($oSet->Count() < 1)
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
|
||||
$oGrantRecord = $oSet->Fetch();
|
||||
switch ($oGrantRecord->Get('permission'))
|
||||
{
|
||||
case 'yes':
|
||||
$iRetCode = UR_ALLOWED_YES;
|
||||
break;
|
||||
case 'no':
|
||||
default:
|
||||
$iRetCode = UR_ALLOWED_NO;
|
||||
break;
|
||||
}
|
||||
return $iRetCode;
|
||||
}
|
||||
|
||||
public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, $oInstanceSet = null)
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND userid = '{$oUser->GetKey()}'"));
|
||||
if ($oSet->Count() < 1)
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
|
||||
$oGrantRecord = $oSet->Fetch();
|
||||
switch ($oGrantRecord->Get('permission'))
|
||||
{
|
||||
case 'yes':
|
||||
$iRetCode = UR_ALLOWED_YES;
|
||||
break;
|
||||
case 'no':
|
||||
default:
|
||||
$iRetCode = UR_ALLOWED_NO;
|
||||
break;
|
||||
}
|
||||
return $iRetCode;
|
||||
}
|
||||
|
||||
public function FlushPrivileges()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
UserRights::SelectModule('UserRightsMatrix');
|
||||
|
||||
?>
|
||||
78
addons/userrights/userrightsnull.class.inc.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2024 Combodo SAS
|
||||
//
|
||||
// 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/>
|
||||
|
||||
/**
|
||||
* UserRightsNull
|
||||
* User management Module - say Yeah! to everything
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class UserRightsNull extends UserRightsAddOnAPI
|
||||
{
|
||||
// Installation: create the very first user
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsAdministrator($oUser)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsPortalUser($oUser)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function Init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
|
||||
{
|
||||
$oNullFilter = new DBObjectSearch($sClass);
|
||||
return $oNullFilter;
|
||||
}
|
||||
|
||||
public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
|
||||
{
|
||||
return UR_ALLOWED_YES;
|
||||
}
|
||||
|
||||
public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, $oInstanceSet = null)
|
||||
{
|
||||
return UR_ALLOWED_YES;
|
||||
}
|
||||
|
||||
public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null)
|
||||
{
|
||||
return UR_ALLOWED_YES;
|
||||
}
|
||||
|
||||
public function FlushPrivileges()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
UserRights::SelectModule('UserRightsNull');
|
||||
|
||||
?>
|
||||
1059
addons/userrights/userrightsprofile.db.class.inc.php
Normal file
1217
addons/userrights/userrightsprojection.class.inc.php
Normal file
25
application/ajaxwebpage.class.inc.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
// cannot notify depreciation for now as this is still load in autoloader
|
||||
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader');
|
||||
use Combodo\iTop\Application\WebPage\AjaxPage;
|
||||
|
||||
/**
|
||||
* Class ajax_page
|
||||
*
|
||||
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to AjaxPage
|
||||
*/
|
||||
class ajax_page extends AjaxPage
|
||||
{
|
||||
function __construct($s_title)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('ajax_page is deprecated. Please use AjaxPage instead');
|
||||
parent::__construct($s_title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,31 +195,16 @@ class ApplicationContext
|
||||
/**
|
||||
* Returns the context as string with the format name1=value1&name2=value2....
|
||||
* @return string The context as a string to be appended to an href property
|
||||
*
|
||||
*/
|
||||
public function GetForLink(bool $bWithLeadingAmpersand = false)
|
||||
public function GetForLink()
|
||||
{
|
||||
// If there are no parameters, return an empty string
|
||||
if(empty($this->aValues)){
|
||||
return '';
|
||||
}
|
||||
|
||||
// Build the query string with ampersand separated parameters
|
||||
$aParams = array();
|
||||
foreach($this->aValues as $sName => $sValue)
|
||||
{
|
||||
$aParams[] = "c[$sName]".'='.urlencode($sValue);
|
||||
}
|
||||
$sReturnValue = implode('&', $aParams);
|
||||
|
||||
// add the leading ampersand if requested
|
||||
if($bWithLeadingAmpersand){
|
||||
$sReturnValue = '&' . $sReturnValue;
|
||||
}
|
||||
|
||||
return $sReturnValue;
|
||||
return implode("&", $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.0.0 N°2534 - dashboard: bug with autorefresh that deactivates filtering on organisation
|
||||
* Returns the params as c[menu]:..., c[org_id]:....
|
||||
@@ -397,7 +382,7 @@ class ApplicationContext
|
||||
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
|
||||
if (utils::StrLen($sUrl) > 0) {
|
||||
if ($bWithNavigationContext) {
|
||||
return $sUrl.$oAppContext->GetForLink(true);
|
||||
return $sUrl."&".$oAppContext->GetForLink();
|
||||
} else {
|
||||
return $sUrl;
|
||||
}
|
||||
|
||||
@@ -1158,6 +1158,61 @@ class JSButtonItem extends JSPopupMenuItem
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any iTopWebPage
|
||||
*
|
||||
* There are 3 places where content can be added:
|
||||
*
|
||||
* * The north pane: (normaly empty/hidden) at the top of the page, spanning the whole
|
||||
* width of the page
|
||||
* * The south pane: (normaly empty/hidden) at the bottom of the page, spanning the whole
|
||||
* width of the page
|
||||
* * The admin banner (two tones gray background) at the left of the global search.
|
||||
* Limited space, use it for short messages
|
||||
*
|
||||
* Each of the methods of this interface is supposed to return the HTML to be inserted at
|
||||
* the specified place and can use the passed iTopWebPage object to add javascript or CSS definitions
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.0
|
||||
* @deprecated 3.0.0 If you need to include:
|
||||
* * JS/CSS files/snippets, use {@see \iBackofficeLinkedScriptsExtension}, {@see \iBackofficeLinkedStylesheetsExtension}, etc instead
|
||||
* * HTML (and optionally JS/CSS), use {@see \iPageUIBlockExtension} to manipulate {@see \Combodo\iTop\Application\UI\Base\UIBlock} instead
|
||||
*/
|
||||
interface iPageUIExtension
|
||||
{
|
||||
/**
|
||||
* Add content to the header of the page
|
||||
*
|
||||
* @api
|
||||
* @param iTopWebPage $oPage The page to insert stuff into.
|
||||
*
|
||||
* @return string The HTML content to add into the page
|
||||
*/
|
||||
public function GetNorthPaneHtml(iTopWebPage $oPage);
|
||||
|
||||
/**
|
||||
* Add content to the footer of the page
|
||||
*
|
||||
* @api
|
||||
* @param iTopWebPage $oPage The page to insert stuff into.
|
||||
*
|
||||
* @return string The HTML content to add into the page
|
||||
*/
|
||||
public function GetSouthPaneHtml(iTopWebPage $oPage);
|
||||
|
||||
/**
|
||||
* Add content to the "admin banner"
|
||||
*
|
||||
* @api
|
||||
* @param iTopWebPage $oPage The page to insert stuff into.
|
||||
*
|
||||
* @return string The HTML content to add into the page
|
||||
*/
|
||||
public function GetBannerHtml(iTopWebPage $oPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any iTopWebPage
|
||||
*
|
||||
@@ -1205,7 +1260,43 @@ interface iPageUIBlockExtension
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend this class instead of iPageUIBlockExtension if you don't need to overload all methods
|
||||
* Extend this class instead of iPageUIExtension if you don't need to overload all methods
|
||||
*
|
||||
* @api
|
||||
* @package UIExtensibilityAPI
|
||||
* @since 2.7.0
|
||||
* @deprecated 3.0.0 use AbstractPageUIBlockExtension instead
|
||||
*/
|
||||
abstract class AbstractPageUIExtension implements iPageUIExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetNorthPaneHtml(iTopWebPage $oPage)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetSouthPaneHtml(iTopWebPage $oPage)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetBannerHtml(iTopWebPage $oPage)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend this class instead of iPageUIExtension if you don't need to overload all methods
|
||||
*
|
||||
* @api
|
||||
* @package UIBlockExtensibilityAPI
|
||||
@@ -1366,23 +1457,6 @@ interface iBackofficeStyleExtension
|
||||
public function GetStyle(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add sass file (SCSS) to the backoffice pages.
|
||||
* example: return "css/setup.scss"
|
||||
*
|
||||
* @api
|
||||
* @package BackofficeUIExtensibilityAPI
|
||||
* @since 3.3.0
|
||||
*/
|
||||
interface iBackofficeSassExtension
|
||||
{
|
||||
/**
|
||||
* @api
|
||||
* @see \iTopWebPage::$a_styles
|
||||
* @return string
|
||||
*/
|
||||
public function GetSass(): string;
|
||||
}
|
||||
/**
|
||||
* Implement this interface to add Dict entries
|
||||
*
|
||||
@@ -1855,41 +1929,76 @@ class RestUtils
|
||||
* @return array of class => list of attributes (see RestResultWithObjects::AddObject that uses it)
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function GetFieldList($sClass, $oData, $sParamName)
|
||||
public static function GetFieldList($sClass, $oData, $sParamName, $bFailIfNotFound = true)
|
||||
{
|
||||
$sFields = self::GetOptionalParam($oData, $sParamName, '*');
|
||||
$aShowFields = array();
|
||||
if ($sFields == '*')
|
||||
{
|
||||
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
$aShowFields[$sClass][] = $sAttCode;
|
||||
}
|
||||
return match($sFields) {
|
||||
'*' => self::GetFieldListForClass($sClass),
|
||||
'*+' => self::GetFieldListForParentClass($sClass),
|
||||
default => self::GetLimitedFieldListForClass($sClass, $sFields, $sParamName, $bFailIfNotFound),
|
||||
};
|
||||
}
|
||||
|
||||
public static function HasRequestedExtendedOutput(string $sFields): bool
|
||||
{
|
||||
return match($sFields) {
|
||||
'*' => false,
|
||||
'*+' => true,
|
||||
default => substr_count($sFields, ':') > 1,
|
||||
};
|
||||
}
|
||||
|
||||
public static function HasRequestedAllOutputFields(string $sFields): bool
|
||||
{
|
||||
return match($sFields) {
|
||||
'*', '*+' => true,
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
|
||||
protected static function GetFieldListForClass(string $sClass): array
|
||||
{
|
||||
return [$sClass => array_keys(MetaModel::ListAttributeDefs($sClass))];
|
||||
}
|
||||
|
||||
protected static function GetFieldListForParentClass(string $sClass): array
|
||||
{
|
||||
$aFieldList = array();
|
||||
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sRefClass) {
|
||||
$aFieldList = array_merge($aFieldList, self::GetFieldListForClass($sRefClass));
|
||||
}
|
||||
elseif ($sFields == '*+')
|
||||
{
|
||||
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sRefClass)
|
||||
{
|
||||
foreach (MetaModel::ListAttributeDefs($sRefClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
$aShowFields[$sRefClass][] = $sAttCode;
|
||||
return $aFieldList;
|
||||
}
|
||||
|
||||
protected static function GetLimitedFieldListForSingleClass(string $sClass, string $sFields, string $sParamName, bool $bFailIfNotFound = true): array
|
||||
{
|
||||
$aFieldList = [$sClass => []];
|
||||
foreach (explode(',', $sFields) as $sAttCode) {
|
||||
$sAttCode = trim($sAttCode);
|
||||
if (($sAttCode == 'id') || (MetaModel::IsValidAttCode($sClass, $sAttCode))) {
|
||||
$aFieldList[$sClass][] = $sAttCode;
|
||||
} else {
|
||||
if ($bFailIfNotFound) {
|
||||
throw new Exception("$sParamName: invalid attribute code '$sAttCode' for class '$sClass'");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (explode(',', $sFields) as $sAttCode)
|
||||
{
|
||||
$sAttCode = trim($sAttCode);
|
||||
if (($sAttCode != 'id') && (!MetaModel::IsValidAttCode($sClass, $sAttCode)))
|
||||
{
|
||||
throw new Exception("$sParamName: invalid attribute code '$sAttCode'");
|
||||
}
|
||||
$aShowFields[$sClass][] = $sAttCode;
|
||||
}
|
||||
return $aFieldList;
|
||||
}
|
||||
|
||||
protected static function GetLimitedFieldListForClass(string $sClass, string $sFields, string $sParamName, bool $bFailIfNotFound = true): array
|
||||
{
|
||||
if (!str_contains($sFields, ':')) {
|
||||
return self::GetLimitedFieldListForSingleClass($sClass, $sFields, $sParamName, $bFailIfNotFound);
|
||||
}
|
||||
|
||||
return $aShowFields;
|
||||
$aFieldList = [];
|
||||
$aFieldListParts = explode(';', $sFields);
|
||||
foreach ($aFieldListParts as $sClassFields) {
|
||||
list($sSubClass, $sSubClassFields) = explode(':', $sClassFields);
|
||||
$aFieldList = array_merge($aFieldList, self::GetLimitedFieldListForSingleClass(trim($sSubClass), trim($sSubClassFields), $sParamName, $bFailIfNotFound));
|
||||
}
|
||||
return $aFieldList;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
8
application/capturewebpage.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader');
|
||||
8
application/clipage.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader');
|
||||
@@ -311,7 +311,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
$sParams .= $sName.'='.urlencode($value).'&'; // Always add a trailing &
|
||||
}
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().$oAppContext->GetForLink(true).'&a=1';
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink().'&a=1';
|
||||
$oPage->add_early_script(<<<JS
|
||||
if (!sessionStorage.getItem('$sSessionStorageKey'))
|
||||
{
|
||||
@@ -1337,7 +1337,7 @@ HTML
|
||||
}
|
||||
}
|
||||
}
|
||||
$aHeader['friendlyname'] = ['label' => MetaModel::GetName($sClassName)];
|
||||
|
||||
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
|
||||
$sColLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx) : $sAttCodeEx;
|
||||
|
||||
@@ -1358,7 +1358,6 @@ HTML
|
||||
$aRow = [];
|
||||
foreach ($aAuthorizedClasses as $sAlias => $sClassName) {
|
||||
$oObj = $aObjects[$sAlias];
|
||||
$aRow["friendlyname"] = $oObj->Get('friendlyname');
|
||||
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
|
||||
if (is_null($oObj)) {
|
||||
$aRow[$oAttDef->GetCode()] = '';
|
||||
@@ -2931,11 +2930,11 @@ JS
|
||||
$sStatesSelection .= '</select>';
|
||||
$sStatesSelection .= '<input type="hidden" id="obj_state_orig" name="obj_state_orig" value="'.$this->GetState().'"/>';
|
||||
$oPage->add_ready_script(<<<JS
|
||||
$('.state_select_{$this->m_iFormId}').on('change', function() {
|
||||
$('.state_select_{$this->m_iFormId}').change( function() {
|
||||
if ($('#obj_state_orig').val() != $(this).val()) {
|
||||
$('.state_select_{$this->m_iFormId}').val($(this).val());
|
||||
$('#form_{$this->m_iFormId}').data('force_submit', true);
|
||||
$('#form_{$this->m_iFormId}').trigger('submit');
|
||||
$('#form_{$this->m_iFormId}').submit();
|
||||
}
|
||||
});
|
||||
JS
|
||||
@@ -3072,7 +3071,7 @@ JS
|
||||
$oPage->add($oAppContext->GetForForm());
|
||||
|
||||
// Hook the cancel button via jQuery so that it can be unhooked easily as well if needed
|
||||
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.$oAppContext->GetForLink(true);
|
||||
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.'&'.$oAppContext->GetForLink();
|
||||
|
||||
$sCancelButtonOnClickScript = "let fOnClick{$this->m_iFormId}CancelButton = ";
|
||||
if(isset($aExtraParams['js_handlers']['cancel_button_on_click'])){
|
||||
@@ -3080,7 +3079,7 @@ JS
|
||||
} else {
|
||||
$sCancelButtonOnClickScript .= "function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)};";
|
||||
}
|
||||
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click.navigation.itop', fOnClick{$this->m_iFormId}CancelButton);";
|
||||
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click', fOnClick{$this->m_iFormId}CancelButton);";
|
||||
$oPage->add_ready_script($sCancelButtonOnClickScript);
|
||||
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
@@ -3925,7 +3924,7 @@ HTML;
|
||||
public static function GetShortcutActions($sFinalClass)
|
||||
{
|
||||
$sShortcutActions = MetaModel::GetConfig()->Get('shortcut_actions');
|
||||
$aShortcutActions = array_map('trim', explode(',', $sShortcutActions));
|
||||
$aShortcutActions = explode(',', $sShortcutActions);
|
||||
|
||||
return $aShortcutActions;
|
||||
}
|
||||
@@ -4313,15 +4312,24 @@ HTML;
|
||||
|
||||
case 'Image':
|
||||
$value = null;
|
||||
$aDimensions = null;
|
||||
$oImage = utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents');
|
||||
$oImage = $oImage->ResizeImageToFit(
|
||||
$oAttDef->Get('storage_max_width'),
|
||||
$oAttDef->Get('storage_max_height'),
|
||||
$aDimensions
|
||||
);
|
||||
if (is_null($aDimensions)) {
|
||||
IssueLog::Warning($sClass . ':' . $this->GetKey() . '/' . $sAttCode . ': Image could not be resized. Mimetype: ' . $oImage->GetMimeType() . ', filename: ' . $oImage->GetFileName());
|
||||
if (!is_null($oImage->GetData()))
|
||||
{
|
||||
$aSize = utils::GetImageSize($oImage->GetData());
|
||||
if (is_array($aSize) && $aSize[0] > 0 && $aSize[1] > 0)
|
||||
{
|
||||
$oImage = utils::ResizeImageToFit(
|
||||
$oImage,
|
||||
$aSize[0],
|
||||
$aSize[1],
|
||||
$oAttDef->Get('storage_max_width'),
|
||||
$oAttDef->Get('storage_max_height')
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Warning($sClass . ':' . $this->GetKey() . '/' . $sAttCode . ': Image could not be resized. Mimetype: ' . $oImage->GetMimeType() . ', filename: ' . $oImage->GetFileName());
|
||||
}
|
||||
}
|
||||
$aOtherData = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null, 'raw_data');
|
||||
if (is_array($aOtherData))
|
||||
@@ -4566,6 +4574,28 @@ 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));
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
public function DBUpdate()
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
@@ -4659,6 +4689,25 @@ HTML;
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
public function IsModified()
|
||||
{
|
||||
if (parent::IsModified())
|
||||
@@ -4810,6 +4859,110 @@ HTML;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special display where the case log uses the whole "screen" at the bottom of the "Properties" tab
|
||||
*
|
||||
* @param WebPage $oPage
|
||||
* @param string $sAttCode
|
||||
* @param string $sComment
|
||||
* @param string $sPrefix
|
||||
* @param bool $bEditMode
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
* @throws \Exception
|
||||
* @deprecated 3.0.0, will be removed in 3.1.0
|
||||
*/
|
||||
public function DisplayCaseLog(WebPage $oPage, $sAttCode, $sComment = '', $sPrefix = '', $bEditMode = false)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
$oPage->SetCurrentTab('UI:PropertiesTab');
|
||||
$sClass = get_class($this);
|
||||
|
||||
if ($this->IsNew()) {
|
||||
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
|
||||
} else {
|
||||
$iFlags = $this->GetAttributeFlags($sAttCode);
|
||||
}
|
||||
|
||||
if ($iFlags & OPT_ATT_HIDDEN) {
|
||||
// The case log is hidden do nothing
|
||||
} else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$sAttDefClass = get_class($oAttDef);
|
||||
$sAttLabel = $oAttDef->GetLabel();
|
||||
$sAttMetaDataLabel = utils::HtmlEntities($sAttLabel);
|
||||
$sAttMetaDataFlagHidden = (($iFlags & OPT_ATT_HIDDEN) === OPT_ATT_HIDDEN) ? 'true' : 'false';
|
||||
$sAttMetaDataFlagReadOnly = (($iFlags & OPT_ATT_READONLY) === OPT_ATT_READONLY) ? 'true' : 'false';
|
||||
$sAttMetaDataFlagMandatory = (($iFlags & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY) ? 'true' : 'false';
|
||||
$sAttMetaDataFlagMustChange = (($iFlags & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE) ? 'true' : 'false';
|
||||
$sAttMetaDataFlagMustPrompt = (($iFlags & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT) ? 'true' : 'false';
|
||||
$sAttMetaDataFlagSlave = (($iFlags & OPT_ATT_SLAVE) === OPT_ATT_SLAVE) ? 'true' : 'false';
|
||||
|
||||
$sInputId = $this->m_iFormId.'_'.$sAttCode;
|
||||
|
||||
if ((!$bEditMode) || ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)))
|
||||
{
|
||||
// Check if the attribute is not read-only because of a synchro...
|
||||
if ($iFlags & OPT_ATT_SLAVE)
|
||||
{
|
||||
$aReasons = array();
|
||||
$sTip = '';
|
||||
foreach($aReasons as $aRow) {
|
||||
$sDescription = utils::EscapeHtml($aRow['description']);
|
||||
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
|
||||
$sTip .= "<div class=\"synchro-source\">";
|
||||
$sTip .= "<div class=\"synchro-source-title\">Synchronized with {$aRow['name']}</div>";
|
||||
$sTip .= "<div class=\"synchro-source-description\">$sDescription</div>";
|
||||
}
|
||||
$sTip = addslashes($sTip);
|
||||
$oPage->add_ready_script("$('#synchro_$sInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
|
||||
// Attribute is read-only
|
||||
$sHTMLValue = $this->GetAsHTML($sAttCode);
|
||||
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.utils::EscapeHtml($this->GetEditValue($sAttCode)).'"/>';
|
||||
$aFieldsMap[$sAttCode] = $sInputId;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sValue = $this->Get($sAttCode);
|
||||
$sDisplayValue = $this->GetEditValue($sAttCode);
|
||||
$aArgs = array('this' => $this, 'formPrefix' => $sPrefix);
|
||||
|
||||
$sCommentAsHtml = ($sComment != '') ? '<span>'.$sComment.'</span><br/>' : '';
|
||||
$sFieldAsHtml = self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs);
|
||||
$sHTMLValue = <<<HTML
|
||||
<div class="field_data">
|
||||
<div class="field_value">
|
||||
$sCommentAsHtml
|
||||
$sFieldAsHtml
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$aFieldsMap[$sAttCode] = $sInputId;
|
||||
}
|
||||
|
||||
$oPage->add(<<<HTML
|
||||
<fieldset>
|
||||
<legend>{$sAttLabel}</legend>
|
||||
<div class="field_container field_large" data-attribute-code="{$sAttCode}" data-attribute-type="{$sAttDefClass}" data-attribute-label="{$sAttMetaDataLabel}"
|
||||
data-attribute-flag-hidden="{$sAttMetaDataFlagHidden}" data-attribute-flag-read-only="{$sAttMetaDataFlagReadOnly}" data-attribute-flag-mandatory="{$sAttMetaDataFlagMandatory}"
|
||||
data-attribute-flag-must-change="{$sAttMetaDataFlagMustChange}" data-attribute-flag-must-prompt="{$sAttMetaDataFlagMustPrompt}" data-attribute-flag-slave="{$sAttMetaDataFlagSlave}">
|
||||
{$sHTMLValue}
|
||||
</div>
|
||||
</fieldset>
|
||||
HTML
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special display where the case log uses the whole "screen" at the bottom of the "Properties" tab
|
||||
*
|
||||
|
||||
8
application/csvpage.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader');
|
||||
@@ -524,7 +524,9 @@ EOF
|
||||
*/
|
||||
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
|
||||
{
|
||||
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
|
||||
if (!array_key_exists('dashboard_div_id', $aExtraParams)) {
|
||||
$aExtraParams['dashboard_div_id'] = utils::Sanitize($this->GetId(), '', 'element_identifier');
|
||||
}
|
||||
|
||||
/** @var \DashboardLayoutMultiCol $oLayout */
|
||||
$oLayout = new $this->sLayoutClass();
|
||||
@@ -1050,7 +1052,7 @@ EOF
|
||||
$sSelectorHtml .= '</div>';
|
||||
|
||||
$sFile = addslashes($this->GetDefinitionFile());
|
||||
$sReloadURL = json_encode($this->GetReloadURL());
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
|
||||
$bFromDashboardPage = isset($aAjaxParams['from_dashboard_page']) ? isset($aAjaxParams['from_dashboard_page']) : false;
|
||||
if ($bFromDashboardPage) {
|
||||
@@ -1139,6 +1141,7 @@ JS
|
||||
->AddCSSClass('ibo-action-button');
|
||||
|
||||
$oToolbar->AddSubBlock($oActionButton);
|
||||
|
||||
$aActions = array();
|
||||
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
|
||||
$sJSExtraParams = json_encode($aExtraParams);
|
||||
@@ -1163,7 +1166,7 @@ JS
|
||||
$oToolbar->AddSubBlock($oActionButton)
|
||||
->AddSubBlock($oActionsMenu);
|
||||
|
||||
$sReloadURL = json_encode($this->GetReloadURL());
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
function EditDashboard(sId, sDashboardFile, aExtraParams)
|
||||
@@ -1270,7 +1273,7 @@ EOF
|
||||
$sTitle = json_encode($this->sTitle);
|
||||
$sFile = json_encode($this->GetDefinitionFile());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
||||
$sReloadURL = json_encode($this->GetReloadURL());
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
|
||||
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
|
||||
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
|
||||
|
||||
@@ -1967,10 +1967,7 @@ class DashletHeaderStatic extends Dashlet
|
||||
$sIcon = $this->aProperties['icon'];
|
||||
|
||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$sIconPath = '';
|
||||
if (Utils::IsNotNullOrEmptyString($sIcon)) {
|
||||
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
|
||||
}
|
||||
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
|
||||
|
||||
return DashletFactory::MakeForDashletHeaderStatic($this->oModelReflection->DictString($sTitle), $sIconPath);
|
||||
}
|
||||
@@ -1984,7 +1981,6 @@ class DashletHeaderStatic extends Dashlet
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = $this->oModelReflection->GetIconSelectionField('icon', Dict::S('UI:DashletHeaderStatic:Prop-Icon'), $this->aProperties['icon']);
|
||||
$oField->AddAllowedValue(['value' => '', 'label' => Dict::S('UI:DashletIcon:None'), 'icon' => '']);
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
|
||||
@@ -2097,10 +2093,7 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
|
||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$sIconPath = '';
|
||||
if (Utils::IsNotNullOrEmptyString($sIcon)) {
|
||||
$sIconPath = $oIconSelect->MakeFileUrl($sIcon);
|
||||
}
|
||||
$sIconPath = $oIconSelect->MakeFileUrl($sIcon);
|
||||
|
||||
$aValues = $this->GetValues();
|
||||
if (count($aValues) > 0) {
|
||||
@@ -2145,7 +2138,7 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$iCount = $oSet->Count();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($oFilter->serialize());
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($oFilter->serialize());
|
||||
$oSubTitle->AddHtml('<a class="summary" href="'.$sHyperlink.'">'.Dict::Format(str_replace('_', ':', $sSubtitle), $iCount).'</a>');
|
||||
|
||||
return $oPanel;
|
||||
@@ -2230,7 +2223,6 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = $this->oModelReflection->GetIconSelectionField('icon', Dict::S('UI:DashletHeaderDynamic:Prop-Icon'), $this->aProperties['icon']);
|
||||
$oField->AddAllowedValue(['value' => '', 'label' => Dict::S('UI:DashletIcon:None'), 'icon' => '']);
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerTextField('subtitle', Dict::S('UI:DashletHeaderDynamic:Prop-Subtitle'), $this->aProperties['subtitle']);
|
||||
|
||||
@@ -111,6 +111,7 @@
|
||||
<menu id="UserManagement" xsi:type="TemplateMenuNode" _delta="define">
|
||||
<rank>10</rank>
|
||||
<parent>AdminTools</parent>
|
||||
<template_file/>
|
||||
</menu>
|
||||
<menu id="UserAccountsMenu" xsi:type="OQLMenuNode" _delta="define">
|
||||
<rank>11</rank>
|
||||
@@ -140,6 +141,7 @@
|
||||
<menu id="Queries" xsi:type="TemplateMenuNode" _delta="define">
|
||||
<rank>30</rank>
|
||||
<parent>AdminTools</parent>
|
||||
<template_file/>
|
||||
</menu>
|
||||
<menu id="RunQueriesMenu" xsi:type="WebPageMenuNode" _delta="define">
|
||||
<rank>31</rank>
|
||||
@@ -186,6 +188,7 @@
|
||||
<menu id="Integrations" xsi:type="TemplateMenuNode" _delta="define">
|
||||
<rank>50</rank>
|
||||
<parent>ConfigurationTools</parent>
|
||||
<template_file/>
|
||||
</menu>
|
||||
<menu id="DataSources" xsi:type="OQLMenuNode" _delta="define">
|
||||
<rank>20</rank>
|
||||
|
||||
880
application/datatable.class.inc.php
Normal file
@@ -0,0 +1,880 @@
|
||||
<?php
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
|
||||
/**
|
||||
* Copyright (C) 2013-2024 Combodo SAS
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @deprecated 3.0.0 use Combodo\iTop\Application\UI\Base\Component\DataTable\Datatable
|
||||
*/
|
||||
|
||||
class DataTable
|
||||
{
|
||||
protected $iListId; // Unique ID inside the web page
|
||||
/** @var string */
|
||||
private $sDatatableContainerId;
|
||||
protected $sTableId; // identifier for saving the settings (combined with the class aliases)
|
||||
protected $oSet; // The set of objects to display
|
||||
protected $aClassAliases; // The aliases (alias => class) inside the set
|
||||
protected $iNbObjects; // Total number of objects in the set
|
||||
protected $bUseCustomSettings; // Whether or not the current display uses custom settings
|
||||
protected $oDefaultSettings; // the default settings for displaying such a list
|
||||
protected $bShowObsoleteData;
|
||||
|
||||
/**
|
||||
* @param string $iListId Unique ID for this div/table in the page
|
||||
* @param DBObjectSet $oSet The set of data to display
|
||||
* @param array$aClassAliases The list of classes/aliases to be displayed in this set $sAlias => $sClassName
|
||||
* @param string $sTableId A string (or null) identifying this table in order to persist its settings
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use Combodo\iTop\Application\UI\Base\Component\DataTable\Datatable');
|
||||
$this->iListId = utils::GetSafeId($iListId); // Make a "safe" ID for jQuery
|
||||
$this->sDatatableContainerId = 'datatable_'.utils::GetSafeId($iListId);
|
||||
$this->oSet = $oSet;
|
||||
$this->aClassAliases = $aClassAliases;
|
||||
$this->sTableId = $sTableId;
|
||||
$this->iNbObjects = $oSet->Count();
|
||||
$this->bUseCustomSettings = false;
|
||||
$this->oDefaultSettings = null;
|
||||
$this->bShowObsoleteData = $oSet->GetShowObsoleteData();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DataTableSettings $oSettings
|
||||
* @param $bActionsMenu
|
||||
* @param $sSelectMode
|
||||
* @param $bViewLink
|
||||
* @param $aExtraParams
|
||||
*
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function Display(WebPage $oPage, DataTableSettings $oSettings, $bActionsMenu, $sSelectMode, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$this->oDefaultSettings = $oSettings;
|
||||
|
||||
// Identified tables can have their own specific settings
|
||||
$oCustomSettings = DataTableSettings::GetTableSettings($this->aClassAliases, $this->sTableId);
|
||||
|
||||
if ($oCustomSettings != null)
|
||||
{
|
||||
// Custom settings overload the default ones
|
||||
$this->bUseCustomSettings = true;
|
||||
if ($this->oDefaultSettings->iDefaultPageSize == 0)
|
||||
{
|
||||
$oCustomSettings->iDefaultPageSize = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oCustomSettings = $oSettings;
|
||||
}
|
||||
|
||||
if ($oCustomSettings->iDefaultPageSize > 0)
|
||||
{
|
||||
$this->oSet->SetLimit($oCustomSettings->iDefaultPageSize);
|
||||
}
|
||||
$this->oSet->SetOrderBy($oCustomSettings->GetSortOrder());
|
||||
|
||||
// Load only the requested columns
|
||||
$aColumnsToLoad = array();
|
||||
foreach($oCustomSettings->aColumns as $sAlias => $aColumnsInfo)
|
||||
{
|
||||
foreach($aColumnsInfo as $sAttCode => $aData)
|
||||
{
|
||||
if ($sAttCode != '_key_')
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
$aColumnsToLoad[$sAlias][] = $sAttCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// See if this column is a must to load
|
||||
$sClass = $this->aClassAliases[$sAlias];
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->AlwaysLoadInTables()) {
|
||||
$aColumnsToLoad[$sAlias][] = $sAttCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->oSet->OptimizeColumnLoad($aColumnsToLoad);
|
||||
|
||||
|
||||
$bToolkitMenu = true;
|
||||
if (isset($aExtraParams['toolkit_menu']))
|
||||
{
|
||||
$bToolkitMenu = (bool) $aExtraParams['toolkit_menu'];
|
||||
}
|
||||
if (UserRights::IsPortalUser())
|
||||
{
|
||||
// Portal users have a limited access to data, for now they can only see what's configured for them
|
||||
$bToolkitMenu = false;
|
||||
}
|
||||
|
||||
return $this->GetAsHTML($oPage, $oCustomSettings->iDefaultPageSize, $oCustomSettings->iDefaultPageSize, 0, $oCustomSettings->aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $iPageSize
|
||||
* @param $iDefaultPageSize
|
||||
* @param $iPageIndex
|
||||
* @param $aColumns
|
||||
* @param $bActionsMenu
|
||||
* @param $bToolkitMenu
|
||||
* @param $sSelectMode
|
||||
* @param $bViewLink
|
||||
* @param $aExtraParams
|
||||
*
|
||||
* @return string
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$sObjectsCount = $this->GetObjectCount($oPage, $sSelectMode);
|
||||
$sPager = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
|
||||
$sActionsMenu = '';
|
||||
$sToolkitMenu = '';
|
||||
if ($bActionsMenu) {
|
||||
$sActionsMenu = $this->GetActionsMenu($oPage, $aExtraParams);
|
||||
}
|
||||
// if ($bToolkitMenu)
|
||||
// {
|
||||
// $sToolkitMenu = $this->GetToolkitMenu($oPage, $aExtraParams);
|
||||
// }
|
||||
|
||||
$sDataTable = $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
|
||||
$sConfigDlg = $this->GetTableConfigDlg($oPage, $aColumns, $bViewLink, $iDefaultPageSize);
|
||||
|
||||
$sHtml = "<table id=\"{$this->sDatatableContainerId}\" class=\"datatable\">";
|
||||
$sHtml .= "<tr><td>";
|
||||
$sHtml .= "<table style=\"width:100%;\">";
|
||||
$sHtml .= "<tr><td class=\"pagination_container\">$sObjectsCount</td><td class=\"menucontainer\">$sToolkitMenu $sActionsMenu</td></tr>";
|
||||
$sHtml .= "<tr>$sPager</tr>";
|
||||
$sHtml .= "</table>";
|
||||
$sHtml .= "</td></tr>";
|
||||
$sHtml .= "<tr><td class=\"datacontents\">$sDataTable</td></tr>";
|
||||
$sHtml .= "</table>\n";
|
||||
$oPage->add_at_the_end($sConfigDlg);
|
||||
|
||||
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
|
||||
|
||||
$aOptions = array(
|
||||
'sPersistentId' => '',
|
||||
'sFilter' => $this->oSet->GetFilter()->serialize(),
|
||||
'oColumns' => $aColumns,
|
||||
'sSelectMode' => $sSelectMode,
|
||||
'sViewLink' => ($bViewLink ? 'true' : 'false'),
|
||||
'iNbObjects' => $this->iNbObjects,
|
||||
'iDefaultPageSize' => $iDefaultPageSize,
|
||||
'iPageSize' => $iPageSize,
|
||||
'iPageIndex' => $iPageIndex,
|
||||
'oClassAliases' => $this->aClassAliases,
|
||||
'sTableId' => $this->sTableId,
|
||||
'oExtraParams' => $aExtraParams,
|
||||
'sRenderUrl' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
|
||||
'oRenderParameters' => array('str' => ''), // Forces JSON to encode this as a object...
|
||||
'oDefaultSettings' => array('str' => ''), // Forces JSON to encode this as a object...
|
||||
'oLabels' => array('moveup' => Dict::S('UI:Button:MoveUp'), 'movedown' => Dict::S('UI:Button:MoveDown')),
|
||||
);
|
||||
if($this->oDefaultSettings != null)
|
||||
{
|
||||
$aOptions['oDefaultSettings'] = $this->GetAsHash($this->oDefaultSettings);
|
||||
}
|
||||
$sJSOptions = json_encode($aOptions);
|
||||
$oPage->add_ready_script("$('#{$this->sDatatableContainerId}').datatable($sJSOptions);");
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* When refreshing the body of a paginated table, get the rows of the table (inside the TBODY)
|
||||
* return string The HTML rows to insert inside the <tbody> node
|
||||
*/
|
||||
public function GetAsHTMLTableRows(WebPage $oPage, $iPageSize, $aColumns, $sSelectMode, $bViewLink, $aExtraParams)
|
||||
{
|
||||
if ($iPageSize < 1)
|
||||
{
|
||||
$iPageSize = -1; // convention: no pagination
|
||||
}
|
||||
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
|
||||
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
|
||||
|
||||
$sHtml = '';
|
||||
foreach($aValues as $aRow)
|
||||
{
|
||||
$sHtml .= $oPage->GetTableRow($aRow, $aAttribs);
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $sSelectMode
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function GetObjectCount(WebPage $oPage, $sSelectMode)
|
||||
{
|
||||
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
|
||||
{
|
||||
$sHtml = '<div class="pagination_objcount">'.Dict::Format('UI:Pagination:HeaderSelection', '<span id="total">'.$this->iNbObjects.'</span>', '<span class="selectedCount">0</span>').'</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml = '<div class="pagination_objcount">'.Dict::Format('UI:Pagination:HeaderNoSelection', '<span id="total">'.$this->iNbObjects.'</span>').'</div>';
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $iPageSize
|
||||
* @param $iDefaultPageSize
|
||||
* @param $iPageIndex
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function GetPager(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex)
|
||||
{
|
||||
$sHtml = '';
|
||||
if ($iPageSize < 1) // Display all
|
||||
{
|
||||
$sPagerStyle = 'style="display:none"'; // no limit: display the full table, so hide the "pager" UI
|
||||
// WARNING: mPDF does not take the "display" style into account
|
||||
// when applied to a <td> or a <table> tag, so make sure you apply this to a div
|
||||
}
|
||||
else
|
||||
{
|
||||
$sPagerStyle = '';
|
||||
}
|
||||
|
||||
$sCombo = '<select class="pagesize">';
|
||||
if($iPageSize < 1)
|
||||
{
|
||||
$sCombo .= "<option selected=\"selected\" value=\"-1\">".Dict::S('UI:Pagination:All')."</option>";
|
||||
}
|
||||
else
|
||||
{
|
||||
for($iPage = 1; $iPage < 5; $iPage++)
|
||||
{
|
||||
$iNbItems = $iPage * $iDefaultPageSize;
|
||||
$sSelected = ($iNbItems == $iPageSize) ? 'selected="selected"' : '';
|
||||
$sCombo .= "<option $sSelected value=\"$iNbItems\">$iNbItems</option>";
|
||||
}
|
||||
$sCombo .= "<option value=\"-1\">".Dict::S('UI:Pagination:All')."</option>";
|
||||
}
|
||||
|
||||
$sCombo .= '</select>';
|
||||
|
||||
$sPages = Dict::S('UI:Pagination:PagesLabel');
|
||||
$sPageSizeCombo = Dict::Format('UI:Pagination:PageSize', $sCombo);
|
||||
|
||||
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
|
||||
if ($iNbPages == 1)
|
||||
{
|
||||
// No need to display the pager
|
||||
$sPagerStyle = 'style="display:none"';
|
||||
}
|
||||
$aPagesToDisplay = array();
|
||||
for($idx = 0; $idx <= min(4, $iNbPages-1); $idx++)
|
||||
{
|
||||
if ($idx == 0)
|
||||
{
|
||||
$aPagesToDisplay[$idx] = '<span page="0" class="curr_page">1</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPagesToDisplay[$idx] = "<span id=\"gotopage_$idx\" class=\"gotopage\" page=\"$idx\">".(1+$idx)."</span>";
|
||||
}
|
||||
}
|
||||
$iLastPageIdx = $iNbPages - 1;
|
||||
if (!isset($aPagesToDisplay[$iLastPageIdx]))
|
||||
{
|
||||
unset($aPagesToDisplay[$idx - 1]); // remove the last page added to make room for the very last page
|
||||
$aPagesToDisplay[$iLastPageIdx] = "<span id=\"gotopage_$iLastPageIdx\" class=\"gotopage\" page=\"$iLastPageIdx\">... $iNbPages</span>";
|
||||
}
|
||||
$sPagesLinks = implode('', $aPagesToDisplay);
|
||||
$sPagesList = '['.implode(',', array_keys($aPagesToDisplay)).']';
|
||||
|
||||
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$sSelectionMode = ($iNbPages == 1) ? '' : 'positive';
|
||||
$sHtml =
|
||||
<<<EOF
|
||||
<td colspan="2">
|
||||
<div $sPagerStyle>
|
||||
<table id="pager{$this->iListId}" class="pager"><tr>
|
||||
<td>$sPages</td>
|
||||
<td><img src="{$sAppRootUrl}images/first.png" class="first"/>AAAA</td>
|
||||
<td><img src="{$sAppRootUrl}images/prev.png" class="prev"/></td>
|
||||
<td><span id="index">$sPagesLinks</span></td>
|
||||
<td><img src="{$sAppRootUrl}images/next.png" class="next"/></td>
|
||||
<td><img src="{$sAppRootUrl}images/last.png" class="last"/></td>
|
||||
<td>$sPageSizeCombo</td>
|
||||
<td><span id="loading"> </span><input type="hidden" name="selectionMode" value="$sSelectionMode"></input>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
EOF;
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $aExtraParams
|
||||
*
|
||||
* @return string
|
||||
* @throws \ApplicationException
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
* @throws \ReflectionException
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*/
|
||||
protected function GetActionsMenu(WebPage $oPage, $aExtraParams)
|
||||
{
|
||||
$oMenuBlock = new MenuBlock($this->oSet->GetFilter(), 'list');
|
||||
$oBlock = $oMenuBlock->GetRenderContent($oPage, $aExtraParams, $this->iListId);
|
||||
|
||||
return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $aExtraParams
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
|
||||
{
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
$sMenuTitle = Dict::S('UI:ConfigureThisList');
|
||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li aria-label="'.Dict::S('UI:Menu:Toolkit').'"><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
|
||||
|
||||
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
|
||||
$aActions = array(
|
||||
$oMenuItem1->GetUID() => $oMenuItem1->GetMenuItem(),
|
||||
);
|
||||
$this->oSet->Rewind();
|
||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $this->oSet, $aActions, $this->sTableId, $this->iListId);
|
||||
$this->oSet->Rewind();
|
||||
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml = '';
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $aColumns
|
||||
* @param $bViewLink
|
||||
* @param $iDefaultPageSize
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function GetTableConfigDlg(WebPage $oPage, $aColumns, $bViewLink, $iDefaultPageSize)
|
||||
{
|
||||
$sHtml = "<div id=\"datatable_dlg_{$this->iListId}\" style=\"display: none;\">";
|
||||
$sHtml .= "<form onsubmit=\"return false\">";
|
||||
$sChecked = ($this->bUseCustomSettings) ? '' : 'checked';
|
||||
$sHtml .= "<p><input id=\"dtbl_dlg_settings_{$this->iListId}\" type=\"radio\" name=\"settings\" $sChecked value=\"defaults\"><label for=\"dtbl_dlg_settings_{$this->iListId}\"> ".Dict::S('UI:UseDefaultSettings').'</label></p>';
|
||||
$sHtml .= "<fieldset>";
|
||||
$sChecked = ($this->bUseCustomSettings) ? 'checked': '';
|
||||
$sHtml .= "<legend class=\"transparent\"><input id=\"dtbl_dlg_specific_{$this->iListId}\" type=\"radio\" class=\"specific_settings\" name=\"settings\" $sChecked value=\"specific\"><label for=\"dtbl_dlg_specific_{$this->iListId}\"> ".Dict::S('UI:UseSpecificSettings')."</label></legend>";
|
||||
$sHtml .= Dict::S('UI:ColumnsAndSortOrder').'<br/><ul class="sortable_field_list" id="sfl_'.$this->iListId.'"></ul>';
|
||||
|
||||
$sHtml .= '<p>'.Dict::Format('UI:Display_X_ItemsPerPage', '<input type="text" size="4" name="page_size" value="'.$iDefaultPageSize.'">').'</p>';
|
||||
$sHtml .= "</fieldset>";
|
||||
$sHtml .= "<fieldset>";
|
||||
$sSaveChecked = ($this->sTableId != null) ? 'checked' : '';
|
||||
$sCustomDisabled = ($this->sTableId == null) ? 'disabled="disabled" stay-disabled="true" ' : '';
|
||||
$sCustomChecked = ($this->sTableId != null) ? 'checked' : '';
|
||||
$sGenericChecked = ($this->sTableId == null) ? 'checked' : '';
|
||||
$sHtml .= "<legend class=\"transparent\"><input id=\"dtbl_dlg_save_{$this->iListId}\" type=\"checkbox\" $sSaveChecked name=\"save_settings\"><label for=\"dtbl_dlg_save_{$this->iListId}\"> ".Dict::S('UI:UseSavetheSettings')."</label></legend>";
|
||||
$sHtml .= "<p><input id=\"dtbl_dlg_this_list_{$this->iListId}\" type=\"radio\" name=\"scope\" $sCustomChecked $sCustomDisabled value=\"this_list\"><label for=\"dtbl_dlg_this_list_{$this->iListId}\"> ".Dict::S('UI:OnlyForThisList').'</label> ';
|
||||
$sHtml .= "<input id=\"dtbl_dlg_all_{$this->iListId}\" type=\"radio\" name=\"scope\" $sGenericChecked value=\"defaults\"><label for=\"dtbl_dlg_all_{$this->iListId}\"> ".Dict::S('UI:ForAllLists').'</label></p>';
|
||||
$sHtml .= "</fieldset>";
|
||||
$sHtml .= '<table style="width:100%"><tr><td style="text-align:center;">';
|
||||
$sHtml .= '<button type="button" onclick="$(\'#'.$this->sDatatableContainerId.'\').datatable(\'onDlgCancel\'); $(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\')">'.Dict::S('UI:Button:Cancel').'</button>';
|
||||
$sHtml .= '</td><td style="text-align:center;">';
|
||||
$sHtml .= '<button type="submit" onclick="$(\'#'.$this->sDatatableContainerId.'\').datatable(\'onDlgOk\');$(\'#datatable_dlg_'.$this->iListId.'\').dialog(\'close\');">'.Dict::S('UI:Button:Ok').'</button>';
|
||||
$sHtml .= '</td></tr></table>';
|
||||
$sHtml .= "</form>";
|
||||
$sHtml .= "</div>";
|
||||
|
||||
$sDlgTitle = addslashes(Dict::S('UI:ListConfigurationTitle'));
|
||||
$oPage->add_ready_script("$('#datatable_dlg_{$this->iListId}').dialog({autoOpen: false, title: '$sDlgTitle', width: 500, close: function() { $('#{$this->sDatatableContainerId}').datatable('onDlgCancel'); } });");
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $oSetting
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetAsHash($oSetting)
|
||||
{
|
||||
$aSettings = array('iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns);
|
||||
return $aSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aColumns
|
||||
* @param string $sSelectMode
|
||||
* @param bool $bViewLink
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink)
|
||||
{
|
||||
$aAttribs = array();
|
||||
if ($sSelectMode == 'multiple')
|
||||
{
|
||||
$aAttribs['form::select'] = array(
|
||||
'label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->iListId}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>",
|
||||
'description' => Dict::S('UI:SelectAllToggle+'),
|
||||
'metadata' => array(),
|
||||
);
|
||||
}
|
||||
else if ($sSelectMode == 'single')
|
||||
{
|
||||
$aAttribs['form::select'] = array('label' => '', 'description' => '', 'metadata' => array());
|
||||
}
|
||||
|
||||
foreach($this->aClassAliases as $sAlias => $sClassName)
|
||||
{
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
if ($sAttCode == '_key_')
|
||||
{
|
||||
$sAttLabel = MetaModel::GetName($sClassName);
|
||||
|
||||
$aAttribs['key_'.$sAlias] = array(
|
||||
'label' => $sAttLabel,
|
||||
'description' => '',
|
||||
'metadata' => array(
|
||||
'object_class' => $sClassName,
|
||||
'attribute_label' => $sAttLabel,
|
||||
),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
|
||||
$sAttDefClass = get_class($oAttDef);
|
||||
$sAttLabel = MetaModel::GetLabel($sClassName, $sAttCode);
|
||||
|
||||
$aAttribs[$sAttCode.'_'.$sAlias] = array(
|
||||
'label' => $sAttLabel,
|
||||
'description' => $oAttDef->GetOrderByHint(),
|
||||
'metadata' => array(
|
||||
'object_class' => $sClassName,
|
||||
'attribute_code' => $sAttCode,
|
||||
'attribute_type' => $sAttDefClass,
|
||||
'attribute_label' => $sAttLabel,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aAttribs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $aColumns
|
||||
* @param $sSelectMode
|
||||
* @param $iPageSize
|
||||
* @param $bViewLink
|
||||
* @param $aExtraParams
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$bLocalize = true;
|
||||
if (isset($aExtraParams['localize_values']))
|
||||
{
|
||||
$bLocalize = (bool) $aExtraParams['localize_values'];
|
||||
}
|
||||
|
||||
$aValues = array();
|
||||
$aAttDefsCache = array();
|
||||
$this->oSet->Seek(0);
|
||||
$iMaxObjects = $iPageSize;
|
||||
while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0))
|
||||
{
|
||||
$bFirstObject = true;
|
||||
$aRow = array();
|
||||
foreach($this->aClassAliases as $sAlias => $sClassName)
|
||||
{
|
||||
if (is_object($aObjects[$sAlias]))
|
||||
{
|
||||
$sHilightClass = MetaModel::GetHilightClass($sClassName, $aObjects[$sAlias]);
|
||||
if ($sHilightClass != '')
|
||||
{
|
||||
$aRow['@class'] = $sHilightClass;
|
||||
}
|
||||
if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject)
|
||||
{
|
||||
if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]))
|
||||
{
|
||||
$sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDisabled = '';
|
||||
}
|
||||
if ($sSelectMode == 'single')
|
||||
{
|
||||
$aRow['form::select'] = "<input type=\"radio\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow['form::select'] = "<input type=\"checkbox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
|
||||
}
|
||||
}
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
if ($sAttCode == '_key_')
|
||||
{
|
||||
$aRow['key_'.$sAlias] = array(
|
||||
'value_raw' => $aObjects[$sAlias]->GetKey(),
|
||||
'value_html' => $aObjects[$sAlias]->GetHyperLink(),
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Prepare att. def. classes cache to avoid retrieving AttDef for each row
|
||||
if(!isset($aAttDefsCache[$sClassName][$sAttCode]))
|
||||
{
|
||||
$aAttDefClassesCache[$sClassName][$sAttCode] = get_class(MetaModel::GetAttributeDef($sClassName, $sAttCode));
|
||||
}
|
||||
|
||||
// Only retrieve raw (stored) value for simple fields
|
||||
$bExcludeRawValue = false;
|
||||
foreach (cmdbAbstractObject::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
|
||||
{
|
||||
if (is_a($aAttDefClassesCache[$sClassName][$sAttCode], $sAttDefClassToExclude, true))
|
||||
{
|
||||
$bExcludeRawValue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if($bExcludeRawValue)
|
||||
{
|
||||
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow[$sAttCode.'_'.$sAlias] = array(
|
||||
'value_raw' => $aObjects[$sAlias]->Get($sAttCode),
|
||||
'value_html' => $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
if ($sAttCode == '_key_')
|
||||
{
|
||||
$aRow['key_'.$sAlias] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow[$sAttCode.'_'.$sAlias] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$bFirstObject = false;
|
||||
}
|
||||
$aValues[] = $aRow;
|
||||
$iMaxObjects--;
|
||||
}
|
||||
return $aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $aColumns
|
||||
* @param $sSelectMode
|
||||
* @param $iPageSize
|
||||
* @param $bViewLink
|
||||
* @param $aExtraParams
|
||||
*
|
||||
* @return string
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
|
||||
if ($iPageSize < 1)
|
||||
{
|
||||
$iPageSize = -1; // convention: no pagination
|
||||
}
|
||||
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
|
||||
|
||||
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
|
||||
|
||||
$sHtml = '<table class="listContainer object-list">';
|
||||
|
||||
foreach($this->oSet->GetFilter()->GetInternalParams() as $sName => $sValue)
|
||||
{
|
||||
$aExtraParams['query_params'][$sName] = $sValue;
|
||||
}
|
||||
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
|
||||
|
||||
$sHtml .= "<tr><td>";
|
||||
$sHtml .= $oPage->GetTable($aAttribs, $aValues);
|
||||
$sHtml .= '</td></tr>';
|
||||
$sHtml .= '</table>';
|
||||
$iCount = $this->iNbObjects;
|
||||
|
||||
$aArgs = $this->oSet->GetArgs();
|
||||
$sExtraParams = addslashes(str_replace('"', "'", json_encode(array_merge($aExtraParams, $aArgs)))); // JSON encode, change the style of the quotes and escape them
|
||||
$sSelectModeJS = '';
|
||||
$sHeaders = '';
|
||||
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
|
||||
{
|
||||
$sSelectModeJS = $sSelectMode;
|
||||
$sHeaders = 'headers: { 0: {sorter: false}},';
|
||||
}
|
||||
$sDisplayKey = ($bViewLink) ? 'true' : 'false';
|
||||
// Protect against duplicate elements in the Zlist
|
||||
$aUniqueOrderedList = array();
|
||||
foreach($this->aClassAliases as $sAlias => $sClassName)
|
||||
{
|
||||
foreach($aColumns[$sAlias] as $sAttCode => $aData)
|
||||
{
|
||||
if ($aData['checked'])
|
||||
{
|
||||
$aUniqueOrderedList[$sAttCode] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$aUniqueOrderedList = array_keys($aUniqueOrderedList);
|
||||
$sJSColumns = json_encode($aColumns);
|
||||
$sJSClassAliases = json_encode($this->aClassAliases);
|
||||
$sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : '';
|
||||
$this->oSet->ApplyParameters();
|
||||
// Display the actual sort order of the table
|
||||
$aRealSortOrder = $this->oSet->GetRealSortOrder();
|
||||
$aDefaultSort = array();
|
||||
$iColOffset = 0;
|
||||
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
|
||||
{
|
||||
$iColOffset += 1;
|
||||
}
|
||||
if ($bViewLink)
|
||||
{
|
||||
// $iColOffset += 1;
|
||||
}
|
||||
foreach($aRealSortOrder as $sColCode => $bAscending)
|
||||
{
|
||||
$iPos = array_search($sColCode, $aUniqueOrderedList);
|
||||
if ($iPos !== false)
|
||||
{
|
||||
$aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]";
|
||||
}
|
||||
else if (($iPos = array_search(preg_replace('/_friendlyname$/', '', $sColCode), $aUniqueOrderedList)) !== false)
|
||||
{
|
||||
// if sorted on the friendly name of an external key, then consider it sorted on the column that shows the links
|
||||
$aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]";
|
||||
}
|
||||
else if($sColCode == 'friendlyname' && $bViewLink)
|
||||
{
|
||||
$aDefaultSort[] = "[".($iColOffset).",".($bAscending ? '0' : '1')."]";
|
||||
}
|
||||
}
|
||||
$sFakeSortList = '';
|
||||
if (count($aDefaultSort) > 0)
|
||||
{
|
||||
$sFakeSortList = '['.implode(',', $aDefaultSort).']';
|
||||
}
|
||||
$sOQL = addslashes($this->oSet->GetFilter()->serialize());
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
var oTable = $('#{$this->sDatatableContainerId} table.listResults');
|
||||
oTable.tableHover();
|
||||
oTable
|
||||
.tablesorter({ $sHeaders widgets: ['myZebra', 'truncatedList']})
|
||||
.tablesorterPager({
|
||||
container: $('#pager{$this->iListId}'),
|
||||
totalRows:$iCount,
|
||||
size: $iPageSize,
|
||||
filter: '$sOQL',
|
||||
extra_params: '$sExtraParams',
|
||||
select_mode: '$sSelectModeJS',
|
||||
displayKey: $sDisplayKey,
|
||||
table_id: '{$this->sDatatableContainerId}',
|
||||
columns: $sJSColumns,
|
||||
class_aliases: $sJSClassAliases $sCssCount
|
||||
});
|
||||
JS
|
||||
);
|
||||
if ($sFakeSortList != '')
|
||||
{
|
||||
$oPage->add_ready_script("oTable.trigger(\"fakesorton\", [$sFakeSortList]);");
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $iDefaultPageSize
|
||||
* @param $iStart
|
||||
*/
|
||||
public function UpdatePager(WebPage $oPage, $iDefaultPageSize, $iStart)
|
||||
{
|
||||
$iPageSize = $iDefaultPageSize;
|
||||
$iPageIndex = 0;
|
||||
$sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
|
||||
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".json_encode($sHtml)."');");
|
||||
if ($iDefaultPageSize < 1)
|
||||
{
|
||||
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().hide()");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().show()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simplified version of the data table with less "decoration" (and no paging)
|
||||
* which is optimized for printing
|
||||
*/
|
||||
class PrintableDataTable extends DataTable
|
||||
{
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $iPageSize
|
||||
* @param $iDefaultPageSize
|
||||
* @param $iPageIndex
|
||||
* @param $aColumns
|
||||
* @param $bActionsMenu
|
||||
* @param $bToolkitMenu
|
||||
* @param $sSelectMode
|
||||
* @param $bViewLink
|
||||
* @param $aExtraParams
|
||||
*
|
||||
* @return string
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
|
||||
{
|
||||
return $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, -1, $bViewLink, $aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $aColumns
|
||||
* @param $sSelectMode
|
||||
* @param $iPageSize
|
||||
* @param $bViewLink
|
||||
* @param $aExtraParams
|
||||
*
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
|
||||
{
|
||||
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
|
||||
if ($iPageSize < 1)
|
||||
{
|
||||
$iPageSize = -1; // convention: no pagination
|
||||
}
|
||||
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
|
||||
|
||||
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
|
||||
|
||||
$sHtml = $oPage->GetTable($aAttribs, $aValues);
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
}
|
||||
@@ -1126,7 +1126,7 @@ JS
|
||||
$oSingleGroupByValueFilter->SetShowObsoleteData($this->m_bShowObsoleteData);
|
||||
}
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot()
|
||||
.'pages/UI.php?operation=search'.$oAppContext->GetForLink(true)
|
||||
.'pages/UI.php?operation=search&'.$oAppContext->GetForLink()
|
||||
.'&filter='.rawurlencode($oSingleGroupByValueFilter->serialize());
|
||||
$aCounts[$sStateValue] = ['link' => $sHyperlink, 'label' => $aCounts[$sStateValue]];
|
||||
}
|
||||
@@ -1234,7 +1234,7 @@ JS
|
||||
$iCount = $this->m_oSet->Count();
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
$sClassIconUrl = MetaModel::GetClassIcon($sClass, false);
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($this->m_oFilter->serialize());
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize());
|
||||
|
||||
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
||||
$aRefreshParams = [
|
||||
@@ -1243,7 +1243,7 @@ JS
|
||||
];
|
||||
|
||||
if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) {
|
||||
$sCreateActionUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.$oAppContext->GetForLink(true);
|
||||
$sCreateActionUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.'&'.$oAppContext->GetForLink();
|
||||
$sCreateActionLabel = Dict::Format('UI:Button:Create');
|
||||
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, $sCreateActionUrl,
|
||||
$sCreateActionLabel, $aRefreshParams);
|
||||
@@ -1291,7 +1291,7 @@ JS
|
||||
|
||||
$aData = array();
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sParams = $oAppContext->GetForLink(true);
|
||||
$sParams = $oAppContext->GetForLink();
|
||||
foreach ($aGroupBy as $iRow => $iCount) {
|
||||
// Build the search for this subset
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
@@ -1306,7 +1306,7 @@ JS
|
||||
|
||||
$aData[] = array(
|
||||
'group' => $aLabels[$iRow],
|
||||
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1$sParams&filter=$sFilter\">$iCount</a>"
|
||||
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"
|
||||
); // TO DO: add the context information
|
||||
}
|
||||
$aAttribs = array(
|
||||
@@ -1638,7 +1638,7 @@ JS
|
||||
$sGroupByExpr = isset($aExtraParams['group_by_expr']) ? '¶ms[group_by_expr]='.$aExtraParams['group_by_expr'] : '';
|
||||
$sFilter = $this->m_oFilter->serialize(false, $aQueryParams);
|
||||
$oContext = new ApplicationContext();
|
||||
$sContextParam = $oContext->GetForLink(true);
|
||||
$sContextParam = $oContext->GetForLink();
|
||||
$sAggregationFunction = isset($aExtraParams['aggregation_function']) ? $aExtraParams['aggregation_function'] : '';
|
||||
$sAggregationAttr = isset($aExtraParams['aggregation_attribute']) ? $aExtraParams['aggregation_attribute'] : '';
|
||||
$sLimit = isset($aExtraParams['limit']) ? $aExtraParams['limit'] : '';
|
||||
@@ -1646,7 +1646,7 @@ JS
|
||||
$sOrderDirection = isset($aExtraParams['order_direction']) ? $aExtraParams['order_direction'] : '';
|
||||
|
||||
if (isset($aExtraParams['group_by_label'])) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$sChartId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).$sContextParam;
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$sChartId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam;
|
||||
} else {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$sGroupBy{$sGroupByExpr}¶ms[chart_type]=$sChartType¶ms[currentId]=$sChartId{$iChartCounter}¶ms[order_direction]=$sOrderDirection¶ms[order_by]=$sOrderBy¶ms[limit]=$sLimit¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam;
|
||||
}
|
||||
@@ -1683,14 +1683,11 @@ JS
|
||||
$oBlock = null;
|
||||
$sJSURLs = '';
|
||||
|
||||
$oContext = new ApplicationContext();
|
||||
$sContextParam = $oContext->GetForLink(true);
|
||||
|
||||
if (isset($aExtraParams['group_by'])) {
|
||||
$this->MakeGroupByQuery($aExtraParams, $oGroupByExp, $sGroupByLabel, $aGroupBy, $sAggregationFunction, $sFctVar, $sAggregationAttr, $sSql);
|
||||
$aRes = CMDBSource::QueryToArray($sSql);
|
||||
|
||||
|
||||
$oContext = new ApplicationContext();
|
||||
$sContextParam = $oContext->GetForLink();
|
||||
|
||||
$iTotalCount = 0;
|
||||
$aURLs = array();
|
||||
@@ -1710,14 +1707,14 @@ JS
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
|
||||
$oSubsetSearch->AddConditionExpression($oCondition);
|
||||
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).$sContextParam;
|
||||
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).'&'.$sContextParam;
|
||||
}
|
||||
$sJSURLs = json_encode($aURLs);
|
||||
}
|
||||
if (isset($aExtraParams['group_by_label'])) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$aExtraParams[group_by]¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$aExtraParams[currentId]¶ms[order_direction]=$aExtraParams[order_direction]¶ms[order_by]=$aExtraParams[order_by]¶ms[limit]=$aExtraParams[limit]¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).$sContextParam;
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$aExtraParams[group_by]¶ms[group_by_label]={$aExtraParams['group_by_label']}¶ms[chart_type]=$sChartType¶ms[currentId]=$aExtraParams[currentId]¶ms[order_direction]=$aExtraParams[order_direction]¶ms[order_by]=$aExtraParams[order_by]¶ms[limit]=$aExtraParams[limit]¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).'&'.$sContextParam;
|
||||
} else {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$aExtraParams[group_by]¶ms[chart_type]=$sChartType¶ms[currentId]=$aExtraParams[currentId]¶ms[order_direction]=$aExtraParams[order_direction]¶ms[order_by]=$aExtraParams[order_by]¶ms[limit]=$aExtraParams[limit]¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).$sContextParam;
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart¶ms[group_by]=$aExtraParams[group_by]¶ms[chart_type]=$sChartType¶ms[currentId]=$aExtraParams[currentId]¶ms[order_direction]=$aExtraParams[order_direction]¶ms[order_by]=$aExtraParams[order_by]¶ms[limit]=$aExtraParams[limit]¶ms[aggregation_function]=$sAggregationFunction¶ms[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).'&'.$sContextParam;
|
||||
}
|
||||
|
||||
switch ($sChartType) {
|
||||
@@ -1790,7 +1787,7 @@ JS
|
||||
|
||||
$oBlock->sCsvFile = strtolower($this->m_oFilter->GetClass()).'.csv';
|
||||
$oBlock->sDownloadLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?expression='.urlencode($this->m_oFilter->ToOQL(true)).'&format=csv&filename='.urlencode($oBlock->sCsvFile);
|
||||
$oBlock->sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
|
||||
$oBlock->sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
|
||||
// Pass the parameters via POST, since expression may be very long
|
||||
$aParamsToPost = array(
|
||||
'expression' => $this->m_oFilter->ToOQL(true),
|
||||
@@ -1894,7 +1891,10 @@ class MenuBlock extends DisplayBlock
|
||||
&& (!isset($aExtraParams['menu']) || $aExtraParams['menu'] === "1" || $aExtraParams['menu'] === true)
|
||||
) {
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (utils::IsNotNullOrEmptyString($sContext)) {
|
||||
$sContext = '&'.$sContext;
|
||||
}
|
||||
|
||||
|
||||
$sFilter = $this->GetFilter()->serialize();
|
||||
@@ -2224,11 +2224,6 @@ class MenuBlock extends DisplayBlock
|
||||
$aFavoriteRegularActions[$key] = $aRegularActions[$key];
|
||||
unset($aRegularActions[$key]);
|
||||
}
|
||||
// Toolkit actions
|
||||
if (isset($aToolkitActions[$key])) {
|
||||
$aFavoriteRegularActions[$key] = $aToolkitActions[$key];
|
||||
unset($aToolkitActions[$key]);
|
||||
}
|
||||
|
||||
// Transitions
|
||||
if (isset($aTransitionActions[$key])) {
|
||||
@@ -2318,27 +2313,7 @@ class MenuBlock extends DisplayBlock
|
||||
break;
|
||||
|
||||
case 'UI:Menu:EMail':
|
||||
$sIconClass = 'far fa-envelope fa-lg';
|
||||
$sLabel = '';
|
||||
break;
|
||||
case 'UI:Menu:OpenOQL':
|
||||
$sIconClass = 'fas fa-play fa-sm';
|
||||
$sLabel = '';
|
||||
break;
|
||||
case 'UI:Menu:ExportXLSX':
|
||||
$sIconClass = 'far fa-file-excel fa-lg';
|
||||
$sLabel = '';
|
||||
break;
|
||||
case 'UI:Menu:CSVExport':
|
||||
$sIconClass = 'fas fa-file-csv fa-lg';
|
||||
$sLabel = '';
|
||||
break;
|
||||
case 'UI:Menu:PrintableVersion':
|
||||
$sIconClass = 'fas fa-print';
|
||||
$sLabel = '';
|
||||
break;
|
||||
case 'UI:Menu:ExportPDF':
|
||||
$sIconClass = 'fas fa-file-pdf fa-lg';
|
||||
$sIconClass = 'fas fa-share-alt';
|
||||
$sLabel = '';
|
||||
break;
|
||||
|
||||
@@ -2350,12 +2325,7 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
|
||||
$sTarget = isset($aAction['target']) ? $aAction['target'] : '';
|
||||
if (!empty($aAction['onclick'])) {
|
||||
$oActionButton = ButtonUIBlockFactory::MakeIconAction($sIconClass, $aAction['label'], $aAction['label'], $sLabel,false); //utils::Sanitize($sActionId.md5($aAction['onclick']), '', utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER))
|
||||
$oActionButton->SetOnClickJsCode($aAction['onclick']);
|
||||
} else {
|
||||
$oActionButton = ButtonUIBlockFactory::MakeLinkNeutral($sUrl, $sLabel, $sIconClass, $sTarget, utils::Sanitize($sActionId, '', utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER));
|
||||
}
|
||||
$oActionButton = ButtonUIBlockFactory::MakeLinkNeutral($sUrl, $sLabel, $sIconClass, $sTarget, utils::Sanitize($sActionId, '', utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER));
|
||||
// ResourceId should not be sanitized
|
||||
$oActionButton->AddDataAttribute('resource-id', $sActionId);
|
||||
$oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
|
||||
@@ -2614,8 +2584,11 @@ class MenuBlock extends DisplayBlock
|
||||
$sUrl = "{$sRootUrl}pages/{$sUIPage}?{$sUrlParams}";
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (utils::IsNotNullOrEmptyString($sContext)) {
|
||||
$sUrl .= '&'.$sContext;
|
||||
}
|
||||
|
||||
return $sUrl . $sContext;
|
||||
return $sUrl;
|
||||
}
|
||||
}
|
||||
|
||||
8
application/errorpage.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader');
|
||||
@@ -63,14 +63,14 @@ class CoreCannotSaveObjectException extends CoreException
|
||||
public function getTextMessage()
|
||||
{
|
||||
$sTitle = Dict::S('UI:Error:SaveFailed');
|
||||
$sContent = $sTitle;
|
||||
$sContent = utils::HtmlEntities($sTitle);
|
||||
|
||||
if (count($this->aIssues) == 1) {
|
||||
$sIssue = reset($this->aIssues);
|
||||
$sContent .= $sIssue;
|
||||
$sContent .= utils::HtmlEntities($sIssue);
|
||||
} else {
|
||||
foreach ($this->aIssues as $sError) {
|
||||
$sContent .= " " . $sError . ", ";
|
||||
$sContent .= " ".utils::HtmlEntities($sError).", ";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ class CoreException extends Exception
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
|
||||
{
|
||||
return utils::EscapeHtml($this->getMessage());
|
||||
return $this->getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -363,7 +363,7 @@ $('#$sDialogId').dialog({
|
||||
buttons: [
|
||||
{ text: "$sOkButtonLabel", click: function() {
|
||||
var oForm = $(this).closest('.ui-dialog').find('form');
|
||||
oForm.trigger('submit');
|
||||
oForm.submit();
|
||||
if (AnimateDlgButtons)
|
||||
{
|
||||
sFormId = oForm.attr('id');
|
||||
@@ -1501,11 +1501,6 @@ class DesignerIconSelectionField extends DesignerFormField
|
||||
$this->aAllowedValues = $aAllowedValues;
|
||||
}
|
||||
|
||||
public function AddAllowedValue($aValue)
|
||||
{
|
||||
// Add a null value to the list of allowed values
|
||||
$this->aAllowedValues = array_merge([$aValue], $this->aAllowedValues);
|
||||
}
|
||||
public function EnableUpload($sIconUploadUrl)
|
||||
{
|
||||
$this->sUploadUrl = $sIconUploadUrl;
|
||||
|
||||
9
application/itopwebpage.class.inc.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader');
|
||||
8
application/itopwizardwebpage.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader');
|
||||
@@ -91,6 +91,15 @@ function _MaintenanceJsonMessage($sTitle, $sMessage)
|
||||
$oP->AddData($aMessage);
|
||||
$oP->Output();
|
||||
} else {
|
||||
_MaintenanceTextMessage($sMessage);
|
||||
@include_once(APPROOT."/application/ajaxwebpage.class.inc.php");
|
||||
if (class_exists('ajax_page')) {
|
||||
$oP = new ajax_page($sTitle);
|
||||
$oP->add_header('Access-Control-Allow-Origin: *');
|
||||
$oP->SetContentType('application/json');
|
||||
$oP->add('{"code":100, "message":"'.$sMessage.'"}');
|
||||
$oP->Output();
|
||||
} else {
|
||||
_MaintenanceTextMessage($sMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/application/template.class.inc.php');
|
||||
require_once(APPROOT."/application/user.dashboard.class.inc.php");
|
||||
|
||||
|
||||
@@ -998,11 +999,15 @@ class MenuGroup extends MenuNode
|
||||
*/
|
||||
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)
|
||||
* @param string $sTemplateFile unused deprecated
|
||||
* @param string $sTemplateFile Path (or URL) to the file that will be used as a template for displaying the page's content
|
||||
* @param integer $iParentIndex ID of the parent menu
|
||||
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
|
||||
* @param string $sEnableClass Name of class of object
|
||||
@@ -1013,6 +1018,17 @@ class TemplateMenuNode extends MenuNode
|
||||
public function __construct($sMenuId, $sTemplateFile, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sTemplateFile = $sTemplateFile;
|
||||
$this->aReflectionProperties['template_file'] = $sTemplateFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
if ($this->sTemplateFile == '') return '';
|
||||
return parent::GetHyperlink($aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1021,7 +1037,18 @@ class TemplateMenuNode extends MenuNode
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
//DO NOTHING this type of menu is only used for title not clickable
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
|
||||
$sTemplate = @file_get_contents($this->sTemplateFile);
|
||||
if ($sTemplate !== false)
|
||||
{
|
||||
$aExtraParams['table_id'] = 'Menu_'.$this->GetMenuId();
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
$oTemplate->Render($oPage, $aExtraParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->p("Error: failed to load template file: '{$this->sTemplateFile}'"); // No need to translate ?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
application/nicewebpage.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader');
|
||||
8
application/pdfpage.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader');
|
||||
@@ -221,10 +221,17 @@ class QueryOQL extends Query
|
||||
{
|
||||
try{
|
||||
// retrieve attributes
|
||||
$sFields = trim($this->Get('fields'));
|
||||
$sOql = $this->Get('oql');
|
||||
|
||||
// construct base url depending on version
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&date_format='.urlencode((string)AttributeDateTime::GetFormat()).'&query='.$this->GetKey();
|
||||
$bExportV1Recommended = ($sFields == '');
|
||||
if ($bExportV1Recommended) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
||||
}
|
||||
else{
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&date_format='.urlencode((string)AttributeDateTime::GetFormat()).'&query='.$this->GetKey();
|
||||
}
|
||||
|
||||
// search object from OQL
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
@@ -249,9 +256,22 @@ class QueryOQL extends Query
|
||||
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-query-oql ibo-is-code'); $('[data-attribute-code=\"oql\"]').addClass('ibo-query-oql ibo-is-code');");
|
||||
|
||||
if (!$bEditMode) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&date_format='.urlencode((string)AttributeDateTime::GetFormat()).'&query='.$this->GetKey();
|
||||
|
||||
$sFields = trim($this->Get('fields'));
|
||||
$bExportV1Recommended = ($sFields == '');
|
||||
if ($bExportV1Recommended) {
|
||||
$oFieldAttDef = MetaModel::GetAttributeDef('QueryOQL', 'fields');
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure()
|
||||
->SetIsClosable(false)
|
||||
->SetIsCollapsible(false);
|
||||
$oAlert->AddCSSClass('mb-5');
|
||||
$oAlert->AddSubBlock(new Html(Dict::Format('UI:Query:UrlV1', '')));
|
||||
$oPage->AddSubBlock($oAlert);
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
||||
} else {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&date_format='.urlencode((string)AttributeDateTime::GetFormat()).'&query='.$this->GetKey();
|
||||
}
|
||||
$sOql = $this->Get('oql');
|
||||
$sMessage = null;
|
||||
try {
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
$aParameters = $oSearch->GetQueryParams();
|
||||
|
||||
427
application/template.class.inc.php
Normal file
@@ -0,0 +1,427 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2024 Combodo SAS
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||
use Combodo\iTop\Application\WebPage\WebPage;
|
||||
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
/**
|
||||
* This class manages the special template format used internally to build the iTop web pages
|
||||
*
|
||||
* @deprecated 3.0.0
|
||||
*/
|
||||
class DisplayTemplate
|
||||
{
|
||||
protected $m_sTemplate;
|
||||
protected $m_aTags;
|
||||
static protected $iBlockCount = 0;
|
||||
|
||||
public function __construct($sTemplate)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
$this->m_aTags = array(
|
||||
'itopblock',
|
||||
'itopcheck',
|
||||
'itoptabs',
|
||||
'itoptab',
|
||||
'itoptoggle',
|
||||
'itopstring',
|
||||
'sqlblock',
|
||||
);
|
||||
$this->m_sTemplate = $sTemplate;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oPage, $aParams = array())
|
||||
{
|
||||
$this->m_sTemplate = MetaModel::ApplyParams($this->m_sTemplate, $aParams);
|
||||
$iStart = 0;
|
||||
$iEnd = strlen($this->m_sTemplate);
|
||||
$iCount = 0;
|
||||
$iBeforeTagPos = $iStart;
|
||||
$iAfterTagPos = $iStart;
|
||||
while($sTag = $this->GetNextTag($iStart, $iEnd))
|
||||
{
|
||||
$sContent = $this->GetTagContent($sTag, $iStart, $iEnd);
|
||||
$iAfterTagPos = $iEnd + strlen('</'.$sTag.'>');
|
||||
$sOuterTag = substr($this->m_sTemplate, $iStart, $iAfterTagPos - $iStart);
|
||||
$oPage->add(substr($this->m_sTemplate, $iBeforeTagPos, $iStart - $iBeforeTagPos));
|
||||
if ($sTag == DisplayBlock::TAG_BLOCK)
|
||||
{
|
||||
try
|
||||
{
|
||||
$oBlock = DisplayBlock::FromTemplate($sOuterTag);
|
||||
if (is_object($oBlock))
|
||||
{
|
||||
$oBlock->Display($oPage, 'block_'.self::$iBlockCount, $aParams);
|
||||
}
|
||||
}
|
||||
catch(OQLException $e)
|
||||
{
|
||||
$oPage->p('Error in template (please contact your administrator) - Invalid query<!--'.$sOuterTag.'-->');
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$oPage->p('Error in template (please contact your administrator)<!--'.$e->getMessage().'--><!--'.$sOuterTag.'-->');
|
||||
}
|
||||
|
||||
self::$iBlockCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAttributes = $this->GetTagAttributes($sTag, $iStart, $iEnd);
|
||||
//$oPage->p("Tag: $sTag - ($iStart, $iEnd)");
|
||||
$this->RenderTag($oPage, $sTag, $aAttributes, $sContent);
|
||||
|
||||
}
|
||||
$iAfterTagPos = $iEnd + strlen('</'.$sTag.'>');
|
||||
$iBeforeTagPos = $iAfterTagPos;
|
||||
$iStart = $iEnd;
|
||||
$iEnd = strlen($this->m_sTemplate);
|
||||
$iCount++;
|
||||
}
|
||||
$oPage->add(substr($this->m_sTemplate, $iAfterTagPos));
|
||||
}
|
||||
|
||||
public function GetNextTag(&$iStartPos, &$iEndPos)
|
||||
{
|
||||
$iChunkStartPos = $iStartPos;
|
||||
$sNextTag = null;
|
||||
$iStartPos = $iEndPos;
|
||||
foreach($this->m_aTags as $sTag)
|
||||
{
|
||||
// Search for the opening tag
|
||||
$iOpeningPos = stripos($this->m_sTemplate, '<'.$sTag.' ', $iChunkStartPos);
|
||||
if ($iOpeningPos === false)
|
||||
{
|
||||
$iOpeningPos = stripos($this->m_sTemplate, '<'.$sTag.'>', $iChunkStartPos);
|
||||
}
|
||||
if ($iOpeningPos !== false)
|
||||
{
|
||||
$iClosingPos = stripos($this->m_sTemplate, '</'.$sTag.'>', $iOpeningPos);
|
||||
}
|
||||
if ( ($iOpeningPos !== false) && ($iClosingPos !== false))
|
||||
{
|
||||
if ($iOpeningPos < $iStartPos)
|
||||
{
|
||||
// This is the next tag
|
||||
$iStartPos = $iOpeningPos;
|
||||
$iEndPos = $iClosingPos;
|
||||
$sNextTag = $sTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sNextTag;
|
||||
}
|
||||
|
||||
public function GetTagContent($sTag, $iStartPos, $iEndPos)
|
||||
{
|
||||
$sContent = "";
|
||||
$iContentStart = strpos($this->m_sTemplate, '>', $iStartPos); // Content of tag start immediatly after the first closing bracket
|
||||
if ($iContentStart !== false)
|
||||
{
|
||||
$sContent = substr($this->m_sTemplate, 1+$iContentStart, $iEndPos - $iContentStart - 1);
|
||||
}
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
public function GetTagAttributes($sTag, $iStartPos, $iEndPos)
|
||||
{
|
||||
$aAttr = array();
|
||||
$iAttrStart = strpos($this->m_sTemplate, ' ', $iStartPos); // Attributes start just after the first space
|
||||
$iAttrEnd = strpos($this->m_sTemplate, '>', $iStartPos); // Attributes end just before the first closing bracket
|
||||
if ( ($iAttrStart !== false) && ($iAttrEnd !== false) && ($iAttrEnd > $iAttrStart))
|
||||
{
|
||||
$sAttributes = substr($this->m_sTemplate, 1+$iAttrStart, $iAttrEnd - $iAttrStart - 1);
|
||||
$aAttributes = explode(' ', $sAttributes);
|
||||
foreach($aAttributes as $sAttr)
|
||||
{
|
||||
if ( preg_match('/(.+) *= *"(.+)"$/', $sAttr, $aMatches) )
|
||||
{
|
||||
$aAttr[strtolower($aMatches[1])] = $aMatches[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aAttr;
|
||||
}
|
||||
|
||||
protected function RenderTag($oPage, $sTag, $aAttributes, $sContent)
|
||||
{
|
||||
static $iTabContainerCount = 0;
|
||||
switch($sTag)
|
||||
{
|
||||
case 'itoptabs':
|
||||
$oPage->AddTabContainer('Tabs_'.$iTabContainerCount);
|
||||
$oPage->SetCurrentTabContainer('Tabs_'.$iTabContainerCount);
|
||||
$iTabContainerCount++;
|
||||
//$oPage->p('Content:<pre>'.htmlentities($sContent, ENT_QUOTES, 'UTF-8').'</pre>');
|
||||
$oTemplate = new DisplayTemplate($sContent);
|
||||
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
|
||||
$oPage->SetCurrentTabContainer('');
|
||||
break;
|
||||
|
||||
case 'itopcheck':
|
||||
$sClassName = $aAttributes['class'];
|
||||
if (MetaModel::IsValidClass($sClassName) && UserRights::IsActionAllowed($sClassName, UR_ACTION_READ))
|
||||
{
|
||||
$oTemplate = new DisplayTemplate($sContent);
|
||||
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave a trace for those who'd like to understand why nothing is displayed
|
||||
$oPage->add("<!-- class $sClassName does not exist, skipping some part of the template -->\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'itoptab':
|
||||
$oPage->SetCurrentTab($aAttributes['name'], str_replace('_', ' ', $aAttributes['name']));
|
||||
$oTemplate = new DisplayTemplate($sContent);
|
||||
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
|
||||
//$oPage->p('iTop Tab Content:<pre>'.htmlentities($sContent, ENT_QUOTES, 'UTF-8').'</pre>');
|
||||
$oPage->SetCurrentTab('');
|
||||
break;
|
||||
|
||||
case 'itoptoggle':
|
||||
$sName = isset($aAttributes['name']) ? $aAttributes['name'] : 'Tagada';
|
||||
$bOpen = isset($aAttributes['open']) ? $aAttributes['open'] : true;
|
||||
$oPage->StartCollapsibleSection(Dict::S($sName), $bOpen);
|
||||
$oTemplate = new DisplayTemplate($sContent);
|
||||
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
|
||||
//$oPage->p('iTop Tab Content:<pre>'.htmlentities($sContent, ENT_QUOTES, 'UTF-8').'</pre>');
|
||||
$oPage->EndCollapsibleSection();
|
||||
break;
|
||||
|
||||
case 'itopstring':
|
||||
$oPage->add(Dict::S($sContent));
|
||||
break;
|
||||
|
||||
case 'sqlblock':
|
||||
$oBlock = SqlBlock::FromTemplate($sContent);
|
||||
$oBlock->RenderContent($oPage);
|
||||
break;
|
||||
|
||||
case 'itopblock': // No longer used, handled by DisplayBlock::FromTemplate see above
|
||||
$oPage->add("<!-- Application Error: should be handled by DisplayBlock::FromTemplate -->");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown tag, just ignore it or now -- output an HTML comment
|
||||
$oPage->add("<!-- unsupported tag: $sTag -->");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit test
|
||||
*/
|
||||
static public function UnitTest()
|
||||
{
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
|
||||
$sAppRootUrl = utils::GetAbsoluteUrlAppRoot();
|
||||
$sTemplate = '<div class="page_header">
|
||||
<div class="actions_details"><a href="#"><span>Actions</span></a></div>
|
||||
<h1>$class$: <span class="hilite">$name$</span></h1>
|
||||
</div>
|
||||
<img src="'.$sAppRootUrl.'images/connect_to_network.png" style="margin-top:-10px; margin-right:10px; float:right">
|
||||
<itoptabs>
|
||||
<itoptab name="Interfaces">
|
||||
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT Interface AS i WHERE i.device_id = $id$</itopblock>
|
||||
</itoptab>
|
||||
<itoptab name="Contacts">
|
||||
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT Contact AS c JOIN lnkContactToCI AS l ON l.contact_id = c.id WHERE l.ci_id = $id$</itopblock>
|
||||
</itoptab>
|
||||
<itoptab name="Documents">
|
||||
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT Document AS d JOIN lnkDocumentToCI as l ON l.document_id = d.id WHERE l.ci_id = $id$)</itopblock>
|
||||
</itoptab>
|
||||
</itoptabs>';
|
||||
|
||||
$oPage = new iTopWebPage('Unit Test');
|
||||
//$oPage->add("Template content: <pre>".htmlentities($sTemplate, ENT_QUOTES, 'UTF-8')."</pre>\n");
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
$oTemplate->Render($oPage, array('class'=>'Network device','pkey'=> 271, 'name' => 'deliversw01.mecanorama.fr', 'org_id' => 3));
|
||||
$oPage->output();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special type of template for displaying the details of an object
|
||||
* On top of the defaut 'blocks' managed by the parent class, the following placeholders
|
||||
* are available in such a template:
|
||||
* $attribute_code$ An attribute of the object (in edit mode this is the input for the attribute)
|
||||
* $attribute_code->label()$ The label of an attribute
|
||||
* $PlugIn:plugInClass->properties()$ The ouput of OnDisplayProperties of the specified plugInClass
|
||||
*/
|
||||
class ObjectDetailsTemplate extends DisplayTemplate
|
||||
{
|
||||
public function __construct($sTemplate, $oObj, $sFormPrefix = '')
|
||||
{
|
||||
parent::__construct($sTemplate);
|
||||
$this->m_oObj = $oObj;
|
||||
$this->m_sPrefix = $sFormPrefix;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oPage, $aParams = array(), $bEditMode = false)
|
||||
{
|
||||
$sStateAttCode = MetaModel :: GetStateAttributeCode(get_class($this->m_oObj));
|
||||
$aTemplateFields = array();
|
||||
preg_match_all('/\\$this->([a-z0-9_]+)\\$/', $this->m_sTemplate, $aMatches);
|
||||
foreach ($aMatches[1] as $sAttCode)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
|
||||
{
|
||||
$aTemplateFields[] = $sAttCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['this->'.$sAttCode] = "<!--Unknown attribute: $sAttCode-->";
|
||||
}
|
||||
}
|
||||
preg_match_all('/\\$this->field\\(([a-z0-9_]+)\\)\\$/', $this->m_sTemplate, $aMatches);
|
||||
foreach ($aMatches[1] as $sAttCode)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
|
||||
{
|
||||
$aTemplateFields[] = $sAttCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['this->field('.$sAttCode.')'] = "<!--Unknown attribute: $sAttCode-->";
|
||||
}
|
||||
}
|
||||
$aFieldsComments = (isset($aParams['fieldsComments'])) ? $aParams['fieldsComments'] : array();
|
||||
$aFieldsMap = array();
|
||||
|
||||
$sClass = get_class($this->m_oObj);
|
||||
// Renders the fields used in the template
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this->m_oObj)) as $sAttCode => $oAttDef)
|
||||
{
|
||||
$aParams['this->label('.$sAttCode.')'] = $oAttDef->GetLabel();
|
||||
$aParams['this->comments('.$sAttCode.')'] = isset($aFieldsComments[$sAttCode]) ? $aFieldsComments[$sAttCode] : '';
|
||||
$iInputId = '2_'.$sAttCode; // TODO: generate a real/unique prefix...
|
||||
if (in_array($sAttCode, $aTemplateFields))
|
||||
{
|
||||
if ($this->m_oObj->IsNew())
|
||||
{
|
||||
$iFlags = $this->m_oObj->GetInitialStateAttributeFlags($sAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iFlags = $this->m_oObj->GetAttributeFlags($sAttCode);
|
||||
}
|
||||
if (($iFlags & OPT_ATT_MANDATORY) && $this->m_oObj->IsNew())
|
||||
{
|
||||
$iFlags = $iFlags & ~OPT_ATT_READONLY; // Mandatory fields cannot be read-only when creating an object
|
||||
}
|
||||
|
||||
if ((!$oAttDef->IsWritable()) || ($sStateAttCode == $sAttCode))
|
||||
{
|
||||
$iFlags = $iFlags | OPT_ATT_READONLY;
|
||||
}
|
||||
|
||||
if ($iFlags & OPT_ATT_HIDDEN)
|
||||
{
|
||||
$aParams['this->label('.$sAttCode.')'] = '';
|
||||
$aParams['this->field('.$sAttCode.')'] = '';
|
||||
$aParams['this->comments('.$sAttCode.')'] = '';
|
||||
$aParams['this->'.$sAttCode] = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($bEditMode && ($iFlags & (OPT_ATT_READONLY|OPT_ATT_SLAVE)))
|
||||
{
|
||||
// Check if the attribute is not read-only because of a synchro...
|
||||
$aReasons = array();
|
||||
$sSynchroIcon = '';
|
||||
if ($iFlags & OPT_ATT_SLAVE)
|
||||
{
|
||||
$iSynchroFlags = $this->m_oObj->GetSynchroReplicaFlags($sAttCode, $aReasons);
|
||||
$sAppRooturl = utils::GetAbsoluteUrlAppRoot();
|
||||
$sSynchroIcon = " <img id=\"synchro_$iInputId\" src=\"{$sAppRooturl}images/transp-lock.png\" style=\"vertical-align:middle\"/>";
|
||||
$sTip = '';
|
||||
foreach($aReasons as $aRow)
|
||||
{
|
||||
$sDescription = utils::EscapeHtml($aRow['description']);
|
||||
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
|
||||
$sTip .= "<div class='synchro-source'>";
|
||||
$sTip .= "<div class='synchro-source-title'>Synchronized with {$aRow['name']}</div>";
|
||||
$sTip .= "<div class='synchro-source-description'>$sDescription</div>";
|
||||
}
|
||||
$oPage->add_ready_script("$('#synchro_$iInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
|
||||
// Attribute is read-only
|
||||
$sHTMLValue = "<span id=\"field_{$iInputId}\">".$this->m_oObj->GetAsHTML($sAttCode);
|
||||
$sHTMLValue .= '<input type="hidden" id="'.$iInputId.'" name="attr_'.$sAttCode.'" value="'.utils::EscapeHtml($this->m_oObj->Get($sAttCode)).'"/></span>';
|
||||
$aFieldsMap[$sAttCode] = $iInputId;
|
||||
$aParams['this->comments('.$sAttCode.')'] = $sSynchroIcon;
|
||||
}
|
||||
|
||||
if ($bEditMode && !($iFlags & OPT_ATT_READONLY)) //TODO: check the data synchro status...
|
||||
{
|
||||
$aParams['this->field('.$sAttCode.')'] = "<span id=\"field_{$iInputId}\">".$this->m_oObj->GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef,
|
||||
$this->m_oObj->Get($sAttCode),
|
||||
$this->m_oObj->GetEditValue($sAttCode),
|
||||
$iInputId, // InputID
|
||||
'',
|
||||
$iFlags,
|
||||
array('this' => $this->m_oObj) // aArgs
|
||||
).'</span>';
|
||||
$aFieldsMap[$sAttCode] = $iInputId;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams['this->field('.$sAttCode.')'] = $this->m_oObj->GetAsHTML($sAttCode);
|
||||
}
|
||||
$aParams['this->'.$sAttCode] = "<table class=\"field\"><tr><td class=\"label\">".$aParams['this->label('.$sAttCode.')'].":</td><td>".$aParams['this->field('.$sAttCode.')']."</td><td>".$aParams['this->comments('.$sAttCode.')']."</td></tr></table>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Renders the PlugIns used in the template
|
||||
preg_match_all('/\\$PlugIn:([A-Za-z0-9_]+)->properties\\(\\)\\$/', $this->m_sTemplate, $aMatches);
|
||||
$aPlugInProperties = $aMatches[1];
|
||||
foreach($aPlugInProperties as $sPlugInClass)
|
||||
{
|
||||
/** @var \iApplicationUIExtension $oInstance */
|
||||
$oInstance = MetaModel::GetPlugins('iApplicationUIExtension', $sPlugInClass);
|
||||
if ($oInstance != null) // Safety check...
|
||||
{
|
||||
$offset = $oPage->start_capture();
|
||||
$oInstance->OnDisplayProperties($this->m_oObj, $oPage, $bEditMode);
|
||||
$sContent = $oPage->end_capture($offset);
|
||||
$aParams["PlugIn:{$sPlugInClass}->properties()"]= $sContent;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams["PlugIn:{$sPlugInClass}->properties()"]= "Missing PlugIn: $sPlugInClass";
|
||||
}
|
||||
}
|
||||
|
||||
$offset = $oPage->start_capture();
|
||||
parent::Render($oPage, $aParams);
|
||||
$sContent = $oPage->end_capture($offset);
|
||||
// Remove empty table rows in case some attributes are hidden...
|
||||
$sContent = preg_replace('/<tr[^>]*>\s*(<td[^>]*>\s*<\\/td>)+\s*<\\/tr>/im', '', $sContent);
|
||||
$oPage->add($sContent);
|
||||
return $aFieldsMap;
|
||||
}
|
||||
}
|
||||
|
||||
//DisplayTemplate::UnitTest();
|
||||
?>
|
||||
@@ -55,6 +55,18 @@ class ThemeHandler
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ID of the theme currently defined in the config. file
|
||||
*
|
||||
* @deprecated 3.0.0, will be removed in 3.1, see N°3898
|
||||
* @return string
|
||||
*/
|
||||
public static function GetCurrentThemeId()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
static::GetCurrentUserThemeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string ID of the theme currently defined in the config. file, which applies to all users by default. If non defined, fallback on the default one.
|
||||
* @since 3.0.0
|
||||
|
||||
127
application/twigextension.class.inc.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop;
|
||||
|
||||
use AttributeDate;
|
||||
use AttributeDateTime;
|
||||
use DeprecatedCallsLog;
|
||||
use Dict;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
use Twig\Environment;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use utils;
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('instead use sources/Application/TwigBase/Twig/Extension.php, which is loaded by the autoloader');
|
||||
|
||||
/**
|
||||
* Class TwigExtension
|
||||
*
|
||||
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
|
||||
* @package Combodo\iTop
|
||||
* @deprecated 3.1.0 N°4034
|
||||
*/
|
||||
class TwigExtension
|
||||
{
|
||||
/**
|
||||
* Registers Twig extensions such as filters or functions.
|
||||
* It allows us to access some stuff directly in twig.
|
||||
*
|
||||
* @param Environment $oTwigEnv
|
||||
*/
|
||||
public static function RegisterTwigExtensions(Environment &$oTwigEnv)
|
||||
{
|
||||
// Filter to translate a string via the Dict::S function
|
||||
// Usage in twig: {{ 'String:ToTranslate'|dict_s }}
|
||||
$oTwigEnv->addFilter(new TwigFilter('dict_s',
|
||||
function ($sStringCode, $sDefault = null, $bUserLanguageOnly = false) {
|
||||
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to format a string via the Dict::Format function
|
||||
// Usage in twig: {{ 'String:ToTranslate'|dict_format() }}
|
||||
$oTwigEnv->addFilter(new TwigFilter('dict_format',
|
||||
function ($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null) {
|
||||
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to format output
|
||||
// example a DateTime is converted to user format
|
||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
||||
$oTwigEnv->addFilter(new TwigFilter('date_format',
|
||||
function ($sDate) {
|
||||
try {
|
||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate))) {
|
||||
return AttributeDateTime::GetFormat()->Format($sDate);
|
||||
}
|
||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d$@', trim($sDate))) {
|
||||
return AttributeDate::GetFormat()->Format($sDate);
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
}
|
||||
return $sDate;
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
// Filter to format output
|
||||
// example a DateTime is converted to user format
|
||||
// Usage in twig: {{ 'String:ToFormat'|output_format }}
|
||||
$oTwigEnv->addFilter(new TwigFilter('size_format',
|
||||
function ($sSize) {
|
||||
return utils::BytesToFriendlyFormat($sSize);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to enable base64 encode/decode
|
||||
// Usage in twig: {{ 'String to encode'|base64_encode }}
|
||||
$oTwigEnv->addFilter(new TwigFilter('base64_encode', 'base64_encode'));
|
||||
$oTwigEnv->addFilter(new TwigFilter('base64_decode', 'base64_decode'));
|
||||
|
||||
// Filter to enable json decode (encode already exists)
|
||||
// Usage in twig: {{ aSomeArray|json_decode }}
|
||||
$oTwigEnv->addFilter(new TwigFilter('json_decode', function ($sJsonString, $bAssoc = false) {
|
||||
return json_decode($sJsonString, $bAssoc);
|
||||
})
|
||||
);
|
||||
|
||||
// Filter to add itopversion to an url
|
||||
$oTwigEnv->addFilter(new TwigFilter('add_itop_version', function ($sUrl) {
|
||||
$sUrl = utils::AddParameterToUrl($sUrl, 'itopversion', ITOP_VERSION);
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
|
||||
// Filter to add a module's version to an url
|
||||
$oTwigEnv->addFilter(new TwigFilter('add_module_version', function ($sUrl, $sModuleName) {
|
||||
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
|
||||
$sUrl = utils::AddParameterToUrl($sUrl, 'moduleversion', $sModuleVersion);
|
||||
|
||||
return $sUrl;
|
||||
}));
|
||||
|
||||
// Function to check our current environment
|
||||
// Usage in twig: {% if is_development_environment() %}
|
||||
$oTwigEnv->addFunction(new TwigFunction('is_development_environment', function () {
|
||||
return utils::IsDevelopmentEnvironment();
|
||||
}));
|
||||
|
||||
// Function to get the URL of a static page in a module
|
||||
// Usage in twig: {{ get_static_page_module_url('itop-my-module', 'path-to-my-page') }}
|
||||
$oTwigEnv->addFunction(new TwigFunction('get_static_page_module_url', function ($sModuleName, $sPage) {
|
||||
return utils::GetAbsoluteUrlModulesRoot().$sModuleName.'/'.$sPage;
|
||||
}));
|
||||
|
||||
// Function to get the URL of a php page in a module
|
||||
// Usage in twig: {{ get_page_module_url('itop-my-module', 'path-to-my-my-page.php') }}
|
||||
$oTwigEnv->addFunction(new TwigFunction('get_page_module_url', function ($sModuleName, $sPage) {
|
||||
return utils::GetAbsoluteUrlModulePage($sModuleName, $sPage);
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -250,7 +250,7 @@ class UIExtKeyWidget
|
||||
foreach ($aAdditionalField as $sAdditionalField) {
|
||||
array_push($aArguments, $oObj->Get($sAdditionalField));
|
||||
}
|
||||
$aOption['additional_field'] = utils::HtmlEntities(utils::VSprintf($sFormatAdditionalField, $aArguments));
|
||||
$aOption['additional_field'] = utils::HtmlEntities(vsprintf($sFormatAdditionalField, $aArguments));
|
||||
}
|
||||
if (!empty($sObjectImageAttCode)) {
|
||||
// Try to retrieve image for contact
|
||||
|
||||
@@ -521,8 +521,8 @@ class utils
|
||||
|
||||
// For URL
|
||||
case static::ENUM_SANITIZATION_FILTER_URL:
|
||||
$retValue = filter_var($value, FILTER_SANITIZE_URL);
|
||||
$retValue = filter_var($retValue, FILTER_VALIDATE_URL);
|
||||
// N°6350 - returns only valid URLs
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_URL);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1516,12 +1516,12 @@ class utils
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
/** @var \DBObjectSet $param */
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($param->GetFilter()->GetClass());
|
||||
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
|
||||
$sFilter = urlencode($param->GetFilter()->serialize());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
@@ -1555,10 +1555,6 @@ class utils
|
||||
}
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
|
||||
if (ApplicationMenu::IsMenuIdEnabled('RunQueriesMenu')) {
|
||||
$oMenuItemPlay = new JSPopupMenuItem('UI:Menu:OpenOQL', Dict::S('UI:Menu:OpenOQL'), "OpenOql('$sOQL')");
|
||||
$aResult[] = $oMenuItemPlay;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -1570,6 +1566,9 @@ class utils
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
@@ -1692,8 +1691,8 @@ class utils
|
||||
$oAppContext = new ApplicationContext();
|
||||
|
||||
$sUrl = $sAppRootUrl
|
||||
.'pages/UI.php?operation=search'
|
||||
.$oAppContext->GetForLink(true)
|
||||
.'pages/UI.php?operation=search&'
|
||||
.$oAppContext->GetForLink()
|
||||
.'&filter='.rawurlencode($oDataTableSearchFilter->serialize());
|
||||
$sUrl .= '&aParams='.rawurlencode($sParams); // Not working... yet, cause not handled by UI.php
|
||||
|
||||
@@ -1948,7 +1947,7 @@ SQL;
|
||||
CURLOPT_HEADER => false, // don't return the headers in the output
|
||||
CURLOPT_FOLLOWLOCATION => true, // follow redirects
|
||||
CURLOPT_ENCODING => "", // handle all encodings
|
||||
CURLOPT_USERAGENT => "spider", // who am i
|
||||
CURLOPT_USERAGENT => static::GetConfig()->Get('http.request.user_agent'), // who am i
|
||||
CURLOPT_AUTOREFERER => true, // set referer on redirect
|
||||
CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
|
||||
CURLOPT_TIMEOUT => 120, // timeout on response
|
||||
@@ -2076,127 +2075,6 @@ SQL;
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a string using vsprintf with safety checks to avoid ValueError
|
||||
*
|
||||
* This method fills missing arguments with their original format specifiers,
|
||||
* then calls vsprintf with the complete array.
|
||||
*
|
||||
* @param string $sFormat The format string
|
||||
* @param array $aArgs The arguments to format
|
||||
* @param bool $bLogErrors Whether to log errors (defaults to true)
|
||||
*
|
||||
* @return string The formatted string
|
||||
* @since 3.2.2
|
||||
*/
|
||||
public static function VSprintf(string $sFormat, array $aArgs, bool $bLogErrors = true): string
|
||||
{
|
||||
// Extract all format specifiers
|
||||
$sPattern = '/%(?:(?:[1-9][0-9]*)\$)?[-+\'0# ]*(?:[0-9]*|\*)?(?:\.(?:[0-9]*|\*))?(?:[hlL])?[diouxXeEfFgGcrs%]/';
|
||||
preg_match_all($sPattern, $sFormat, $aMatches, PREG_OFFSET_CAPTURE);
|
||||
|
||||
// Process matches, keeping track of their positions and excluding escaped percent signs (%%)
|
||||
$aSpecifierMatches = [];
|
||||
foreach ($aMatches[0] as $sMatch) {
|
||||
if ($sMatch[0] !== '%%') {
|
||||
$aSpecifierMatches[] = $sMatch;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for positional specifiers and build position map
|
||||
$bHasPositional = false;
|
||||
$iMaxPosition = 0;
|
||||
$aPositions = [];
|
||||
$aUniquePositions = [];
|
||||
|
||||
foreach ($aSpecifierMatches as $index => $match) {
|
||||
$sSpec = $match[0];
|
||||
if (preg_match('/^%([1-9][0-9]*)\$/', $sSpec, $posMatch)) {
|
||||
$bHasPositional = true;
|
||||
$iPosition = (int)$posMatch[1] - 1; // Convert to 0-based
|
||||
$aPositions[$index] = $iPosition;
|
||||
$aUniquePositions[$iPosition] = true;
|
||||
$iMaxPosition = max($iMaxPosition, $iPosition + 1);
|
||||
} else {
|
||||
$aPositions[$index] = $index;
|
||||
$aUniquePositions[$index] = true;
|
||||
$iMaxPosition = max($iMaxPosition, $index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Count unique positions, this tells us how many arguments we actually need
|
||||
$iExpectedCount = count($aUniquePositions);
|
||||
$iActualCount = count($aArgs);
|
||||
|
||||
// If we have enough arguments, just use vsprintf
|
||||
if ($iActualCount >= $iExpectedCount) {
|
||||
return vsprintf($sFormat, $aArgs);
|
||||
}
|
||||
// else log the error if needed
|
||||
if ($bLogErrors) {
|
||||
IssueLog::Warning("Format string requires $iExpectedCount arguments, but only $iActualCount provided. Format: '$sFormat'" );
|
||||
}
|
||||
|
||||
// Create a replacement map
|
||||
if ($bHasPositional) {
|
||||
// For positional, we need to handle the exact positions
|
||||
$aReplacements = array_fill(0, $iMaxPosition, null);
|
||||
|
||||
// Fill in the real arguments first
|
||||
foreach ($aArgs as $index => $sValue) {
|
||||
if ($index < $iMaxPosition) {
|
||||
$aReplacements[$index] = $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
// For null values in the replacement map, use the original specifier
|
||||
foreach ($aSpecifierMatches as $index => $sMatch) {
|
||||
$iPosition = $aPositions[$index];
|
||||
if ($aReplacements[$iPosition] === null) {
|
||||
// Use the original format specifier when we don't have an argument
|
||||
$aReplacements[$iPosition] = $sMatch[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any remaining nulls (for positions that weren't referenced)
|
||||
$aReplacements = array_filter($aReplacements, static function($val) { return $val !== null; });
|
||||
} else {
|
||||
// For non-positional, we need to map each position
|
||||
$aReplacements = [];
|
||||
$iUsed = 0;
|
||||
|
||||
// Create a map of what values to use for each position
|
||||
$aPositionValues = [];
|
||||
for ($i = 0; $i < $iMaxPosition; $i++) {
|
||||
if (isset($aUniquePositions[$i])) {
|
||||
if ($iUsed < $iActualCount) {
|
||||
// We have an actual argument for this position
|
||||
$aPositionValues[$i] = $aArgs[$iUsed++];
|
||||
} else {
|
||||
// Mark this position to use the original specifier
|
||||
$aPositionValues[$i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build the replacements array preserving the original order
|
||||
foreach ($aSpecifierMatches as $index => $sMatch) {
|
||||
$iPosition = $aPositions[$index];
|
||||
if (isset($aPositionValues[$iPosition])) {
|
||||
$aReplacements[] = $aPositionValues[$iPosition];
|
||||
} else {
|
||||
// Use the original format specifier when we don't have an argument
|
||||
$aReplacements[] = $sMatch[0];
|
||||
// Mark this position as used, so if it appears again, it gets the same replacement
|
||||
$aPositionValues[$iPosition] = $sMatch[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process the format string with our filled-in arguments
|
||||
return vsprintf($sFormat, $aReplacements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string containing some (valid) HTML markup to plain text
|
||||
*
|
||||
@@ -2312,6 +2190,97 @@ SQL;
|
||||
return @getimagesizefromstring($sImageData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize an image attachment so that it fits in the given dimensions
|
||||
* @param ormDocument $oImage The original image stored as an ormDocument
|
||||
* @param int $iWidth Image's original width
|
||||
* @param int $iHeight Image's original height
|
||||
* @param int $iMaxImageWidth Maximum width for the resized image
|
||||
* @param int $iMaxImageHeight Maximum height for the resized image
|
||||
* @return ormDocument The resampled image
|
||||
*/
|
||||
public static function ResizeImageToFit(ormDocument $oImage, $iWidth, $iHeight, $iMaxImageWidth, $iMaxImageHeight)
|
||||
{
|
||||
// If image size smaller than maximums, we do nothing
|
||||
if (($iWidth <= $iMaxImageWidth) && ($iHeight <= $iMaxImageHeight))
|
||||
{
|
||||
return $oImage;
|
||||
}
|
||||
|
||||
|
||||
// If gd extension is not loaded, we put a warning in the log and return the image as is
|
||||
if (extension_loaded('gd') === false)
|
||||
{
|
||||
IssueLog::Warning('Image could not be resized as the "gd" extension does not seem to be loaded. It will remain as ' . $iWidth . 'x' . $iHeight . ' instead of ' . $iMaxImageWidth . 'x' . $iMaxImageHeight);
|
||||
return $oImage;
|
||||
}
|
||||
|
||||
|
||||
switch($oImage->GetMimeType())
|
||||
{
|
||||
case 'image/gif':
|
||||
case 'image/jpeg':
|
||||
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.");
|
||||
return $oImage;
|
||||
}
|
||||
if ($img === false)
|
||||
{
|
||||
//throw new Exception("Warning: corrupted image: '".$oImage->GetFileName()." / ".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
|
||||
return $oImage;
|
||||
}
|
||||
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"))
|
||||
{
|
||||
imagecolortransparent($new, imagecolorallocatealpha($new, 0, 0, 0, 127));
|
||||
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: {########-####-####-####-############}
|
||||
*
|
||||
@@ -2802,6 +2771,21 @@ TXT
|
||||
return static::$iNextId++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sInterface
|
||||
* @param string $sClassNameFilter
|
||||
* @param array $aExcludedPath Reg. exp. of the paths to exclude. Note that backslashes (typically for Windows env.) need to be 4 backslashes, 2 for the escaping backslash, 2 for the actual backslash 😅
|
||||
*
|
||||
* @return array classes are returned in the same order as the module dependency tree, so core classes on top
|
||||
* @since 3.0.0
|
||||
* @deprecated 3.2.0 Use {@see InterfaceDiscovery::FindItopClasses()} instead
|
||||
*/
|
||||
public static function GetClassesForInterface(string $sInterface, string $sClassNameFilter = '', $aExcludedPath = []): array
|
||||
{
|
||||
$oInterfaceDiscoveryService = InterfaceDiscovery::GetInstance();
|
||||
return $oInterfaceDiscoveryService->FindItopClasses($sInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array All keyboard shortcuts config as an array
|
||||
* @throws \CoreException
|
||||
|
||||
8
application/webpage.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader');
|
||||
@@ -350,6 +350,22 @@ class WizardHelper
|
||||
return $this->m_aData['m_bReturnNotEditableFields'] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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()
|
||||
{
|
||||
$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();
|
||||
|
||||
return <<<JS
|
||||
{$sWizardHelperJsVar}.m_oData = {$sWizardHelperJson};
|
||||
{$sWizardHelperJsVar}.UpdateFields();
|
||||
JS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add necessary JS snippets (to the page) to be executed for fields update
|
||||
*
|
||||
|
||||
8
application/xmlpage.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated 3.0.0 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-2024 Combodo SAS
|
||||
*/
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader');
|
||||
@@ -18,7 +18,7 @@
|
||||
"laminas/laminas-mail": "^2.11",
|
||||
"laminas/laminas-servicemanager": "^3.5",
|
||||
"league/oauth2-google": "^4.0.1",
|
||||
"nikic/php-parser": "~5.6.0",
|
||||
"nikic/php-parser": "^4.14.0",
|
||||
"pear/archive_tar": "~1.4.14",
|
||||
"pelago/emogrifier": "^7.2.0",
|
||||
"psr/log": "^3.0.0",
|
||||
@@ -72,6 +72,7 @@
|
||||
"sources"
|
||||
],
|
||||
"exclude-from-classmap": [
|
||||
"application/twigextension.class.inc.php",
|
||||
"core/oql/build/PHP/",
|
||||
"core/apc-emulation.php",
|
||||
"application/startup.inc.php",
|
||||
|
||||
748
composer.lock
generated
@@ -955,9 +955,14 @@ abstract class AttributeDefinition
|
||||
//abstract protected GetBasicFilterHTMLInput();
|
||||
abstract public function GetBasicFilterSQLExpr($sOpCode, $value);
|
||||
|
||||
public function GetMagicFields()
|
||||
/**
|
||||
* since 3.1.0 return has changed (N°4690 - Deprecate "FilterCodes")
|
||||
*
|
||||
* @return array filtercode => attributecode
|
||||
*/
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return [];
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetEditValue($sValue, $oHostObj = null)
|
||||
@@ -2783,6 +2788,11 @@ class AttributeDBFieldVoid extends AttributeDefinition
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array($this->GetCode() => $this->GetCode());
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array("=" => "equals", "!=" => "differs from");
|
||||
@@ -4250,6 +4260,13 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
return 64;
|
||||
}
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
// Note: due to this, you will get an error if a password is being declared as a search criteria (see ZLists)
|
||||
// not allowed to search on passwords!
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (utils::IsNullOrEmptyString($sValue))
|
||||
@@ -4290,6 +4307,13 @@ class AttributeEncryptedString extends AttributeString implements iAttributeNoGr
|
||||
return 255;
|
||||
}
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
// Note: due to this, you will get an error if a an encrypted field is declared as a search criteria (see ZLists)
|
||||
// not allowed to search on encrypted fields !
|
||||
return array();
|
||||
}
|
||||
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue))
|
||||
@@ -4441,7 +4465,7 @@ class AttributeText extends AttributeString
|
||||
{
|
||||
// Is there a way to know the current limitation for mysql?
|
||||
// See mysql_field_len()
|
||||
return 65535;
|
||||
return 16383; // number of characters (that can be 1-4 bytes long), not of bytes
|
||||
}
|
||||
|
||||
public static function RenderWikiHtml($sText, $bWikiOnly = false)
|
||||
@@ -4996,7 +5020,7 @@ class AttributeCaseLog extends AttributeLongText
|
||||
}
|
||||
else
|
||||
{
|
||||
if (utils::StrLen($proposedValue) > 0)
|
||||
if (strlen($proposedValue) > 0)
|
||||
{
|
||||
//N°5135 - add impersonation information in caselog
|
||||
if (UserRights::IsImpersonated()){
|
||||
@@ -7962,6 +7986,11 @@ class AttributeExternalField extends AttributeDefinition
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array($this->GetCode() => $this->GetCode());
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
@@ -8450,6 +8479,11 @@ class AttributeBlob extends AttributeDefinition
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
@@ -9089,19 +9123,20 @@ class AttributeStopWatch extends AttributeDefinition
|
||||
return $aColumns;
|
||||
}
|
||||
|
||||
public function GetMagicFields()
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
$aRes =[
|
||||
$this->GetCode().'_started' ,
|
||||
$this->GetCode().'_laststart',
|
||||
$this->GetCode().'_stopped' ,
|
||||
];
|
||||
$aRes = array(
|
||||
$this->GetCode() => $this->GetCode(),
|
||||
$this->GetCode().'_started' => $this->GetCode(),
|
||||
$this->GetCode().'_laststart' => $this->GetCode(),
|
||||
$this->GetCode().'_stopped' => $this->GetCode(),
|
||||
);
|
||||
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
|
||||
$sPrefix = $this->GetCode().'_'.$iThreshold;
|
||||
$aRes[] = $sPrefix.'_deadline';
|
||||
$aRes[] = $sPrefix.'_passed';
|
||||
$aRes[] = $sPrefix.'_triggered';
|
||||
$aRes[] = $sPrefix.'_overrun';
|
||||
$aRes[$sPrefix.'_deadline'] = $this->GetCode();
|
||||
$aRes[$sPrefix.'_passed'] = $this->GetCode();
|
||||
$aRes[$sPrefix.'_triggered'] = $this->GetCode();
|
||||
$aRes[$sPrefix.'_overrun'] = $this->GetCode();
|
||||
}
|
||||
|
||||
return $aRes;
|
||||
@@ -9877,6 +9912,11 @@ class AttributeSubItem extends AttributeDefinition
|
||||
return array();
|
||||
}
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array($this->GetCode() => $this->GetCode());
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
@@ -10159,6 +10199,12 @@ class AttributeOneWayPassword extends AttributeDefinition implements iAttributeN
|
||||
return $oPassword;
|
||||
}
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array();
|
||||
// still not working... see later...
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array();
|
||||
@@ -10933,12 +10979,12 @@ abstract class AttributeSet extends AttributeDBFieldVoid
|
||||
$sDescription = utils::EscapeHtml($this->GetValueDescription($sValue));
|
||||
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sValue'");
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
|
||||
$sFilter = rawurlencode($oFilter->serialize());
|
||||
$sLink = '';
|
||||
if ($bWithLink && $this->bDisplayLink) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||
$sLink = ' href="'.$sUrl.'"';
|
||||
}
|
||||
|
||||
@@ -12255,13 +12301,13 @@ class AttributeTagSet extends AttributeSet
|
||||
$sTagDescription = $oTag->Get('description');
|
||||
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
|
||||
$sFilter = rawurlencode($oFilter->serialize());
|
||||
|
||||
$sLink = '';
|
||||
if ($bWithLink && $this->bDisplayLink) {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||
$sLink = ' href="'.$sUrl.'"';
|
||||
}
|
||||
|
||||
@@ -12606,6 +12652,11 @@ class AttributeFriendlyName extends AttributeDefinition
|
||||
return '';
|
||||
}
|
||||
|
||||
public function GetFilterDefinitions()
|
||||
{
|
||||
return array($this->GetCode() => $this->GetCode());
|
||||
}
|
||||
|
||||
public function GetBasicFilterOperators()
|
||||
{
|
||||
return array("=" => "equals", "!=" => "differs from");
|
||||
@@ -13060,7 +13111,7 @@ class AttributeRedundancySettings extends AttributeDBField
|
||||
$sEditValue = $bSelected ? $iCurrentValue : '';
|
||||
$sValue = '<input class="redundancy-min-up-count" type="string" size="3" name="'.$sName.'" value="'.$sEditValue.'">';
|
||||
// To fix an issue on Firefox: focus set to the option (because the input is within the label for the option)
|
||||
$oPage->add_ready_script("\$('[name=\"$sName\"]').on('click', function(){var me=this; setTimeout(function(){\$(me).trigger('focus');}, 100);});");
|
||||
$oPage->add_ready_script("\$('[name=\"$sName\"]').on('click', function(){var me=this; setTimeout(function(){\$(me).focus();}, 100);});");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -13075,7 +13126,7 @@ class AttributeRedundancySettings extends AttributeDBField
|
||||
$sEditValue = $bSelected ? $iCurrentValue : '';
|
||||
$sValue = '<input class="redundancy-min-up-percent" type="string" size="3" name="'.$sName.'" value="'.$sEditValue.'">';
|
||||
// To fix an issue on Firefox: focus set to the option (because the input is within the label for the option)
|
||||
$oPage->add_ready_script("\$('[name=\"$sName\"]').on('click', function(){var me=this; setTimeout(function(){\$(me).trigger('focus');}, 100);});");
|
||||
$oPage->add_ready_script("\$('[name=\"$sName\"]').on('click', function(){var me=this; setTimeout(function(){\$(me).focus();}, 100);});");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -472,10 +472,8 @@ class BulkChange
|
||||
protected $m_bLocalizedValues;
|
||||
/** @var array Cache for resolving external keys based on the given search criterias */
|
||||
protected $m_aExtKeysMappingCache;
|
||||
/** @var int number of columns */
|
||||
protected $m_iNbCol;
|
||||
|
||||
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false, $iNbCol = 0)
|
||||
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_aData = $aData;
|
||||
@@ -487,7 +485,6 @@ class BulkChange
|
||||
$this->m_sDateFormat = $sDateFormat;
|
||||
$this->m_bLocalizedValues = $bLocalize;
|
||||
$this->m_aExtKeysMappingCache = array();
|
||||
$this->m_iNbCol =$iNbCol;
|
||||
}
|
||||
|
||||
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
|
||||
@@ -1239,14 +1236,19 @@ class BulkChange
|
||||
$iPreviousTimeLimit = ini_get('max_execution_time');
|
||||
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
|
||||
|
||||
$iNBFields = count($this->m_aAttList);
|
||||
foreach ($this->m_aExtKeys as $aReconcilKeys) {
|
||||
$iNBFields += count($aReconcilKeys);
|
||||
}
|
||||
|
||||
// Avoid too many events
|
||||
cmdbAbstractObject::SetEventDBLinksChangedBlocked(true);
|
||||
try {
|
||||
foreach ($this->m_aData as $iRow => $aRowData) {
|
||||
set_time_limit(intval($iLoopTimeLimit));
|
||||
// Stop if not the good number of cols in $aRowData
|
||||
if($this->m_iNbCol>0 && count($aRowData) != $this->m_iNbCol){
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-NbField',count($aRowData),$this->m_iNbCol) );
|
||||
if(count($aRowData) != $iNBFields){
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-NbField',count($aRowData),$iNBFields) );
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1426,7 +1428,7 @@ class BulkChange
|
||||
$aDetails = array();
|
||||
while ($oChange = $oBulkChanges->Fetch())
|
||||
{
|
||||
$sDate = '<a href="csvimport.php?step=10&changeid='.$oChange->GetKey().$oAppContext->GetForLink(true).'">'.$oChange->Get('date').'</a>';
|
||||
$sDate = '<a href="csvimport.php?step=10&changeid='.$oChange->GetKey().'&'.$oAppContext->GetForLink().'">'.$oChange->Get('date').'</a>';
|
||||
$sUser = $oChange->GetUserName();
|
||||
if (preg_match('/^(.*)\\(CSV\\)$/i', $oChange->Get('userinfo'), $aMatches))
|
||||
{
|
||||
@@ -1508,7 +1510,7 @@ EOF
|
||||
function OnTruncatedHistoryToggle(bShowAll)
|
||||
{
|
||||
$('#csv_history_reload').html('<img src="' + GetAbsoluteUrlAppRoot() + 'images/indicator.gif"/>');
|
||||
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?$sAppContext', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
|
||||
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
|
||||
{
|
||||
$('#$sAjaxDivId').html(data);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
|
||||
class BulkExportException extends Exception
|
||||
{
|
||||
protected $sLocalizedMessage;
|
||||
public function __construct($message, $sLocalizedMessage, $code = 0, $previous = null)
|
||||
public function __construct($message, $sLocalizedMessage, $code = null, $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->sLocalizedMessage = $sLocalizedMessage;
|
||||
|
||||
@@ -39,6 +39,7 @@ require_once('kpi.class.inc.php');
|
||||
require_once('dict.class.inc.php');
|
||||
|
||||
require_once('attributedef.class.inc.php');
|
||||
require_once('filterdef.class.inc.php');
|
||||
require_once('stimulus.class.inc.php');
|
||||
require_once('valuesetdef.class.inc.php');
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
@@ -396,7 +397,60 @@ abstract class CMDBObject extends DBObject
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to ultimately check user rights before writing (Insert, Update or Delete)
|
||||
* The check should never fail, because the UI should prevent from such a usage
|
||||
* Anyhow, if the user has found a workaround... the security gets enforced here
|
||||
*
|
||||
* @deprecated 3.0.0 N°2591 will be removed in 3.1.0
|
||||
*
|
||||
* @param bool $bSkipStrongSecurity
|
||||
* @param int $iActionCode
|
||||
*
|
||||
* @throws \SecurityException
|
||||
*/
|
||||
protected function CheckUserRights($bSkipStrongSecurity, $iActionCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
if (is_null($bSkipStrongSecurity)) {
|
||||
// This is temporary
|
||||
// We have implemented this safety net right before releasing iTop 1.0
|
||||
// and we decided that it was too risky to activate it
|
||||
// Anyhow, users willing to have a very strong security could set
|
||||
// skip_strong_security = 0, in the config file
|
||||
$bSkipStrongSecurity = MetaModel::GetConfig()->Get('skip_strong_security');
|
||||
}
|
||||
if (!$bSkipStrongSecurity)
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
$oSet = DBObjectSet::FromObject($this);
|
||||
if (!UserRights::IsActionAllowed($sClass, $iActionCode, $oSet))
|
||||
{
|
||||
// Intrusion detected
|
||||
throw new SecurityException('You are not allowed to modify objects of class: '.$sClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function DBClone($newKey = null)
|
||||
{
|
||||
return $this->DBCloneTracked_Internal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
|
||||
{
|
||||
self::SetCurrentChange($oChange);
|
||||
$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);
|
||||
$oClone = MetaModel::GetObject(get_class($this), $newKey);
|
||||
@@ -425,6 +479,16 @@ abstract class CMDBObject extends DBObject
|
||||
return $oDeletionPlan;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
$ret = parent::DBDelete($oDeletionPlan);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function DBArchive()
|
||||
{
|
||||
// Note: do the job anyway, so as to repair any DB discrepancy
|
||||
|
||||
@@ -308,6 +308,15 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
// Deprecated in 3.0.0 N°2591 Will be removed in 3.1
|
||||
'skip_strong_security' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Disable strong security - TEMPORARY: this flag should be removed when we are more confident in the recent change in security',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'query_optimization_enabled' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'The queries are optimized based on the assumption that the DB integrity has been preserved. By disabling the optimization one can ensure that the fetched data is clean... but this can be really slower or not usable at all (some queries will exceed the allowed number of joins in MySQL: 61!)',
|
||||
@@ -1224,14 +1233,6 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'sessions_tracking.session_handler_extension' => [
|
||||
'type' => 'string',
|
||||
'description' => 'to store more data in itop session files, set your own iSessionHandlerExtension implementation class in this variable',
|
||||
'default' => '',
|
||||
'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)',
|
||||
@@ -2856,8 +2857,20 @@ class Config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RunTimeEnvironment::CallInstallerHandler($aModuleInfo, "BeforeWritingConfig", [$this]);
|
||||
if (isset($aModuleInfo['installer']))
|
||||
{
|
||||
$sModuleInstallerClass = $aModuleInfo['installer'];
|
||||
if (!class_exists($sModuleInstallerClass))
|
||||
{
|
||||
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not a PHP class - Module: ".$aModuleInfo['label']);
|
||||
}
|
||||
if (!is_subclass_of($sModuleInstallerClass, 'ModuleInstallerAPI'))
|
||||
{
|
||||
throw new Exception("Wrong installer class: '$sModuleInstallerClass' is not derived from 'ModuleInstallerAPI' - Module: ".$aModuleInfo['label']);
|
||||
}
|
||||
$aCallSpec = array($sModuleInstallerClass, 'BeforeWritingConfig');
|
||||
call_user_func_array($aCallSpec, array($this));
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->SetAddOns($aAddOns);
|
||||
|
||||
11
core/coreexception.class.inc.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is only here for compatibility reasons.
|
||||
* It will be removed in future iTop versions (N°6533)
|
||||
*
|
||||
* @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');
|
||||
@@ -760,10 +760,10 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
public function SetTrim($sAttCode, $sValue)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$iMaxSize = $oAttDef->GetMaxSize();
|
||||
$sLength = mb_strlen($sValue);
|
||||
if ($iMaxSize && ($sLength > $iMaxSize)) {
|
||||
if (!$this->StringFitsInField($sAttCode, $sValue)) {
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$iMaxSize = $oAttDef->GetMaxSize();
|
||||
$sLength = mb_strlen($sValue);
|
||||
$sMessage = " -truncated ($sLength chars)";
|
||||
$sValue = mb_substr($sValue, 0, $iMaxSize - mb_strlen($sMessage)).$sMessage;
|
||||
}
|
||||
@@ -818,6 +818,24 @@ abstract class DBObject implements iDisplay
|
||||
$oKPI->ComputeStatsForExtension($this, 'AfterDelete');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sAttCode
|
||||
* @param string $sValue
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*
|
||||
* @Since 3.2.2
|
||||
*/
|
||||
public function StringFitsInField(string $sAttCode, string $sValue): bool
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$iMaxSize = $oAttDef->GetMaxSize();
|
||||
$sLength = mb_strlen($sValue);
|
||||
|
||||
return !($iMaxSize && ($sLength > $iMaxSize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute (and optionally start) the StopWatches deadlines
|
||||
*
|
||||
|
||||
@@ -417,17 +417,8 @@ class DBObjectSearch extends DBSearch
|
||||
*/
|
||||
public function AddCondition($sFilterCode, $value, $sOpCode = null, $bParseSearchString = false)
|
||||
{
|
||||
if (MetaModel::IsValidFilterCode($this->GetClass(),$sFilterCode) == false){
|
||||
/* $sArrayDesc = if (count($aData) == 0)
|
||||
{
|
||||
$sArrayDesc = "{}";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sArrayDesc = "{".implode(", ", $aData)."}";
|
||||
}*/
|
||||
throw new CoreException("Wrong value for '".$this->GetClass()."', found '$sFilterCode'");// while expecting a value in $sArrayDesc");
|
||||
}
|
||||
MyHelpers::CheckKeyInArray('filter code in class: '.$this->GetClass(), $sFilterCode, MetaModel::GetFilterAttribList($this->GetClass()));
|
||||
|
||||
$oField = new FieldExpression($sFilterCode, $this->GetClassAlias());
|
||||
if (empty($sOpCode)) {
|
||||
if ($sFilterCode == 'id') {
|
||||
@@ -1373,41 +1364,44 @@ class DBObjectSearch extends DBSearch
|
||||
$this->m_aParams[$sKey] = $value;
|
||||
}
|
||||
|
||||
public function GetQueryParams()
|
||||
public function GetQueryParams($bExcludeMagicParams = true)
|
||||
{
|
||||
$aParams = array();
|
||||
$this->m_oSearchCondition->RenderExpression(false, $aParams, true);
|
||||
|
||||
$aRet = array();
|
||||
if ($bExcludeMagicParams)
|
||||
{
|
||||
$aRet = array();
|
||||
|
||||
// Make the list of acceptable arguments... could be factorized with run_query, into oSearch->GetQueryParams()
|
||||
$aNakedMagicArguments = array();
|
||||
foreach (MetaModel::PrepareQueryArguments(array(),array(), $this->GetExpectedArguments()) as $sArgName => $value)
|
||||
{
|
||||
$iPos = strpos($sArgName, '->object()');
|
||||
if ($iPos === false)
|
||||
// Make the list of acceptable arguments... could be factorized with run_query, into oSearch->GetQueryParams($bExclude magic params)
|
||||
$aNakedMagicArguments = array();
|
||||
foreach (MetaModel::PrepareQueryArguments(array(),array(), $this->GetExpectedArguments()) as $sArgName => $value)
|
||||
{
|
||||
$aNakedMagicArguments[$sArgName] = $value;
|
||||
$iPos = strpos($sArgName, '->object()');
|
||||
if ($iPos === false)
|
||||
{
|
||||
$aNakedMagicArguments[$sArgName] = $value;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aNakedMagicArguments[substr($sArgName, 0, $iPos)] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
foreach ($aParams as $sParam => $foo)
|
||||
{
|
||||
$aNakedMagicArguments[substr($sArgName, 0, $iPos)] = true;
|
||||
}
|
||||
}
|
||||
foreach ($aParams as $sParam => $foo)
|
||||
{
|
||||
$iPos = strpos($sParam, '->');
|
||||
if ($iPos === false)
|
||||
{
|
||||
$sRefName = $sParam;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRefName = substr($sParam, 0, $iPos);
|
||||
}
|
||||
if (!array_key_exists($sRefName, $aNakedMagicArguments))
|
||||
{
|
||||
$aRet[$sParam] = $foo;
|
||||
$iPos = strpos($sParam, '->');
|
||||
if ($iPos === false)
|
||||
{
|
||||
$sRefName = $sParam;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sRefName = substr($sParam, 0, $iPos);
|
||||
}
|
||||
if (!array_key_exists($sRefName, $aNakedMagicArguments))
|
||||
{
|
||||
$aRet[$sParam] = $foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -372,7 +372,7 @@ abstract class DBSearch
|
||||
* @internal
|
||||
*
|
||||
* @param string $sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
|
||||
* Example: infra_list->ci_id->location_id->country
|
||||
* Example: infra_list->ci_id->location_id->country
|
||||
* @param mixed $value The value to match (can be an array => IN(val1, val2...)
|
||||
* @return void
|
||||
*/
|
||||
@@ -533,9 +533,11 @@ abstract class DBSearch
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @param bool $bExcludeMagicParams
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function GetQueryParams();
|
||||
abstract public function GetQueryParams($bExcludeMagicParams = true);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -769,7 +771,7 @@ abstract class DBSearch
|
||||
$oKPI = new ExecutionKPI();
|
||||
$result = apc_fetch($sAPCCacheId);
|
||||
$oKPI->ComputeStats('Search APC (fetch)', $sQuery);
|
||||
|
||||
|
||||
if (is_object($result))
|
||||
{
|
||||
$oResultFilter = $result;
|
||||
@@ -785,17 +787,17 @@ abstract class DBSearch
|
||||
|
||||
$oOql = new OqlInterpreter($sQuery);
|
||||
$oOqlQuery = $oOql->ParseQuery();
|
||||
|
||||
|
||||
if ($oMetaModel === null)
|
||||
{
|
||||
$oMetaModel = new ModelReflectionRuntime();
|
||||
}
|
||||
$oOqlQuery->Check($oMetaModel, $sQuery); // Exceptions thrown in case of issue
|
||||
|
||||
|
||||
$oResultFilter = $oOqlQuery->ToDBSearch($sQuery);
|
||||
|
||||
$oKPI->ComputeStats('Parse OQL', $sQuery);
|
||||
|
||||
|
||||
if ($bOQLCacheEnabled)
|
||||
{
|
||||
self::$m_aOQLQueries[$sQueryId] = $oResultFilter->DeepClone();
|
||||
@@ -1087,17 +1089,17 @@ abstract class DBSearch
|
||||
{
|
||||
$aOrderSpec[$sSQLExpression] = $bAscending;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aOrderSpec['`'.$sAttClassAlias.$sAttCode.'`'] = $bAscending;
|
||||
}
|
||||
|
||||
// Make sure that the columns used for sorting are present in the loaded columns
|
||||
if (!is_null($aAttToLoad) && !isset($aAttToLoad[$sAttClassAlias][$sAttCode]))
|
||||
{
|
||||
$aAttToLoad[$sAttClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sAttClass, $sAttCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aOrderSpec['`'.$sAttClassAlias.$sAttCode.'`'] = $bAscending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oSQLQuery = $this->GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount);
|
||||
@@ -1255,7 +1257,7 @@ abstract class DBSearch
|
||||
$oSQLQueryExt = new SQLObjectQuery($aExtendedDataSpec['table'], $sTableAlias, $aExtendedFields);
|
||||
$oSQLQuery->AddInnerJoin($oSQLQueryExt, 'id', $aExtendedDataSpec['join_key'] /*, $sTableAlias*/);
|
||||
}
|
||||
|
||||
|
||||
return $oSQLQuery;
|
||||
}
|
||||
|
||||
@@ -1590,7 +1592,7 @@ abstract class DBSearch
|
||||
}
|
||||
$aBacktrace = debug_backtrace();
|
||||
$iCallStackPos = count($aBacktrace) - self::$m_bDebugQuery;
|
||||
$sIndent = "";
|
||||
$sIndent = "";
|
||||
for ($i = 0 ; $i < $iCallStackPos ; $i++)
|
||||
{
|
||||
$sIndent .= " .-=^=-. ";
|
||||
|
||||
@@ -479,12 +479,12 @@ class DBUnionSearch extends DBSearch
|
||||
return $aParams;
|
||||
}
|
||||
|
||||
public function GetQueryParams()
|
||||
public function GetQueryParams($bExcludeMagicParams = true)
|
||||
{
|
||||
$aParams = array();
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$aParams = array_merge($oSearch->GetQueryParams(), $aParams);
|
||||
$aParams = array_merge($oSearch->GetQueryParams($bExcludeMagicParams), $aParams);
|
||||
}
|
||||
return $aParams;
|
||||
}
|
||||
|
||||
@@ -501,8 +501,7 @@ class DesignElement extends \DOMElement
|
||||
{
|
||||
$sSearchId = $oRefNode->getAttribute('id');
|
||||
}
|
||||
$sQuotedId = DesignDocument::XPathQuote($sSearchId);
|
||||
$sXPath = './'.$oRefNode->tagName."[@id=$sQuotedId]";
|
||||
$sXPath = './'.$oRefNode->tagName."[@id='$sSearchId']";
|
||||
|
||||
$oRes = $oXPath->query($sXPath, $oRoot);
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ class Dict
|
||||
}
|
||||
|
||||
try{
|
||||
return utils::VSprintf($sLocalizedFormat, $aArguments);
|
||||
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);
|
||||
|
||||
@@ -1416,6 +1416,32 @@ class DisplayableGraph extends SimpleGraph
|
||||
return $aContextDefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param ApplicationContext $oAppContext
|
||||
* @param array $aExcludedObjects
|
||||
* @param string $sObjClass
|
||||
* @param int $iObjKey
|
||||
* @param string $sContextKey
|
||||
* @param array $aContextParams
|
||||
* @param bool $bLazyLoading since 2.7.7 3.0.1
|
||||
*
|
||||
* @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
|
||||
*
|
||||
@@ -1444,8 +1470,8 @@ class DisplayableGraph extends SimpleGraph
|
||||
try {
|
||||
$this->InitFromGraphviz();
|
||||
$sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
|
||||
$sContext = $oAppContext->GetForLink(true);
|
||||
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s'.$sContext;
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s&'.$sContext;
|
||||
$sExportAsDocumentURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_attachment&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
|
||||
$sLoadFromURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_json&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
|
||||
$sAttachmentExportTitle = '';
|
||||
@@ -1555,6 +1581,31 @@ EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sContextKey
|
||||
* @param array $aContextParams
|
||||
* @param array $aExcludedObjects
|
||||
* @param WebPage $oP
|
||||
* @param array $aResults
|
||||
* @param bool $bLazyLoading
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \ReflectionException
|
||||
* @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
|
||||
|
||||
8
core/expression.class.inc.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
// The file has been moved in iTop 2.2.0+ (revision 3803)
|
||||
// Preserve backward compatibility with some external tools (Cf. toolkit)
|
||||
|
||||
// @deprecated 3.1.0
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use core/oql/expression.class.inc.php instead');
|
||||
|
||||
require_once(APPROOT.'core/oql/expression.class.inc.php');
|
||||
216
core/filterdef.class.inc.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2024 Combodo SAS
|
||||
//
|
||||
// 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/>
|
||||
|
||||
|
||||
/**
|
||||
* Definition of a filter
|
||||
* Most of the time, a filter corresponds to an attribute, but we could imagine other search criteria
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
* Definition of a filter (could be made out of an existing attribute, or from an expression)
|
||||
*
|
||||
* @deprecated 3.1.0 not used N°4690 - Deprecate "FilterCodes"
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class FilterDefinition
|
||||
{
|
||||
abstract public function GetType();
|
||||
abstract public function GetTypeDesc();
|
||||
|
||||
protected $m_sCode;
|
||||
private $m_aParams = array();
|
||||
protected function Get($sParamName) {return $this->m_aParams[$sParamName];}
|
||||
|
||||
public function __construct($sCode, $aParams = array())
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod("Deprecated class ".$this->GetClass().". Do not use. Will be removed in next version.");
|
||||
$this->m_sCode = $sCode;
|
||||
$this->m_aParams = $aParams;
|
||||
$this->ConsistencyCheck();
|
||||
}
|
||||
|
||||
// to be overloaded
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
private function ConsistencyCheck()
|
||||
{
|
||||
// Check that any mandatory param has been specified
|
||||
//
|
||||
$aExpectedParams = $this->ListExpectedParams();
|
||||
foreach($aExpectedParams as $sParamName)
|
||||
{
|
||||
if (!array_key_exists($sParamName, $this->m_aParams))
|
||||
{
|
||||
$aBacktrace = debug_backtrace();
|
||||
$sTargetClass = $aBacktrace[2]["class"];
|
||||
$sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"];
|
||||
throw new CoreException("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function GetCode() {return $this->m_sCode;}
|
||||
abstract public function GetLabel();
|
||||
abstract public function GetValuesDef();
|
||||
|
||||
// returns an array of opcode=>oplabel (e.g. "differs from")
|
||||
abstract public function GetOperators();
|
||||
// returns an opcode
|
||||
abstract public function GetLooseOperator();
|
||||
abstract public function GetSQLExpressions();
|
||||
|
||||
// Wrapper - no need for overloading this one
|
||||
public function GetOpDescription($sOpCode)
|
||||
{
|
||||
$aOperators = $this->GetOperators();
|
||||
if (!array_key_exists($sOpCode, $aOperators))
|
||||
{
|
||||
throw new CoreException("Unknown operator '$sOpCode'");
|
||||
}
|
||||
|
||||
return $aOperators[$sOpCode];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match against the object unique identifier
|
||||
*
|
||||
* @deprecated 3.1.0 N°4690 - Deprecate "FilterCodes"
|
||||
* @package iTopORM
|
||||
*/
|
||||
class FilterPrivateKey extends FilterDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("id_field"));
|
||||
}
|
||||
|
||||
public function GetType() {return "PrivateKey";}
|
||||
public function GetTypeDesc() {return "Match against object identifier";}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return "Object Private Key";
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetOperators()
|
||||
{
|
||||
return array(
|
||||
"="=>"equals",
|
||||
"!="=>"differs from",
|
||||
"IN"=>"in",
|
||||
"NOTIN"=>"not in"
|
||||
);
|
||||
}
|
||||
public function GetLooseOperator()
|
||||
{
|
||||
return "IN";
|
||||
}
|
||||
|
||||
public function GetSQLExpressions()
|
||||
{
|
||||
return array(
|
||||
'' => $this->Get("id_field"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match against an existing attribute (the attribute type will determine the available operators)
|
||||
*
|
||||
* @deprecated 3.1.0 N°4690 - Deprecate "FilterCodes"
|
||||
* @package iTopORM
|
||||
*/
|
||||
class FilterFromAttribute extends FilterDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("refattribute"));
|
||||
}
|
||||
|
||||
public function __construct($oRefAttribute, $sSuffix = '')
|
||||
{
|
||||
// In this very specific case, the code is the one of the attribute
|
||||
// (this to get a very very simple syntax upon declaration)
|
||||
$aParam = array();
|
||||
$aParam["refattribute"] = $oRefAttribute;
|
||||
parent::__construct($oRefAttribute->GetCode().$sSuffix, $aParam);
|
||||
}
|
||||
|
||||
public function GetType() {return "Basic";}
|
||||
public function GetTypeDesc() {return "Match against field contents";}
|
||||
|
||||
public function __GetRefAttribute() // for checking purposes only !!!
|
||||
{
|
||||
return $oAttDef = $this->Get("refattribute");
|
||||
}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetLabel();
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetValuesDef();
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetAllowedValues($aArgs, $sContains);
|
||||
}
|
||||
|
||||
public function GetOperators()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetBasicFilterOperators();
|
||||
}
|
||||
public function GetLooseOperator()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetSQLExpressions()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetSQLExpressions();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -37,7 +37,7 @@ class iTopConfigParser
|
||||
*/
|
||||
public function __construct($sConfig)
|
||||
{
|
||||
$oParser = (new ParserFactory())->createForNewestSupportedVersion();
|
||||
$oParser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7);
|
||||
|
||||
$this->aVarsMap = array(
|
||||
'MySettings' => array(),
|
||||
|
||||
@@ -407,12 +407,86 @@ JS
|
||||
* Resize an image so that it fits the maximum width/height defined in the config file
|
||||
* @param ormDocument $oImage The original image stored as an array (content / mimetype / filename)
|
||||
* @return ormDocument The resampled image (or the original one if it already fit)
|
||||
* @deprecated Replaced by ormDocument::ResizeImageToFit
|
||||
*/
|
||||
public static function ResizeImageToFit(ormDocument $oImage, &$aDimensions = null)
|
||||
{
|
||||
$iMaxImageSize = (int)MetaModel::GetConfig()->Get('inline_image_max_storage_width', 0);
|
||||
return $oImage->ResizeImageToFit($iMaxImageSize, $iMaxImageSize, $aDimensions);
|
||||
$img = false;
|
||||
switch($oImage->GetMimeType())
|
||||
{
|
||||
case 'image/gif':
|
||||
case 'image/jpeg':
|
||||
case 'image/png':
|
||||
$img = @imagecreatefromstring($oImage->GetData());
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unsupported image type, return the image as-is
|
||||
$aDimensions = null;
|
||||
return $oImage;
|
||||
}
|
||||
if ($img === false)
|
||||
{
|
||||
$aDimensions = null;
|
||||
return $oImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Let's scale the image, preserving the transparency for GIFs and PNGs
|
||||
$iWidth = imagesx($img);
|
||||
$iHeight = imagesy($img);
|
||||
$aDimensions = array('width' => $iWidth, 'height' => $iHeight);
|
||||
$iMaxImageSize = (int)MetaModel::GetConfig()->Get('inline_image_max_storage_width', 0);
|
||||
|
||||
if (($iMaxImageSize > 0) && ($iWidth <= $iMaxImageSize) && ($iHeight <= $iMaxImageSize))
|
||||
{
|
||||
// No need to resize
|
||||
return $oImage;
|
||||
}
|
||||
|
||||
$fScale = min($iMaxImageSize / $iWidth, $iMaxImageSize / $iHeight);
|
||||
|
||||
$iNewWidth = (int) ($iWidth * $fScale);
|
||||
$iNewHeight = (int) ($iHeight * $fScale);
|
||||
|
||||
$aDimensions['width'] = $iNewWidth;
|
||||
$aDimensions['height'] = $iNewHeight;
|
||||
|
||||
$new = imagecreatetruecolor($iNewWidth, $iNewHeight);
|
||||
|
||||
// Preserve transparency
|
||||
if(($oImage->GetMimeType() == "image/gif") || ($oImage->GetMimeType() == "image/png"))
|
||||
{
|
||||
imagecolortransparent($new, imagecolorallocatealpha($new, 0, 0, 0, 127));
|
||||
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;
|
||||
}
|
||||
$oNewImage = new ormDocument(ob_get_contents(), $oImage->GetMimeType(), $oImage->GetFileName());
|
||||
@ob_end_clean();
|
||||
|
||||
imagedestroy($img);
|
||||
imagedestroy($new);
|
||||
|
||||
return $oNewImage;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -469,8 +469,7 @@ class ExecutionKPI
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
//$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$sExtension = '';
|
||||
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_STATS,
|
||||
$sOperation,
|
||||
|
||||
@@ -1134,6 +1134,22 @@ abstract class MetaModel
|
||||
return self::$m_aAttribOrigins[$sClass][$sAttCode];
|
||||
}
|
||||
|
||||
/** *
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CoreException
|
||||
*/
|
||||
final public static function GetFilterCodeOrigin($sClass, $sAttCode)
|
||||
{
|
||||
if ($sAttCode == 'id') {
|
||||
return MetaModel::GetRootClass($sClass);
|
||||
}
|
||||
|
||||
return MetaModel::GetAttributeOrigin($sClass, self::$m_aFilterAttribList[$sClass][$sAttCode]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
@@ -1492,21 +1508,9 @@ abstract class MetaModel
|
||||
*/
|
||||
final public static function GetFiltersList($sClass)
|
||||
{
|
||||
$aFilterList = MetaModel::GetAttributesList($sClass);
|
||||
if (array_key_exists($sClass, self::$m_aFilterForbiddenAttributes)) {
|
||||
// Remove the attributes that are not allowed in filters
|
||||
foreach (self::$m_aFilterForbiddenAttributes[$sClass] as $sAttCode) {
|
||||
if (array_key_exists($sAttCode, $aFilterList)) {
|
||||
unset($aFilterList[$sAttCode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (array_key_exists($sClass, self::$m_aMagicFields)) {
|
||||
// Add the magic fields
|
||||
$aFilterList = array_merge($aFilterList,self::$m_aMagicFields[$sClass]);
|
||||
}
|
||||
$aFilterList[] = 'id';
|
||||
return $aFilterList;
|
||||
self::_check_subclass($sClass);
|
||||
|
||||
return array_keys(self::$m_aFilterAttribList[$sClass]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1609,16 +1613,11 @@ abstract class MetaModel
|
||||
*/
|
||||
final public static function IsValidFilterCode($sClass, $sFilterCode)
|
||||
{
|
||||
if ($sFilterCode == 'id') {
|
||||
return true;
|
||||
}
|
||||
if (array_key_exists($sClass, self::$m_aMagicFields) && array_key_exists($sFilterCode, self::$m_aMagicFields[$sClass])) {
|
||||
return true;
|
||||
}
|
||||
if (array_key_exists($sClass, self::$m_aFilterForbiddenAttributes) && array_key_exists($sFilterCode, self::$m_aFilterForbiddenAttributes[$sClass])) {
|
||||
if (!array_key_exists($sClass, self::$m_aFilterAttribList)) {
|
||||
return false;
|
||||
}
|
||||
return self::IsValidAttCode($sClass, $sFilterCode);
|
||||
|
||||
return (array_key_exists($sFilterCode, self::$m_aFilterAttribList[$sClass]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1900,14 +1899,54 @@ abstract class MetaModel
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array array of MagicFieldName
|
||||
* @var array array of (FilterCode => AttributeCode)
|
||||
*/
|
||||
private static $m_aMagicFields = [];
|
||||
private static $m_aFilterAttribList = array();
|
||||
|
||||
/**
|
||||
* @var array array of filter Forbidden Attributes
|
||||
* @deprecated 3.0.0 do not use : dead code, will be removed in the future N°4690 - Deprecate "FilterCodes"
|
||||
* instead of array_keys(MetaModel::GetClassFilterDefs($sClass)); use MetaModel::GetFiltersList($sClass)
|
||||
*
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CoreException
|
||||
*/
|
||||
private static $m_aFilterForbiddenAttributes = [];
|
||||
public static function GetClassFilterDefs($sClass)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use MetaModel::GetClassFilterDefs: dead code, will be removed in the future. Use MetaModel::GetFiltersList or MetaModel::GetFiltersAttributes');
|
||||
|
||||
return self::$m_aFilterAttribList[$sClass];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $sClass
|
||||
*
|
||||
* @return array ($sFilterCode=>$sAttributeCode) + id=>id
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public static function GetFilterAttribList($sClass)
|
||||
{
|
||||
return self::$m_aFilterAttribList[$sClass];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.0.0 do not use : dead code, will be removed in the future use GetLabel instead N°4690 - Deprecate "FilterCodes"
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sFilterCode
|
||||
*
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public static function GetFilterLabel($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use MetaModel::GetFilterLabel : dead code, will be removed in the future. Use MetaModel::GetLabel instead');
|
||||
|
||||
return this::GetLabel($sClass, $sFilterCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var array array of ("listcode" => various info on the list, common to every classes)
|
||||
@@ -2781,6 +2820,25 @@ abstract class MetaModel
|
||||
return $oAttDef->GetAllowedValues($aArgs, $sContains);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 3.1.0 use GetAllowedValues_att N°4690 - Deprecate "FilterCodes"
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sFltCode
|
||||
* @param array $aArgs
|
||||
* @param string $sContains
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public static function GetAllowedValues_flt($sClass, $sFltCode, $aArgs = array(), $sContains = '')
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use MetaModel::GetAllowedValues_flt: dead code, will be removed in the future. Use MetaModel::GetAllowedValues');
|
||||
|
||||
return self::GetAllowedValues_att($sClass, $sFltCode);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
@@ -2853,6 +2911,8 @@ abstract class MetaModel
|
||||
$oAttribute->SetHostClass($sTargetClass);
|
||||
self::$m_aAttribDefs[$sTargetClass][$sCode] = $oAttribute;
|
||||
self::$m_aAttribOrigins[$sTargetClass][$sCode] = $sOriginClass;
|
||||
|
||||
self::$m_aFilterAttribList[$sTargetClass][$sCode] = $sCode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3016,6 +3076,9 @@ abstract class MetaModel
|
||||
if (array_key_exists('finalclass', self::$m_aAttribDefs[$sChildClass])) {
|
||||
throw new CoreException("Class $sChildClass, 'finalclass' is a reserved keyword, it cannot be used as an attribute code");
|
||||
}
|
||||
if (array_key_exists('finalclass', self::$m_aFilterAttribList[$sChildClass])) {
|
||||
throw new CoreException("Class $sChildClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code");
|
||||
}
|
||||
$oCloned = clone $oClassAtt;
|
||||
$oCloned->SetFixedValue($sChildClass);
|
||||
self::AddMagicAttribute($oCloned, $sChildClass, $sRootClass);
|
||||
@@ -3079,17 +3142,8 @@ abstract class MetaModel
|
||||
foreach (self::$m_aAttribDefs[$sClass] as $sAttCode => $oAttDef) {
|
||||
// Compute the filter codes
|
||||
//
|
||||
foreach ($oAttDef->GetMagicFields() as $sCode) {
|
||||
if(!array_key_exists($sClass, self::$m_aMagicFields)) {
|
||||
self::$m_aMagicFields[] = $sClass;
|
||||
}
|
||||
self::$m_aMagicFields[$sClass][] = $sCode;
|
||||
}
|
||||
if(!$oAttDef->IsSearchable()){
|
||||
if(!array_key_exists($sClass, self::$m_aFilterForbiddenAttributes)) {
|
||||
self::$m_aFilterForbiddenAttributes[] = $sClass;
|
||||
}
|
||||
self::$m_aFilterForbiddenAttributes[$sClass][] = $sAttCode;
|
||||
foreach ($oAttDef->GetFilterDefinitions() as $sFilterCode => $sCode) {
|
||||
self::$m_aFilterAttribList[$sClass][$sFilterCode] = $sCode;
|
||||
}
|
||||
|
||||
// Compute the fields that will be used to display a pointer to another object
|
||||
@@ -3198,6 +3252,7 @@ abstract class MetaModel
|
||||
if (array_key_exists('id', self::$m_aAttribDefs[$sClass])) {
|
||||
throw new CoreException("Class $sClass, 'id' is a reserved keyword, it cannot be used as an attribute code");
|
||||
}
|
||||
self::$m_aFilterAttribList[$sClass]['id'] = 'id';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3357,6 +3412,7 @@ abstract class MetaModel
|
||||
|
||||
self::$m_aAttribDefs[$sClass] = array();
|
||||
self::$m_aAttribOrigins[$sClass] = array();
|
||||
self::$m_aFilterAttribList[$sClass] = array();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6456,8 +6512,7 @@ abstract class MetaModel
|
||||
self::$m_aAttribDefs = $result['m_aAttribDefs'];
|
||||
self::$m_aAttribOrigins = $result['m_aAttribOrigins'];
|
||||
self::$m_aIgnoredAttributes = $result['m_aIgnoredAttributes'];
|
||||
self::$m_aFilterForbiddenAttributes = $result['m_aFilterForbiddenAttributes'];
|
||||
self::$m_aMagicFields = $result['m_aMagicFields'];
|
||||
self::$m_aFilterAttribList = $result['m_aFilterList'];
|
||||
self::$m_aListInfos = $result['m_aListInfos'];
|
||||
self::$m_aListData = $result['m_aListData'];
|
||||
self::$m_aRelationInfos = $result['m_aRelationInfos'];
|
||||
@@ -6493,8 +6548,7 @@ abstract class MetaModel
|
||||
$aCache['m_aAttribDefs'] = self::$m_aAttribDefs; // array of ("classname" => array of attributes)
|
||||
$aCache['m_aAttribOrigins'] = self::$m_aAttribOrigins; // array of ("classname" => array of ("attcode"=>"sourceclass"))
|
||||
$aCache['m_aIgnoredAttributes'] = self::$m_aIgnoredAttributes; //array of ("classname" => array of ("attcode")
|
||||
$aCache['m_aFilterForbiddenAttributes'] = self::$m_aFilterForbiddenAttributes; // array of ("classname" => array attributename)
|
||||
$aCache['m_aMagicFields'] = self::$m_aMagicFields; // array of ("classname" => array fieldname)
|
||||
$aCache['m_aFilterList'] = self::$m_aFilterAttribList; // array of ("classname" => array filterdef)
|
||||
$aCache['m_aListInfos'] = self::$m_aListInfos; // array of ("listcode" => various info on the list, common to every classes)
|
||||
$aCache['m_aListData'] = self::$m_aListData; // array of ("classname" => array of "listcode" => list)
|
||||
$aCache['m_aRelationInfos'] = self::$m_aRelationInfos; // array of ("relcode" => various info on the list, common to every classes)
|
||||
@@ -7711,6 +7765,7 @@ abstract class MetaModel
|
||||
'iApplicationUIExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iPopupMenuExtension',
|
||||
'iPageUIExtension',
|
||||
'iPageUIBlockExtension',
|
||||
'iBackofficeLinkedScriptsExtension',
|
||||
'iBackofficeEarlyScriptExtension',
|
||||
@@ -7719,7 +7774,6 @@ abstract class MetaModel
|
||||
'iBackofficeReadyScriptExtension',
|
||||
'iBackofficeLinkedStylesheetsExtension',
|
||||
'iBackofficeStyleExtension',
|
||||
'iBackofficeSassExtension',
|
||||
'iBackofficeDictEntriesExtension',
|
||||
'iBackofficeDictEntriesPrefixesExtension',
|
||||
'iPortalUIExtension',
|
||||
@@ -7728,6 +7782,7 @@ abstract class MetaModel
|
||||
'iModuleExtension',
|
||||
'iKPILoggerExtension',
|
||||
'ModuleHandlerApiInterface',
|
||||
'iNewsroomProvider',
|
||||
];
|
||||
foreach ($aInterfaces as $sInterface) {
|
||||
self::$m_aExtensionClassNames[$sInterface] = array();
|
||||
|
||||
@@ -77,7 +77,7 @@ abstract class ModelReflection
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
}
|
||||
|
||||
return utils::VSprintf($sLocalizedFormat, $aArguments);
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,6 +90,26 @@ abstract class Expression {
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* recursive rendering
|
||||
*
|
||||
* @deprecated 3.0.0 use RenderExpression
|
||||
*
|
||||
* @param array $aArgs used as input by default, or used as output if bRetrofitParams set to True
|
||||
* @param bool $bRetrofitParams
|
||||
*
|
||||
* @return array|string
|
||||
* @throws \MissingQueryArgument
|
||||
*/
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use RenderExpression');
|
||||
|
||||
return $this->RenderExpression(false, $aArgs, $bRetrofitParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* recursive rendering
|
||||
*
|
||||
|
||||
@@ -71,11 +71,16 @@ class OQLActualClassTreeResolver
|
||||
}
|
||||
// Attributes can be stored in attributes list or for magic ones into filter codes list.
|
||||
$sOriginClass = null;
|
||||
if (MetaModel::IsValidAttCode($sClass, $sAttCode)) {
|
||||
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
|
||||
{
|
||||
$sOriginClass = MetaModel::GetAttributeOrigin($sClass, $sAttCode);
|
||||
} else if ($sAttCode == 'id') {
|
||||
$sOriginClass = $sClass;
|
||||
} else {
|
||||
}
|
||||
else if (MetaModel::IsValidFilterCode($sClass, $sAttCode))
|
||||
{
|
||||
$sOriginClass = MetaModel::GetFilterCodeOrigin($sClass, $sAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (!isset($aClassAndAncestorsNodes[$sOriginClass]) || is_null($aClassAndAncestorsNodes[$sOriginClass]))
|
||||
|
||||
@@ -405,101 +405,6 @@ class ormDocument
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize an image so that it fits in the given dimensions
|
||||
* @param int $iMaxImageWidth Maximum width for the resized image
|
||||
* @param int $iMaxImageHeight Maximum height for the resized image
|
||||
* @param array|null $aFinalDimensions Image dimensions after resizing or null if unable to read the image
|
||||
* @return ormDocument The resampled image
|
||||
*
|
||||
*/
|
||||
public function ResizeImageToFit(int $iMaxWidth, int $iMaxHeight, array|null &$aFinalDimensions = null) : static
|
||||
{
|
||||
$aFinalDimensions = null;
|
||||
// If gd extension is not loaded, we put a warning in the log and return the image as is
|
||||
if (extension_loaded('gd') === false) {
|
||||
IssueLog::Warning('Image could not be resized as the "gd" extension does not seem to be loaded. Its dimensions will remain the same instead of ' . $iMaxWidth . 'x' . $iMaxHeight);
|
||||
return $this;
|
||||
}
|
||||
$oGdImage = false;
|
||||
switch($this->GetMimeType()) {
|
||||
case 'image/gif':
|
||||
case 'image/jpeg':
|
||||
case 'image/png':
|
||||
$oGdImage = @imagecreatefromstring($this->GetData());
|
||||
break;
|
||||
default:
|
||||
// Unsupported image type, return the image as-is
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($oGdImage === false) {
|
||||
IssueLog::Warning('Image could not be resized as . It will remain as imagecreatefromstring could not read its data.Its dimensions will remain the same instead of ' . $iMaxWidth . 'x' . $iMaxHeight);
|
||||
return $this;
|
||||
}
|
||||
|
||||
$iWidth = imagesx($oGdImage);
|
||||
$iHeight = imagesy($oGdImage);
|
||||
|
||||
if ( ($iMaxWidth === 0 || $iWidth <= $iMaxWidth) && ($iMaxHeight === 0 || $iHeight <= $iMaxHeight)) {
|
||||
// No need to resize
|
||||
$aFinalDimensions = [
|
||||
'width' => $iWidth,
|
||||
'height' =>$iHeight
|
||||
];
|
||||
return $this;
|
||||
}
|
||||
|
||||
$fScale = 1.0;
|
||||
if ($iMaxWidth > 0) {
|
||||
$fScale = min($fScale, $iMaxWidth / $iWidth);
|
||||
}
|
||||
if ($iMaxHeight > 0) {
|
||||
$fScale = min($fScale, $iMaxHeight / $iHeight);
|
||||
}
|
||||
$iNewWidth = (int)($iWidth * $fScale);
|
||||
$iNewHeight = (int)($iHeight * $fScale);
|
||||
|
||||
$oNewGdImage = imagecreatetruecolor($iNewWidth, $iNewHeight);
|
||||
|
||||
|
||||
$aFinalDimensions = [
|
||||
'width' => $iNewWidth,
|
||||
'height' =>$iNewHeight
|
||||
];
|
||||
|
||||
// Preserve transparency
|
||||
if($this->GetMimeType() == "image/gif" || $this->GetMimeType() == "image/png") {
|
||||
imagecolortransparent($oNewGdImage, imagecolorallocatealpha($oNewGdImage, 0, 0, 0, 127));
|
||||
imagealphablending($oNewGdImage, false);
|
||||
imagesavealpha($oNewGdImage, true);
|
||||
}
|
||||
imagecopyresampled($oNewGdImage, $oGdImage, 0, 0, 0, 0, $iNewWidth, $iNewHeight, $iWidth, $iHeight);
|
||||
|
||||
ob_start();
|
||||
switch ($this->GetMimeType()) {
|
||||
case 'image/gif':
|
||||
imagegif($oNewGdImage); // send image to output buffer
|
||||
break;
|
||||
|
||||
case 'image/jpeg':
|
||||
imagejpeg($oNewGdImage, null, 80); // null = send image to output buffer, 80 = good quality
|
||||
break;
|
||||
|
||||
case 'image/png':
|
||||
imagepng($oNewGdImage, null, 5); // null = send image to output buffer, 5 = medium compression
|
||||
break;
|
||||
}
|
||||
$oResampledImage = new ormDocument(ob_get_contents(), $this->GetMimeType(), $this->GetFileName());
|
||||
@ob_end_clean();
|
||||
|
||||
imagedestroy($oGdImage);
|
||||
imagedestroy($oNewGdImage);
|
||||
|
||||
return $oResampledImage;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
@@ -507,6 +412,4 @@ class ormDocument
|
||||
{
|
||||
return md5($this->GetData() ?? '');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -145,6 +145,18 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
$this->bHasDelta = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DBObject $oObject
|
||||
* @param string $sClassAlias
|
||||
*
|
||||
* @deprecated Since iTop 2.4, use {@link \ormLinkSet::AddItem()} instead.
|
||||
*/
|
||||
public function AddObject(DBObject $oObject, $sClassAlias = '')
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use \ormLinkSet::AddItem() instead');
|
||||
$this->AddItem($oObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $iObjectId
|
||||
*/
|
||||
|
||||
@@ -76,52 +76,6 @@ class ObjectResult
|
||||
$this->fields = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ObjectResult from a DBObject.
|
||||
*
|
||||
* @param DBObject $oObj The object.
|
||||
* @param array|null $aFieldSpec An array of class => attribute codes (Cf. RestUtils::GetFieldList). List of the attributes to be reported.
|
||||
* @param boolean $bExtendedOutput Output all of the link set attributes ?
|
||||
* @param integer $iCode An error code (RestResult::OK is no issue has been found)
|
||||
* @param string $sMessage Description of the error if any, an empty string otherwise
|
||||
*
|
||||
* @return ObjectResult
|
||||
*/
|
||||
public static function FromDBObject(DBObject $oObj, ?array $aFieldSpec = null, $bExtendedOutput = false, $iCode = 0, $sMessage = '') : ObjectResult {
|
||||
|
||||
$oObjRes = new ObjectResult($oObj::class, $oObj->GetKey());
|
||||
$oObjRes->code = $iCode;
|
||||
$oObjRes->message = $sMessage;
|
||||
|
||||
$aFields = null;
|
||||
if (!is_null($aFieldSpec))
|
||||
{
|
||||
// Enum all classes in the hierarchy, starting with the current one
|
||||
foreach (MetaModel::EnumParentClasses($oObj::class, ENUM_PARENT_CLASSES_ALL, false) as $sRefClass)
|
||||
{
|
||||
if (array_key_exists($sRefClass, $aFieldSpec))
|
||||
{
|
||||
$aFields = $aFieldSpec[$sRefClass];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_null($aFields))
|
||||
{
|
||||
// No fieldspec given, or not found...
|
||||
$aFields = array('id', 'friendlyname');
|
||||
}
|
||||
|
||||
foreach ($aFields as $sAttCode)
|
||||
{
|
||||
$oObjRes->AddField($oObj, $sAttCode, $bExtendedOutput);
|
||||
}
|
||||
|
||||
return $oObjRes;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to make an output value for a given attribute
|
||||
*
|
||||
@@ -231,6 +185,40 @@ class RestResultWithObjects extends RestResult
|
||||
/** @var array "DBObject_class:DBObject_key" as key, {@see \ObjectResult} as value */
|
||||
public $objects;
|
||||
|
||||
public function PrepareObject($iCode, $sMessage, $oObject, $aFieldSpec = null, $bExtendedOutput = false)
|
||||
{
|
||||
$sClass = get_class($oObject);
|
||||
$oObjRes = new ObjectResult($sClass, $oObject->GetKey());
|
||||
$oObjRes->code = $iCode;
|
||||
$oObjRes->message = $sMessage;
|
||||
|
||||
$aFields = null;
|
||||
if (!is_null($aFieldSpec))
|
||||
{
|
||||
// Enum all classes in the hierarchy, starting with the current one
|
||||
foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, false) as $sRefClass)
|
||||
{
|
||||
if (array_key_exists($sRefClass, $aFieldSpec))
|
||||
{
|
||||
$aFields = $aFieldSpec[$sRefClass];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_null($aFields))
|
||||
{
|
||||
// No fieldspec given, or not found...
|
||||
$aFields = array('id', 'friendlyname');
|
||||
}
|
||||
|
||||
foreach ($aFields as $sAttCode)
|
||||
{
|
||||
$oObjRes->AddField($oObject, $sAttCode, $bExtendedOutput);
|
||||
}
|
||||
|
||||
return $oObjRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the given object
|
||||
*
|
||||
@@ -249,8 +237,7 @@ class RestResultWithObjects extends RestResult
|
||||
*/
|
||||
public function AddObject($iCode, $sMessage, $oObject, $aFieldSpec = null, $bExtendedOutput = false)
|
||||
{
|
||||
$oObjRes = ObjectResult::FromDBObject($oObject, $aFieldSpec, $bExtendedOutput, $iCode, $sMessage);
|
||||
|
||||
$oObjRes = $this->PrepareObject($iCode, $sMessage, $oObject, $aFieldSpec, $bExtendedOutput);
|
||||
$sObjKey = get_class($oObject).'::'.$oObject->GetKey();
|
||||
$this->objects[$sObjKey] = $oObjRes;
|
||||
}
|
||||
@@ -266,6 +253,45 @@ public function SanitizeContent()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package RESTAPI
|
||||
* @api
|
||||
*/
|
||||
class RestResultWithObjectSets extends RestResultWithObjects
|
||||
{
|
||||
private $current_object = null;
|
||||
|
||||
public function MakeNewObjectSet()
|
||||
{
|
||||
$arr = array();
|
||||
$this->current_object = &$arr;
|
||||
$this->objects[] = &$arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report the given object
|
||||
*
|
||||
* @api
|
||||
* @param string $sObjectAlias Name of the subobject, usually the OQL class alias
|
||||
* @param int $iCode An error code (RestResult::OK is no issue has been found)
|
||||
* @param string $sMessage Description of the error if any, an empty string otherwise
|
||||
* @param DBObject $oObject The object being reported
|
||||
* @param array|null $aFieldSpec An array of class => attribute codes (Cf. RestUtils::GetFieldList). List of the attributes to be reported.
|
||||
* @param boolean $bExtendedOutput Output all of the link set attributes ?
|
||||
*
|
||||
* @return void
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function AppendSubObject($sObjectAlias, $iCode, $sMessage, $oObject, $aFieldSpec = null, $bExtendedOutput = false)
|
||||
{
|
||||
$oObjRes = $this->PrepareObject($iCode, $sMessage, $oObject, $aFieldSpec, $bExtendedOutput);
|
||||
$this->current_object[$sObjectAlias] = $oObjRes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @package RESTAPI
|
||||
* @api
|
||||
@@ -545,14 +571,21 @@ class CoreServices implements iRestServiceProvider, iRestInputSanitizer
|
||||
break;
|
||||
|
||||
case 'core/get':
|
||||
$sClass = RestUtils::GetClass($aParams, 'class');
|
||||
$sClassParam = RestUtils::GetMandatoryParam($aParams, 'class');
|
||||
$key = RestUtils::GetMandatoryParam($aParams, 'key');
|
||||
$aShowFields = RestUtils::GetFieldList($sClass, $aParams, 'output_fields');
|
||||
$bExtendedOutput = (RestUtils::GetOptionalParam($aParams, 'output_fields', '*') == '*+');
|
||||
$sShowFields = RestUtils::GetOptionalParam($aParams, 'output_fields', '*');
|
||||
$iLimit = (int)RestUtils::GetOptionalParam($aParams, 'limit', 0);
|
||||
$iPage = (int)RestUtils::GetOptionalParam($aParams, 'page', 1);
|
||||
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClass, $key, $iLimit, self::getOffsetFromLimitAndPage($iLimit, $iPage));
|
||||
// Validate the class(es)
|
||||
$aClass = explode(',', $sClassParam);
|
||||
foreach ($aClass as $sClass) {
|
||||
if (!MetaModel::IsValidClass(trim($sClass))) {
|
||||
throw new Exception("class '$sClass' is not valid");
|
||||
}
|
||||
}
|
||||
|
||||
$oObjectSet = RestUtils::GetObjectSetFromKey($sClassParam, $key, $iLimit, self::getOffsetFromLimitAndPage($iLimit, $iPage));
|
||||
$sTargetClass = $oObjectSet->GetFilter()->GetClass();
|
||||
|
||||
if (UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ) != UR_ALLOWED_YES)
|
||||
@@ -570,23 +603,68 @@ class CoreServices implements iRestServiceProvider, iRestInputSanitizer
|
||||
$oResult->code = RestResult::INVALID_PAGE;
|
||||
$oResult->message = "The request page number is not valid. It must be an integer greater than 0";
|
||||
}
|
||||
else
|
||||
elseif (count($oObjectSet->GetSelectedClasses()) > 1)
|
||||
{
|
||||
if (!$bExtendedOutput && RestUtils::GetOptionalParam($aParams, 'output_fields', '*') != '*')
|
||||
{
|
||||
$aFields = $aShowFields[$sClass];
|
||||
//Id is not a valid attribute to optimize
|
||||
if (in_array('id', $aFields))
|
||||
{
|
||||
unset($aFields[array_search('id', $aFields)]);
|
||||
}
|
||||
$aAttToLoad = array($oObjectSet->GetClassAlias() => $aFields);
|
||||
$oObjectSet->OptimizeColumnLoad($aAttToLoad);
|
||||
}
|
||||
$oResult = new RestResultWithObjectSets();
|
||||
$aCache = [];
|
||||
$aShowFields = [];
|
||||
foreach ($oObjectSet->GetSelectedClasses() as $sSelectedClass) {
|
||||
$aShowFields = array_merge( $aShowFields, RestUtils::GetFieldList($sSelectedClass, $aParams, 'output_fields', false));
|
||||
}
|
||||
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
$oResult->AddObject(0, '', $oObject, $aShowFields, $bExtendedOutput);
|
||||
while ($oObjects = $oObjectSet->FetchAssoc()) {
|
||||
$oResult->MakeNewObjectSet();
|
||||
|
||||
foreach ($oObjects as $sAlias => $oObject) {
|
||||
if (!$oObject) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!array_key_exists($sAlias, $aCache)) {
|
||||
$sClass = get_class($oObject);
|
||||
$bExtendedOutput = RestUtils::HasRequestedExtendedOutput($sShowFields);
|
||||
|
||||
if (!RestUtils::HasRequestedAllOutputFields($sShowFields)) {
|
||||
$aFields = $aShowFields[$sClass];
|
||||
//Id is not a valid attribute to optimize
|
||||
if ($aFields && in_array('id', $aFields)) {
|
||||
unset($aFields[array_search('id', $aFields)]);
|
||||
}
|
||||
$aAttToLoad = [$sAlias => $aFields];
|
||||
$oObjectSet->OptimizeColumnLoad($aAttToLoad);
|
||||
}
|
||||
$aCache[$sAlias] = [
|
||||
'aShowFields' => $aShowFields,
|
||||
'bExtendedOutput' => $bExtendedOutput,
|
||||
];
|
||||
} else {
|
||||
$aShowFields = $aCache[$sAlias]['aShowFields'];
|
||||
$bExtendedOutput = $aCache[$sAlias]['bExtendedOutput'];
|
||||
}
|
||||
|
||||
$oResult->AppendSubObject($sAlias, 0, '', $oObject, $aShowFields, $bExtendedOutput);
|
||||
}
|
||||
}
|
||||
$oResult->message = "Found: ".$oObjectSet->Count();
|
||||
} else {
|
||||
$aShowFields =[];
|
||||
foreach ($aClass as $sSelectedClass) {
|
||||
$sSelectedClass = trim($sSelectedClass);
|
||||
$aShowFields = array_merge($aShowFields, RestUtils::GetFieldList($sSelectedClass, $aParams, 'output_fields', false));
|
||||
}
|
||||
|
||||
if (!RestUtils::HasRequestedAllOutputFields($sShowFields) && count($aShowFields) == 1) {
|
||||
$aFields = $aShowFields[$sClass];
|
||||
//Id is not a valid attribute to optimize
|
||||
if (in_array('id', $aFields)) {
|
||||
unset($aFields[array_search('id', $aFields)]);
|
||||
}
|
||||
$aAttToLoad = array($oObjectSet->GetClassAlias() => $aFields);
|
||||
$oObjectSet->OptimizeColumnLoad($aAttToLoad);
|
||||
}
|
||||
|
||||
while ($oObject = $oObjectSet->Fetch()) {
|
||||
$oResult->AddObject(0, '', $oObject, $aShowFields, RestUtils::HasRequestedExtendedOutput($sShowFields));
|
||||
}
|
||||
$oResult->message = "Found: ".$oObjectSet->Count();
|
||||
}
|
||||
|
||||
@@ -1962,15 +1962,6 @@ class UserRights
|
||||
return self::$m_aCacheUsers[$sAuthentication][$sLogin];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the cache of users
|
||||
* @return void
|
||||
*/
|
||||
public static function ResetCacheUsers()
|
||||
{
|
||||
self::$m_aCacheUsers = [ 'internal' => [], 'external' => [] ];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string$sClass
|
||||
* @param array $aAllowedOrgs
|
||||
@@ -2129,8 +2120,6 @@ class StimulusChecker extends ActionChecker
|
||||
{
|
||||
var $sState = null;
|
||||
|
||||
public mixed $iState = null;
|
||||
|
||||
public function __construct(DBSearch $oFilter, $sState, $iStimulusCode)
|
||||
{
|
||||
parent::__construct($oFilter, $iStimulusCode);
|
||||
|
||||
@@ -435,7 +435,7 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
foreach ($aAdditionalField as $sAdditionalField) {
|
||||
array_push($aArguments, $oObject->Get($sAdditionalField));
|
||||
}
|
||||
$aData['additional_field'] = utils::VSprintf($sFormatAdditionalField, $aArguments);
|
||||
$aData['additional_field'] = vsprintf($sFormatAdditionalField, $aArguments);
|
||||
} else {
|
||||
$aData['additional_field'] = '';
|
||||
}
|
||||
|
||||
150
css/css-variables.scss
Normal file
@@ -0,0 +1,150 @@
|
||||
/*!
|
||||
* Copyright (C) 2013-2024 Combodo SAS
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/********************************************************************************/
|
||||
/* */
|
||||
/* @deprecated 3.0.0 N°5311 The backoffice now uses files from css/backoffice/* */
|
||||
/* */
|
||||
/********************************************************************************/
|
||||
|
||||
// Beware the version number MUST be enclosed with quotes otherwise v2.3.0 becomes v2 0.3 .0
|
||||
$version: "v2.7.7";
|
||||
$approot-relative: "../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
$version: "v2.7.8";
|
||||
$approot-relative: "../../../../../" !default; // relative to env-***/branding/themes/***/main.css
|
||||
|
||||
// Base colors
|
||||
$gray-base: #000 !default;
|
||||
$gray-darker: lighten($gray-base, 13.5%) !default; // #222
|
||||
$gray-dark: #444 !default;
|
||||
$gray: #777 !default;
|
||||
$gray-light: #808080 !default;
|
||||
$gray-lighter: #ddd !default;
|
||||
$gray-extra-light: #F1F1F1 !default;
|
||||
|
||||
$white: #FFFFFF !default;
|
||||
|
||||
$combodo-orange: #EA7D1E !default;
|
||||
$combodo-dark-gray: #585653 !default;
|
||||
|
||||
$combodo-orange-dark: darken($combodo-orange, 13.8%) !default;
|
||||
$combodo-orange-darker: darken($combodo-orange, 18%) !default;
|
||||
$combodo-dark-gray-dark: darken($combodo-dark-gray, 13.5%) !default;
|
||||
$combodo-dark-gray-darker: darken($combodo-dark-gray, 18%) !default;
|
||||
|
||||
// Brand colors
|
||||
// - Bases
|
||||
$brand-primary: $combodo-orange !default;
|
||||
$brand-secondary: $combodo-dark-gray !default;
|
||||
// - Shades
|
||||
$brand-primary-lightest: lighten($brand-primary, 15%) !default;
|
||||
$brand-primary-lighter: lighten($brand-primary, 10%) !default;
|
||||
$brand-primary-light: lighten($brand-primary, 6%) !default;
|
||||
$brand-primary-dark: darken($brand-primary, 6%) !default;
|
||||
$brand-primary-darker: darken($brand-primary, 10%) !default;
|
||||
$brand-primary-darkest: darken($brand-primary, 15%) !default;
|
||||
$brand-secondary-lightest: lighten($brand-secondary, 15%) !default;
|
||||
$brand-secondary-lighter: lighten($brand-secondary, 10%) !default;
|
||||
$brand-secondary-light: lighten($brand-secondary, 6%) !default;
|
||||
$brand-secondary-dark: darken($brand-secondary, 6%) !default;
|
||||
$brand-secondary-darker: darken($brand-secondary, 10%) !default;
|
||||
$brand-secondary-darkest: darken($brand-secondary, 15%) !default;
|
||||
|
||||
// Vars
|
||||
$highlight-color: $brand-primary !default;
|
||||
$grey-color: #555555 !default;
|
||||
$complement-color: #1c94c4 !default;
|
||||
$complement-light: #d6e8ef !default;
|
||||
$frame-background-color: $gray-extra-light !default;
|
||||
$text-color: #000 !default;
|
||||
$box-radius: 0px !default;
|
||||
|
||||
$box-shadow-lightest: 0 1px 1px rgba(0, 0, 0, 0.15) !default;
|
||||
$box-shadow-lighter: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24) !default;
|
||||
$box-shadow-light: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22) !default;
|
||||
$box-shadow-regular: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23) !default;
|
||||
$box-shadow-strong: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23) !default;
|
||||
$box-shadow-stronger: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22) !default;
|
||||
$box-shadow-strongest: 0 19px 38px rgba(0,0,0,0.30), 0 15px 12px rgba(0,0,0,0.22) !default;
|
||||
|
||||
$body-background-color : $white !default;
|
||||
|
||||
$hyperlink-color: $complement-color !default;
|
||||
$hyperlink-text-decoration: none !default;
|
||||
|
||||
////////////
|
||||
// Search //
|
||||
$search-form-container-color: $white !default;
|
||||
$search-form-container-bg-color: $complement-color !default;
|
||||
//
|
||||
$search-criteria-box-color: #2D2D2D !default;
|
||||
$search-criteria-box-picto-color: $brand-primary !default;
|
||||
$search-criteria-box-bg-color: #EEEEEE !default;
|
||||
$search-criteria-box-hover-color: $white !default;
|
||||
$search-criteria-box-border-color: #CCCCCC !default;
|
||||
$search-criteria-box-border: 1px solid $search-criteria-box-border-color !default;
|
||||
$search-criteria-box-radius: 1px !default;
|
||||
$search-criteria-more-less-details-color: #3F7294 !default;
|
||||
//
|
||||
$search-add-criteria-box-color: $search-criteria-box-color !default;
|
||||
$search-add-criteria-box-bg-color: $white !default;
|
||||
$search-add-criteria-box-hover-color: $gray-extra-light !default;
|
||||
//
|
||||
$search-button-box-color: $brand-primary !default;
|
||||
$search-button-box-bg-color: $white !default;
|
||||
$search-button-box-bg-hover-color: $gray-extra-light !default;
|
||||
|
||||
/////////////
|
||||
// Buttons //
|
||||
/////////////
|
||||
// Toggle button
|
||||
$toggle-button-bg-color: #CCCCCC !default;
|
||||
$toggle-button-bg-checked-color: $brand-primary !default;
|
||||
$toggle-button-slider-bg-color: #FFFFFF !default;
|
||||
|
||||
// Console elements
|
||||
$summary-details-background: $grey-color !default;
|
||||
$main-header-background: $frame-background-color !default;
|
||||
$table-even-background: $frame-background-color !default;
|
||||
$table-hover-background: #fdf5d0 !default;
|
||||
$popup-menu-highlight-color: $highlight-color !default;
|
||||
$popup-menu-text-color: #000 !default;
|
||||
$popup-menu-background-color: #fff !default;
|
||||
$popup-menu-text-higlight-color: #fff !default;
|
||||
$breadcrumb-color: $grey-color !default;
|
||||
$breadcrumb-highlight-color: $highlight-color !default;
|
||||
|
||||
// jQuery UI widgets vars
|
||||
$primary-text-color: #333333 !default;
|
||||
$secondary-text-color: $grey-color !default;
|
||||
$error-text-color: $white !default;
|
||||
$highlight-text-color: #363636 !default;
|
||||
$button-content-background-color: $gray-extra-light !default;
|
||||
$button-header-background-color: $gray-extra-light !default;
|
||||
$main-menu-background-color: $gray-extra-light !default;
|
||||
$hover-background-color: #fde17c !default;
|
||||
$border-highlight-color: $brand-primary-dark !default;
|
||||
$highlight-item-color: $white !default;
|
||||
$content-color: #eeeeee !default;
|
||||
$default-font-family: "Raleway";
|
||||
$icons-filter: hue-rotate(0deg) !default;
|
||||
|
||||
%font-awesome-prerequisites {
|
||||
font-family: 'Font Awesome 5 Free';
|
||||
font-weight: 600;
|
||||
}
|
||||
4289
css/light-grey.scss
Normal file
@@ -1,7 +1,7 @@
|
||||
{# @copyright Copyright (C) 2010-2024 Combodo SAS #}
|
||||
{# @license http://opensource.org/licenses/AGPL-3.0 #}
|
||||
|
||||
<div class="sso-button" title="{{ aData.sTooltip }}" onclick="$('#login_mode').val('{{ aData.sLoginMode }}'); $('#login_form').trigger('submit'); return false;">
|
||||
<div class="sso-button" title="{{ aData.sTooltip }}" onclick="$('#login_mode').val('{{ aData.sLoginMode }}'); $('#login_form').submit(); return false;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 336.82 167.83" class="logo sso-image" style="max-height: 20px;">
|
||||
<defs>
|
||||
<style>
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
<db_key_field>id</db_key_field>
|
||||
<db_final_class_field/>
|
||||
<naming>
|
||||
<format>%1$s</format>
|
||||
<attributes>
|
||||
<attribute id="login"/>
|
||||
</attributes>
|
||||
</naming>
|
||||
<display_template/>
|
||||
<style>
|
||||
<icon/>
|
||||
</style>
|
||||
@@ -66,7 +68,8 @@
|
||||
$sServer = $this->Get('ldap_server');
|
||||
if (empty($sServer))
|
||||
{
|
||||
$sURI = MetaModel::GetModuleSetting('authent-ldap', 'uri', 'ldap://localhost');
|
||||
$sLDAPHost = MetaModel::GetModuleSetting('authent-ldap', 'host', 'localhost');
|
||||
$iLDAPPort = MetaModel::GetModuleSetting('authent-ldap', 'port', 389);
|
||||
|
||||
$sDefaultLDAPUser = MetaModel::GetModuleSetting('authent-ldap', 'default_user', '');
|
||||
$sDefaultLDAPPwd = MetaModel::GetModuleSetting('authent-ldap', 'default_pwd', '');
|
||||
@@ -87,7 +90,8 @@
|
||||
return false;
|
||||
}
|
||||
$aServerParams = $aServers[$sServer];
|
||||
$sURI = $aServerParams['uri'] ?? 'ldap://localhost';
|
||||
$sLDAPHost = isset($aServerParams['host']) ? $aServerParams['host'] : 'localhost';
|
||||
$iLDAPPort = isset($aServerParams['port']) ? $aServerParams['port'] : 389;
|
||||
$sDefaultLDAPUser = isset($aServerParams['default_user']) ? $aServerParams['default_user'] : '';
|
||||
$sDefaultLDAPPwd = isset($aServerParams['default_pwd']) ? $aServerParams['default_pwd'] : '';
|
||||
$bLDAPStartTLS = isset($aServerParams['start_tls']) ? $aServerParams['start_tls'] : false;
|
||||
@@ -97,10 +101,10 @@
|
||||
$bDebug = isset($aServerParams['debug']) ? $aServerParams['debug'] : false;
|
||||
}
|
||||
|
||||
$hDS = @ldap_connect($sURI);
|
||||
$hDS = @ldap_connect($sLDAPHost, $iLDAPPort);
|
||||
if ($hDS === false)
|
||||
{
|
||||
$this->LogIssue($bDebug, "ldap_authentication: can not connect to the LDAP server '$sURI'. Check the configuration file config-itop.php.");
|
||||
$this->LogIssue($bDebug, "ldap_authentication: can not connect to the LDAP server '$sLDAPHost' (port: $iLDAPPort). Check the configuration file config-itop.php.");
|
||||
return false;
|
||||
}
|
||||
if (array_key_exists(LDAP_OPT_DEBUG_LEVEL, $aOptions))
|
||||
@@ -182,7 +186,7 @@
|
||||
else
|
||||
{
|
||||
// Trace: invalid default user for LDAP initial binding
|
||||
$this->LogIssue($bDebug, "ldap_authentication: cannot bind to the LDAP server '$sURI', user='$sDefaultLDAPUser', pwd='****'. Error: '".ldap_error($hDS)."'. Check the configuration file config-itop.php.");
|
||||
$this->LogIssue($bDebug, "ldap_authentication: cannot bind to the LDAP server '$sLDAPHost' (port: $iLDAPPort), user='$sDefaultLDAPUser', pwd='****'. Error: '".ldap_error($hDS)."'. Check the configuration file config-itop.php.");
|
||||
return false;
|
||||
}
|
||||
}]]></code>
|
||||
|
||||
@@ -43,7 +43,8 @@ SetupWebPage::AddModule(
|
||||
// Default settings
|
||||
//
|
||||
'settings' => array(
|
||||
'uri' => 'ldap://localhost', // URI with host or IP address of your LDAP server
|
||||
'host' => 'localhost', // host or IP address of your LDAP server
|
||||
'port' => 389, // LDAP port (std: 389)
|
||||
'default_user' => '', // User and password used for initial "Anonymous" bind to LDAP
|
||||
'default_pwd' => '', // Leave both blank, if anonymous (read-only) bind is allowed
|
||||
'base_dn' => 'dc=yourcompany,dc=com', // Base DN for User queries, adjust it to your LDAP schema
|
||||
@@ -74,27 +75,6 @@ class AuthentLDAPInstaller extends ModuleInstallerAPI
|
||||
$sSQL = "insert into $sUserLDAPTable (id) select U.id from $sUserTable as U left join $sUserLDAPTable as L on U.id = L.id where U.finalclass='UserLDAP' and isnull(L.id);";
|
||||
CMDBSource::Query($sSQL);
|
||||
}
|
||||
|
||||
public static function BeforeWritingConfig(Config $oConfiguration)
|
||||
{
|
||||
$sURI = $oConfiguration->GetModuleSetting('authent-ldap', 'uri');
|
||||
if (empty($sURI)) {
|
||||
$sLDAPHost = MetaModel::GetModuleSetting('authent-ldap', 'host', 'localhost');
|
||||
$iLDAPPort = MetaModel::GetModuleSetting('authent-ldap', 'port', 389);
|
||||
$sURI = preg_match('#^ldaps?://#i', $sLDAPHost) ? $sLDAPHost : 'ldap://'.$sLDAPHost.':'.$iLDAPPort;
|
||||
$oConfiguration->SetModuleSetting('authent-ldap', 'uri', $sURI);
|
||||
}
|
||||
|
||||
$aServers = $oConfiguration->GetModuleSetting('authent-ldap', 'servers', []);
|
||||
foreach ($aServers as &$aServer) {
|
||||
if (!array_key_exists($aServer, 'uri')) {
|
||||
$sLDAPHost = $aServerParams['host'] ?? 'localhost';
|
||||
$iLDAPPort = $aServerParams['port'] ?? 389;
|
||||
$aServer['uri'] = preg_match('#^ldaps?://#i', $sLDAPHost) ? $sLDAPHost : 'ldap://'.$sLDAPHost.':'.$iLDAPPort;
|
||||
}
|
||||
}
|
||||
$oConfiguration->SetModuleSetting('authent-ldap', 'servers', $aServers);
|
||||
}
|
||||
}
|
||||
|
||||
} // if (function_exists('ldap_connect'))
|
||||
|
||||
@@ -204,7 +204,7 @@ SQL;
|
||||
}
|
||||
SetupLog::Info("Initializing attachment/item_org_id - $iUpdated records have been adjusted");
|
||||
|
||||
if (MetaModel::GetAttributeDef('Attachment', 'contact_id') instanceof AttributeExternalKey && version_compare($sPreviousVersion, '3.2.0', '<')) {
|
||||
if (MetaModel::GetAttributeDef('Attachment', 'contact_id') instanceof AttributeExternalKey) {
|
||||
SetupLog::Info("Upgrading itop-attachment from '$sPreviousVersion' to '$sCurrentVersion'. Starting with 3.2.0, contact_id will be added into the DB...");
|
||||
$sUserTableName = MetaModel::DBGetTable('User');
|
||||
$sUserFieldContactId = MetaModel::GetAttributeDef('User', 'contactid')->Get('sql');
|
||||
|
||||
@@ -53,7 +53,14 @@ class DBRestore extends DBBackup
|
||||
$sUser = self::EscapeShellArg($this->sDBUser);
|
||||
$sPwd = self::EscapeShellArg($this->sDBPwd);
|
||||
$sDBName = self::EscapeShellArg($this->sDBName);
|
||||
$sMySQLExe = DBBackup::MakeSafeMySQLCommand($this->sMySQLBinDir, 'mysql');
|
||||
if (empty($this->sMySQLBinDir))
|
||||
{
|
||||
$sMySQLExe = 'mysql';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sMySQLExe = '"'.$this->sMySQLBinDir.'/mysql"';
|
||||
}
|
||||
if (is_null($this->iDBPort))
|
||||
{
|
||||
$sPortOption = '';
|
||||
|
||||
@@ -95,7 +95,12 @@ try {
|
||||
//
|
||||
$sMySQLBinDir = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '');
|
||||
$sMySQLBinDir = utils::ReadParam('mysql_bindir', $sMySQLBinDir, true);
|
||||
$sMySQLDump = DBBackup::MakeSafeMySQLCommand($sMySQLBinDir, 'mysqldump');
|
||||
if (empty($sMySQLBinDir)) {
|
||||
$sMySQLDump = 'mysqldump';
|
||||
} else {
|
||||
//echo 'Info - Found mysql_bindir: '.$sMySQLBinDir;
|
||||
$sMySQLDump = '"'.$sMySQLBinDir.'/mysqldump"';
|
||||
}
|
||||
$sCommand = "$sMySQLDump -V 2>&1";
|
||||
|
||||
$aOutput = array();
|
||||
|
||||
@@ -46,12 +46,12 @@
|
||||
<field id="status" xsi:type="AttributeEnum">
|
||||
<sort_type>rank</sort_type>
|
||||
<values>
|
||||
<value id="approved">
|
||||
<value id="approved">approved
|
||||
<code>approved</code>
|
||||
<rank>60</rank>
|
||||
<style><main_color>$ibo-lifecycle-success-state-primary-color</main_color><complementary_color>$ibo-lifecycle-success-state-secondary-color</complementary_color><decoration_classes>fas fa-user-check</decoration_classes></style>
|
||||
</value>
|
||||
<value id="assigned">
|
||||
<value id="assigned">assigned
|
||||
<code>assigned</code>
|
||||
<rank>40</rank>
|
||||
<style><main_color>$ibo-lifecycle-neutral-state-primary-color</main_color><complementary_color>$ibo-lifecycle-neutral-state-secondary-color</complementary_color><decoration_classes/></style>
|
||||
@@ -4591,6 +4591,7 @@
|
||||
<menu id="Change:Shortcuts" xsi:type="TemplateMenuNode" _delta="define">
|
||||
<rank>3</rank>
|
||||
<parent>ChangeManagement</parent>
|
||||
<template_file/>
|
||||
</menu>
|
||||
<menu id="MyChanges" xsi:type="OQLMenuNode" _delta="define">
|
||||
<rank>1</rank>
|
||||
@@ -4640,7 +4641,7 @@
|
||||
AND (C.id != :this->id)
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
<oql><![CDATA[
|
||||
@@ -4655,7 +4656,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.png</icon>
|
||||
</item>
|
||||
</items>
|
||||
</down>
|
||||
@@ -4677,7 +4678,7 @@
|
||||
AND (C.id != :this->id)
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
<oql><![CDATA[
|
||||
@@ -4692,7 +4693,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.png</icon>
|
||||
</item>
|
||||
</items>
|
||||
</down>
|
||||
@@ -4714,7 +4715,7 @@
|
||||
AND (C.id != :this->id)
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
<oql><![CDATA[
|
||||
@@ -4729,7 +4730,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.png</icon>
|
||||
</item>
|
||||
</items>
|
||||
</down>
|
||||
@@ -4754,7 +4755,7 @@
|
||||
AND (L.impact_code != 'not_impacted')
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
<oql><![CDATA[
|
||||
@@ -4768,7 +4769,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.png</icon>
|
||||
</item>
|
||||
</items>
|
||||
</down>
|
||||
@@ -4785,7 +4786,7 @@
|
||||
AND (L.impact_code != 'not_impacted')
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-ongoing.png</icon>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
<oql><![CDATA[
|
||||
@@ -4799,7 +4800,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt-itil/images/change-done.png</icon>
|
||||
</item>
|
||||
</items>
|
||||
</up>
|
||||
|
||||
@@ -151,7 +151,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Class:Change/Stimulus:ev_approve+' => '',
|
||||
'Class:Change/Stimulus:ev_replan' => 'Replan',
|
||||
'Class:Change/Stimulus:ev_replan+' => '',
|
||||
'Class:Change/Stimulus:ev_notapprove' => 'Reject approval',
|
||||
'Class:Change/Stimulus:ev_notapprove' => 'Reject',
|
||||
'Class:Change/Stimulus:ev_notapprove+' => '',
|
||||
'Class:Change/Stimulus:ev_implement' => 'Implement',
|
||||
'Class:Change/Stimulus:ev_implement+' => '',
|
||||
|
||||
@@ -151,7 +151,7 @@ Dict::Add('EN GB', 'British English', 'British English', array(
|
||||
'Class:Change/Stimulus:ev_approve+' => '',
|
||||
'Class:Change/Stimulus:ev_replan' => 'Replan',
|
||||
'Class:Change/Stimulus:ev_replan+' => '',
|
||||
'Class:Change/Stimulus:ev_notapprove' => 'Reject approval',
|
||||
'Class:Change/Stimulus:ev_notapprove' => 'Reject',
|
||||
'Class:Change/Stimulus:ev_notapprove+' => '',
|
||||
'Class:Change/Stimulus:ev_implement' => 'Implement',
|
||||
'Class:Change/Stimulus:ev_implement+' => '',
|
||||
|
||||
@@ -139,7 +139,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
|
||||
'Class:Change/Stimulus:ev_approve+' => '~~',
|
||||
'Class:Change/Stimulus:ev_replan' => 'Replan~~',
|
||||
'Class:Change/Stimulus:ev_replan+' => '~~',
|
||||
'Class:Change/Stimulus:ev_notapprove' => 'Reject approval~~',
|
||||
'Class:Change/Stimulus:ev_notapprove' => 'Reject~~',
|
||||
'Class:Change/Stimulus:ev_notapprove+' => '~~',
|
||||
'Class:Change/Stimulus:ev_implement' => 'Implement~~',
|
||||
'Class:Change/Stimulus:ev_implement+' => '~~',
|
||||
|
||||
@@ -380,6 +380,7 @@
|
||||
</attribute>
|
||||
<attribute id="private_log">
|
||||
<read_only/>
|
||||
<read_only/>
|
||||
</attribute>
|
||||
<attribute id="caller_id">
|
||||
<read_only/>
|
||||
@@ -833,6 +834,7 @@
|
||||
<menu id="Change:Shortcuts" xsi:type="TemplateMenuNode" _delta="define">
|
||||
<rank>3</rank>
|
||||
<parent>ChangeManagement</parent>
|
||||
<template_file/>
|
||||
</menu>
|
||||
<menu id="MyChanges" xsi:type="OQLMenuNode" _delta="define">
|
||||
<rank>1</rank>
|
||||
@@ -875,7 +877,7 @@
|
||||
AND (C.id != :this->id)
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.png</icon>
|
||||
<default>yes</default>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
@@ -891,7 +893,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-done.png</icon>
|
||||
<default>no</default>
|
||||
</item>
|
||||
</items>
|
||||
@@ -914,7 +916,7 @@
|
||||
AND (C.id != :this->id)
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.png</icon>
|
||||
<default>yes</default>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
@@ -930,7 +932,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-done.png</icon>
|
||||
<default>no</default>
|
||||
</item>
|
||||
</items>
|
||||
@@ -953,7 +955,7 @@
|
||||
AND (C.id != :this->id)
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.png</icon>
|
||||
<default>yes</default>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
@@ -969,7 +971,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-done.png</icon>
|
||||
<default>no</default>
|
||||
</item>
|
||||
</items>
|
||||
@@ -995,7 +997,7 @@
|
||||
AND (L.impact_code != 'not_impacted')
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.png</icon>
|
||||
<default>yes</default>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
@@ -1010,7 +1012,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-done.png</icon>
|
||||
<default>no</default>
|
||||
</item>
|
||||
</items>
|
||||
@@ -1028,7 +1030,7 @@
|
||||
AND (L.impact_code != 'not_impacted')
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:OpenChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-ongoing.png</icon>
|
||||
<default>yes</default>
|
||||
</item>
|
||||
<item id="recent_changes" _delta="define">
|
||||
@@ -1043,7 +1045,7 @@
|
||||
AND (C.end_date < NOW() AND C.end_date > DATE_SUB(NOW(), INTERVAL 3 DAY ))
|
||||
]]></oql>
|
||||
<dict>Tickets:Related:RecentChanges</dict>
|
||||
<icon>itop-change-mgmt/images/change-done.svg</icon>
|
||||
<icon>itop-change-mgmt/images/change-done.png</icon>
|
||||
<default>no</default>
|
||||
</item>
|
||||
</items>
|
||||
|
||||
BIN
datamodels/2.x/itop-change-mgmt/images/change-approved.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
datamodels/2.x/itop-change-mgmt/images/change-closed.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
datamodels/2.x/itop-change-mgmt/images/change-done.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
datamodels/2.x/itop-change-mgmt/images/change-ongoing.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
datamodels/2.x/itop-change-mgmt/images/change-rejected.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
datamodels/2.x/itop-change-mgmt/images/change.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
@@ -4351,6 +4351,7 @@
|
||||
</item>
|
||||
<item id="document_id">
|
||||
<rank>20</rank>
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
</items>
|
||||
</list>
|
||||
@@ -5735,7 +5736,8 @@
|
||||
<duplicates/>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
<methods>
|
||||
</methods>
|
||||
<presentation>
|
||||
<details>
|
||||
<items>
|
||||
@@ -7228,12 +7230,7 @@
|
||||
</class>
|
||||
<class id="DocumentFile" _delta="must_exist">
|
||||
<presentation>
|
||||
<details>
|
||||
<items>
|
||||
<item id="cis_list" _delta="define">
|
||||
<rank>70</rank>
|
||||
</item>
|
||||
</items>
|
||||
<details><items><item id="cis_list" _delta="define"><rank>70</rank></item></items>>
|
||||
</details>
|
||||
</presentation>
|
||||
</class>
|
||||
|
||||
@@ -323,8 +323,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Class:NetworkDevice/Attribute:networkdevicetype_id+' => '',
|
||||
'Class:NetworkDevice/Attribute:networkdevicetype_name' => 'Nom Type',
|
||||
'Class:NetworkDevice/Attribute:networkdevicetype_name+' => '',
|
||||
'Class:NetworkDevice/Attribute:connectablecis_list' => 'Matériel connecté',
|
||||
'Class:NetworkDevice/Attribute:connectablecis_list+' => 'Tous les équipements connectés à cet appareil réseau',
|
||||
'Class:NetworkDevice/Attribute:connectablecis_list' => 'Matériel connectés',
|
||||
'Class:NetworkDevice/Attribute:connectablecis_list+' => 'Tous les matériels connectés à cet appareil réseau',
|
||||
'Class:NetworkDevice/Attribute:iosversion_id' => 'Version IOS',
|
||||
'Class:NetworkDevice/Attribute:iosversion_id+' => '',
|
||||
'Class:NetworkDevice/Attribute:iosversion_name' => 'Nom Version IOS',
|
||||
@@ -1597,7 +1597,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Class:lnkConnectableCIToNetworkDevice/Attribute:network_port+' => '',
|
||||
'Class:lnkConnectableCIToNetworkDevice/Attribute:device_port' => 'Port matériel',
|
||||
'Class:lnkConnectableCIToNetworkDevice/Attribute:device_port+' => '',
|
||||
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type' => 'Type de connexion',
|
||||
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type' => 'Type de connection',
|
||||
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type+' => '',
|
||||
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type/Value:downlink' => 'lien descendant',
|
||||
'Class:lnkConnectableCIToNetworkDevice/Attribute:connection_type/Value:downlink+' => 'lien descendant',
|
||||
|
||||
BIN
datamodels/2.x/itop-config-mgmt/images/application.png
Executable file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/applicationservice.png
Executable file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/building.png
Executable file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/business-process.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/computer.png
Executable file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/contact.png
Executable file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/database-instance.png
Executable file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/database-schema.png
Executable file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/database.png
Executable file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/diskarray.png
Executable file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
datamodels/2.x/itop-config-mgmt/images/document.png
Executable file
|
After Width: | Height: | Size: 5.0 KiB |