Merge branch 'support/3.2' into develop

This commit is contained in:
odain
2025-11-07 20:33:14 +01:00
1837 changed files with 33034 additions and 34549 deletions

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -48,8 +49,7 @@ class DBSearchHelper
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
}
}
catch (Exception $e) {
} catch (Exception $e) {
// If filtering fails just ignore it
}
}
@@ -57,4 +57,4 @@ class DBSearchHelper
}
}
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class ApplicationContext
*
@@ -47,16 +47,16 @@ interface iDBObjectURLMaker
/**
* Direct end-users to the standard iTop application: UI.php
*/
*/
class iTopStandardURLMaker implements iDBObjectURLMaker
{
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
public static function MakeObjectURL($sClass, $iId)
{
$sPage = DBObject::ComputeStandardUIPage($sClass);
@@ -68,16 +68,16 @@ class iTopStandardURLMaker implements iDBObjectURLMaker
/**
* Direct end-users to the standard Portal application
*/
*/
class PortalURLMaker implements iDBObjectURLMaker
{
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
public static function MakeObjectURL($sClass, $iId)
{
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
@@ -86,7 +86,6 @@ class PortalURLMaker implements iDBObjectURLMaker
}
}
/**
* Helper class to store and manipulate the parameters that make the application's context
*
@@ -99,99 +98,90 @@ class PortalURLMaker implements iDBObjectURLMaker
*/
class ApplicationContext
{
public static $m_sUrlMakerClass = null;
protected static $m_aPluginProperties = null;
protected static $aDefaultValues; // Cache shared among all instances
public static $m_sUrlMakerClass = null;
protected static $m_aPluginProperties = null;
protected static $aDefaultValues; // Cache shared among all instances
protected $aNames;
protected $aValues;
/**
* ApplicationContext constructor.
*
* @param bool $bReadContext
*
* @throws \Exception
*/
/**
* ApplicationContext constructor.
*
* @param bool $bReadContext
*
* @throws \Exception
*/
public function __construct($bReadContext = true)
{
$this->aNames = array(
'org_id', 'menu'
);
if ($bReadContext)
{
$this->ReadContext();
$this->aNames = [
'org_id', 'menu',
];
if ($bReadContext) {
$this->ReadContext();
}
}
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*
* @throws \Exception
*/
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*
* @throws \Exception
*/
protected function ReadContext()
{
if (!isset(self::$aDefaultValues))
{
self::$aDefaultValues = array();
$aContext = utils::ReadParam('c', array(), false, 'context_param');
foreach($this->aNames as $sName)
{
if (!isset(self::$aDefaultValues)) {
self::$aDefaultValues = [];
$aContext = utils::ReadParam('c', [], false, 'context_param');
foreach ($this->aNames as $sName) {
$sValue = isset($aContext[$sName]) ? $aContext[$sName] : '';
// TO DO: check if some of the context parameters are mandatory (or have default values)
if (!empty($sValue))
{
if (!empty($sValue)) {
self::$aDefaultValues[$sName] = $sValue;
}
// Hmm, there must be a better (more generic) way to handle the case below:
// When there is only one possible (allowed) organization, the context must be
// fixed to this org unless there is only one organization in the system then
// no filter is applied
if ($sName == 'org_id')
{
if (MetaModel::IsValidClass('Organization'))
{
if ($sName == 'org_id') {
if (MetaModel::IsValidClass('Organization')) {
$oSearchFilter = new DBObjectSearch('Organization');
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->CountWithLimit(2);
if ($iCount > 1)
{
if ($iCount > 1) {
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->CountWithLimit(2);
if ($iCount == 1)
{
if ($iCount == 1) {
// Only one possible value for org_id, set it in the context
$oOrg = $oSet->Fetch();
self::$aDefaultValues[$sName] = $oOrg->GetKey();
}
}
}
}
}
}
}
$this->aValues = self::$aDefaultValues;
}
/**
* Returns the current value for the given parameter
*
* @param string $sParamName Name of the parameter to read
* @param string $defaultValue
*
* @return mixed The value for this parameter
*/
/**
* Returns the current value for the given parameter
*
* @param string $sParamName Name of the parameter to read
* @param string $defaultValue
*
* @return mixed The value for this parameter
*/
public function GetCurrentValue($sParamName, $defaultValue = '')
{
if (isset($this->aValues[$sParamName]))
{
if (isset($this->aValues[$sParamName])) {
return $this->aValues[$sParamName];
}
return $defaultValue;
}
/**
* 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
@@ -200,21 +190,20 @@ class ApplicationContext
public function GetForLink(bool $bWithLeadingAmpersand = false)
{
// If there are no parameters, return an empty string
if(empty($this->aValues)){
if (empty($this->aValues)) {
return '';
}
// Build the query string with ampersand separated parameters
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams = [];
foreach ($this->aValues as $sName => $sValue) {
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
$sReturnValue = implode('&', $aParams);
// add the leading ampersand if requested
if($bWithLeadingAmpersand){
$sReturnValue = '&' . $sReturnValue;
if ($bWithLeadingAmpersand) {
$sReturnValue = '&'.$sReturnValue;
}
return $sReturnValue;
@@ -278,14 +267,13 @@ class ApplicationContext
*/
public function GetAsHash()
{
$aReturn = array();
foreach($this->aValues as $sName => $sValue)
{
$aReturn = [];
foreach ($this->aValues as $sName => $sValue) {
$aReturn["c[$sName]"] = $sValue;
}
return $aReturn;
}
/**
* Returns an array of the context parameters NAMEs
* @return array The list of context parameters
@@ -298,11 +286,10 @@ class ApplicationContext
* Removes the specified parameter from the context, for example when the same parameter
* is already a search parameter
* @param string $sParamName Name of the parameter to remove
*/
*/
public function Reset($sParamName)
{
if (isset($this->aValues[$sParamName]))
{
if (isset($this->aValues[$sParamName])) {
unset($this->aValues[$sParamName]);
}
}
@@ -318,27 +305,22 @@ class ApplicationContext
public function InitObjectFromContext(DBObject &$oObj)
{
$sClass = get_class($oObj);
foreach($this->GetNames() as $key)
{
$aCallSpec = array($sClass, 'MapContextParam');
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
foreach ($this->GetNames() as $key) {
$aCallSpec = [$sClass, 'MapContextParam'];
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
{
if (MetaModel::IsValidAttCode($sClass, $sAttCode)) {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsWritable())
{
if ($oAttDef->IsWritable()) {
$value = $this->GetCurrentValue($key, null);
if (!is_null($value))
{
if (!is_null($value)) {
$oObj->Set($sAttCode, $value);
}
}
}
}
}
}
}
/**
@@ -362,14 +344,10 @@ class ApplicationContext
*/
public static function GetUrlMakerClass()
{
if (is_null(self::$m_sUrlMakerClass))
{
if (Session::IsSet('UrlMakerClass'))
{
if (is_null(self::$m_sUrlMakerClass)) {
if (Session::IsSet('UrlMakerClass')) {
self::$m_sUrlMakerClass = Session::Get('UrlMakerClass');
}
else
{
} else {
self::$m_sUrlMakerClass = 'iTopStandardURLMaker';
}
}
@@ -387,23 +365,23 @@ class ApplicationContext
* @return string the name of the class
* @throws \Exception
*/
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
{
$oAppContext = new ApplicationContext();
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
{
$oAppContext = new ApplicationContext();
if (is_null($sUrlMakerClass)) {
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl.$oAppContext->GetForLink(true);
} else {
return $sUrl;
}
} else {
return '';
}
if (is_null($sUrlMakerClass)) {
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func([$sUrlMakerClass, 'MakeObjectUrl'], $sObjClass, $sObjKey);
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl.$oAppContext->GetForLink(true);
} else {
return $sUrl;
}
} else {
return '';
}
}
/**
@@ -412,13 +390,10 @@ class ApplicationContext
*/
protected static function LoadPluginProperties()
{
if (Session::IsSet('PluginProperties'))
{
if (Session::IsSet('PluginProperties')) {
self::$m_aPluginProperties = Session::Get('PluginProperties');
}
else
{
self::$m_aPluginProperties = array();
} else {
self::$m_aPluginProperties = [];
}
}
@@ -431,7 +406,9 @@ class ApplicationContext
*/
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
if (is_null(self::$m_aPluginProperties)) {
self::LoadPluginProperties();
}
self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
Session::Set(['PluginProperties', $sPluginClass, $sProperty], $value);
@@ -444,15 +421,14 @@ class ApplicationContext
*/
public static function GetPluginProperties($sPluginClass)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
if (array_key_exists($sPluginClass, self::$m_aPluginProperties))
{
return self::$m_aPluginProperties[$sPluginClass];
if (is_null(self::$m_aPluginProperties)) {
self::LoadPluginProperties();
}
else
{
return array();
if (array_key_exists($sPluginClass, self::$m_aPluginProperties)) {
return self::$m_aPluginProperties[$sPluginClass];
} else {
return [];
}
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* This class manages the audit "categories". Each category defines a set of objects
* to check and is linked to a set of rules that determine the valid or invalid objects
@@ -32,34 +32,36 @@ class AuditCategory extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"reconc_keys" => ['name'],
"db_table" => "priv_auditcategory",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", array("allowed_values"=>null, "sql"=>"definition_set", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array(), "edit_mode" => LINKSET_EDITMODE_INPLACE, "tracking_level" => LINKSET_TRACKING_ALL)));
MetaModel::Init_AddAttribute(new AttributeInteger("ok_error_tolerance", array("allowed_values"=>null, "sql"=>"ok_error_tolerance", "default_value"=>5, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("warning_error_tolerance", array("allowed_values" => null, "sql" => "warning_error_tolerance", "default_value" => 25, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("domains_list",
array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "category_id", "ext_key_to_remote" => "domain_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
MetaModel::Init_AddAttribute(new AttributeString("name", ["description" => "Short name for this category", "allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("description", ["allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", ["allowed_values" => null, "sql" => "definition_set", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", ["linked_class" => "AuditRule", "ext_key_to_me" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => [], "edit_mode" => LINKSET_EDITMODE_INPLACE, "tracking_level" => LINKSET_TRACKING_ALL]));
MetaModel::Init_AddAttribute(new AttributeInteger("ok_error_tolerance", ["allowed_values" => null, "sql" => "ok_error_tolerance", "default_value" => 5, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeInteger("warning_error_tolerance", ["allowed_values" => null, "sql" => "warning_error_tolerance", "default_value" => 25, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect(
"domains_list",
["linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "category_id", "ext_key_to_remote" => "domain_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => [], "display_style" => 'property']
));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'ok_error_tolerance', 'warning_error_tolerance', 'rules_list', 'domains_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', ['name', 'description', 'definition_set', 'ok_error_tolerance', 'warning_error_tolerance', 'rules_list', 'domains_list']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['description', ]); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'definition_set')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
MetaModel::Init_SetZListItems('standard_search', ['description', 'definition_set']); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', ['name', 'description']); // Criteria of the default search form
}
/**
@@ -74,9 +76,9 @@ class AuditCategory extends cmdbAbstractObject
public function GetReportColor($iTotal, $iErrors)
{
$sResult = 'red';
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) ) {
if (($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100)) {
$sResult = 'green';
} else if (($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100)) {
} elseif (($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100)) {
$sResult = 'orange';
}
@@ -93,4 +95,3 @@ class AuditCategory extends cmdbAbstractObject
return $aShortcutActions;
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* This class manages the audit "categories". Each category defines a set of objects
* to check and is linked to a set of rules that determine the valid or invalid objects
@@ -33,32 +33,34 @@ class AuditDomain extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => array('description'),
"complementary_name_attcode" => ['description'],
"state_attcode" => "",
"reconc_keys" => array('name'),
"reconc_keys" => ['name'],
"db_table" => "priv_auditdomain",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-album.svg'),
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("description" => "Short name for this category", "allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeImage("icon", array("is_null_allowed" => true, "depends_on" => array(), "display_max_width" => 96, "display_max_height" => 96, "storage_max_width" => 256, "storage_max_height" => 256, "default_image" => null, "always_load_in_tables" => false)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("categories_list",
array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("name", ["description" => "Short name for this category", "allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("description", ["allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeImage("icon", ["is_null_allowed" => true, "depends_on" => [], "display_max_width" => 96, "display_max_height" => 96, "storage_max_width" => 256, "storage_max_height" => 256, "default_image" => null, "always_load_in_tables" => false]));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect(
"categories_list",
["linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => []]
));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'icon', 'categories_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description',)); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', ['name', 'description', 'icon', 'categories_list']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['description',]); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
MetaModel::Init_SetZListItems('standard_search', ['description']); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', ['name', 'description']); // Criteria of the default search form
}
public static function GetShortcutActions($sFinalClass)
@@ -84,40 +86,39 @@ class lnkAuditCategoryToAuditDomain extends cmdbAbstractObject
*/
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array('category_id', 'domain_id'),
"reconc_keys" => ['category_id', 'domain_id'],
"db_table" => "priv_link_audit_category_domain",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true,
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
'uniqueness_rules' => [
'no_duplicate' => [
'attributes' => [
0 => 'category_id',
1 => 'domain_id',
),
],
'filter' => '',
'disabled' => false,
'is_blocking' => true,
),
),
);
],
],
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("targetclass" => "AuditCategory", "jointype" => '', "allowed_values" => null, "sql" => "category_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeExternalKey("domain_id", array("targetclass" => "AuditDomain", "jointype" => '', "allowed_values" => null, "sql" => "domain_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("domain_name", array("allowed_values" => null, "extkey_attcode" => 'domain_id', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", ["targetclass" => "AuditCategory", "jointype" => '', "allowed_values" => null, "sql" => "category_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", ["allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name"]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("domain_id", ["targetclass" => "AuditDomain", "jointype" => '', "allowed_values" => null, "sql" => "domain_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("domain_name", ["allowed_values" => null, "extkey_attcode" => 'domain_id', "target_attcode" => "name"]));
// Display lists
MetaModel::Init_SetZListItems('details', array('category_id', 'domain_id'));
MetaModel::Init_SetZListItems('list', array('category_id', 'domain_id'));
MetaModel::Init_SetZListItems('details', ['category_id', 'domain_id']);
MetaModel::Init_SetZListItems('list', ['category_id', 'domain_id']);
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'domain_id'));
MetaModel::Init_SetZListItems('standard_search', ['category_id', 'domain_id']);
}
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* This class manages the audit "rule" linked to a given audit category.
* Each rule is based on an OQL expression that returns either the "good" objects
@@ -33,35 +33,34 @@ class AuditRule extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"reconc_keys" => ['name'],
"db_table" => "priv_auditrule",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit.svg'),
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("query", array("allowed_values" => null, "sql" => "query", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", array("allowed_values" => new ValueSetEnum('true,false'), "sql" => "valid_flag", "default_value" => "true", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("allowed_values" => null, "sql" => "category_id", "targetclass" => "AuditCategory", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeString("name", ["allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("description", ["allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeOQL("query", ["allowed_values" => null, "sql" => "query", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", ["allowed_values" => new ValueSetEnum('true,false'), "sql" => "valid_flag", "default_value" => "true", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", ["allowed_values" => null, "sql" => "category_id", "targetclass" => "AuditCategory", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", ["allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name"]));
// Display lists
MetaModel::Init_SetZListItems('details', array('category_id', 'name', 'description', 'query', 'valid_flag')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('category_id', 'description', 'valid_flag')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', ['category_id', 'name', 'description', 'query', 'valid_flag']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['category_id', 'description', 'valid_flag']); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'category_id')); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', ['category_id', 'name', 'description', 'valid_flag', 'query']); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', ['name', 'description', 'category_id']); // Criteria of the advanced search form
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
@@ -72,4 +71,3 @@ class AuditRule extends cmdbAbstractObject
return $aShortcutActions;
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -32,7 +33,8 @@ class CompileCSSService
{
}
public function CompileCSSFromSASS($sSassContent, $aImportPaths = [], $aVariables = []){
public function CompileCSSFromSASS($sSassContent, $aImportPaths = [], $aVariables = [])
{
return utils::CompileCSSFromSASS($sSassContent, $aImportPaths, $aVariables);
}
}
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -38,21 +39,21 @@ abstract class DashboardLayout
* @since 2.7.0
*/
abstract public function GetDashletCoordinates($iCellIdx);
public static function GetInfo()
{
return array(
return [
'label' => '',
'icon' => '',
'description' => '',
);
];
}
}
abstract class DashboardLayoutMultiCol extends DashboardLayout
{
protected $iNbCols;
public function __construct()
{
$this->iNbCols = 1;
@@ -63,47 +64,38 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$aKeys = array_reverse(array_keys($aDashlets));
$idx = 0;
$bNoVisibleFound = true;
while($idx < count($aKeys) && $bNoVisibleFound)
{
while ($idx < count($aKeys) && $bNoVisibleFound) {
/** @var \Dashlet $oDashlet */
$oDashlet = $aDashlets[$aKeys[$idx]];
if ($oDashlet::IsVisible())
{
if ($oDashlet::IsVisible()) {
$bNoVisibleFound = false;
}
else
{
} else {
unset($aDashlets[$aKeys[$idx]]);
}
$idx++;
}
return $aDashlets;
}
protected function TrimCellsArray($aCells)
{
foreach($aCells as $key => $aDashlets)
{
foreach ($aCells as $key => $aDashlets) {
$aCells[$key] = $this->TrimCell($aDashlets);
}
$aKeys = array_reverse(array_keys($aCells));
$idx = 0;
$bNoVisibleFound = true;
while($idx < count($aKeys) && $bNoVisibleFound)
{
while ($idx < count($aKeys) && $bNoVisibleFound) {
$aDashlets = $aCells[$aKeys[$idx]];
if (count($aDashlets) > 0)
{
if (count($aDashlets) > 0) {
$bNoVisibleFound = false;
}
else
{
} else {
unset($aCells[$aKeys[$idx]]);
}
$idx++;
}
return $aCells;
return $aCells;
}
/**
@@ -112,7 +104,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
* @param bool $bEditMode
* @param array $aExtraParams
*/
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = array())
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = [])
{
// Trim the list of cells to remove the invisible/empty ones at the end of the array
$aCells = $this->TrimCellsArray($aCells);
@@ -157,8 +149,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$oPage->add_script("function updateDashboard".$aExtraParams['dashboard_div_id']."(){".$sJSReload."}");
if ($bEditMode) // Add one row for extensibility
{
if ($bEditMode) { // Add one row for extensibility
$oDashboardRow = new DashboardRow();
$oDashboardLayout->AddDashboardRow($oDashboardRow);
@@ -180,7 +171,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$iColNumber = (int) $iCellIdx % $this->iNbCols;
$iRowNumber = (int) floor($iCellIdx / $this->iNbCols);
return array($iColNumber, $iRowNumber);
return [$iColNumber, $iRowNumber];
}
}
@@ -191,13 +182,13 @@ class DashboardLayoutOneCol extends DashboardLayoutMultiCol
parent::__construct();
$this->iNbCols = 1;
}
static public function GetInfo()
public static function GetInfo()
{
return array(
return [
'label' => 'One Column',
'icon' => 'images/layout_1col.png',
'description' => '',
);
];
}
}
@@ -208,13 +199,13 @@ class DashboardLayoutTwoCols extends DashboardLayoutMultiCol
parent::__construct();
$this->iNbCols = 2;
}
static public function GetInfo()
public static function GetInfo()
{
return array(
return [
'label' => 'Two Columns',
'icon' => 'images/layout_2col.png',
'description' => '',
);
];
}
}
@@ -225,12 +216,12 @@ class DashboardLayoutThreeCols extends DashboardLayoutMultiCol
parent::__construct();
$this->iNbCols = 3;
}
static public function GetInfo()
public static function GetInfo()
{
return array(
return [
'label' => 'Two Columns',
'icon' => 'images/layout_3col.png',
'description' => '',
);
];
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -6,4 +7,4 @@
class BulkChangeException extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -6,4 +7,4 @@
class CSVParserException extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -6,4 +7,4 @@
class ConfigException extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -63,21 +64,20 @@ class CoreCannotSaveObjectException extends CoreException
public function getTextMessage()
{
$sTitle = Dict::S('UI:Error:SaveFailed');
$sContent = $sTitle;
$sContent = $sTitle;
if (count($this->aIssues) == 1) {
$sIssue = reset($this->aIssues);
$sContent .= $sIssue;
$sContent .= $sIssue;
} else {
foreach ($this->aIssues as $sError) {
$sContent .= " " . $sError . ", ";
$sContent .= " ".$sError.", ";
}
}
return $sContent;
}
public function getIssues()
{
return $this->aIssues;

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -35,10 +36,10 @@ class CoreException extends Exception
}
if (count($this->m_aContextData) > 0) {
$sMessage .= ": ";
$aContextItems = array();
$aContextItems = [];
foreach ($this->m_aContextData as $sKey => $value) {
if (is_array($value)) {
$aPairs = array();
$aPairs = [];
foreach ($value as $key => $val) {
if (is_array($val)) {
$aPairs[] = $key.'=>('.implode(', ', $val).')';
@@ -109,4 +110,4 @@ class CoreException extends Exception
{
return $this->m_aContextData;
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -9,5 +10,4 @@
*/
class CorePortalInvalidActionRuleException extends CoreException
{
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -14,4 +15,4 @@ class CoreTemplateException extends CoreException
$sMessage = "Twig Exception when rendering '$sTemplatePath' : ".$oTwigException->getMessage();
parent::__construct($sMessage, null, '', $oTwigException);
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -6,4 +7,4 @@
class DeleteException extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -12,13 +12,13 @@ class InvalidExternalKeyValueException extends CoreUnexpectedValue
public function __construct($oObject, $sAttCode, $aContextData = null, $oPrevious = null)
{
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject) . '::' . $oObject->GetKey();
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject).'::'.$oObject->GetKey();
$aContextData[self::ENUM_PARAMS_ATTCODE] = $sAttCode;
$aContextData[self::ENUM_PARAMS_ATTVALUE] = $oObject->Get($sAttCode);
$oCurrentUser = UserRights::GetUserObject();
if (false === is_null($oCurrentUser)) {
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser) . '::' . $oCurrentUser->GetKey();
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser).'::'.$oCurrentUser->GetKey();
}
parent::__construct('Attribute pointing to an object that is either non existing or not readable by the current user', $aContextData, '', $oPrevious);

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -11,4 +12,4 @@
*/
class InvalidPasswordAttributeOneWayPassword extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -10,4 +11,4 @@ use Exception;
class PageNotFoundException extends Exception
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -6,4 +7,4 @@
class SynchroExceptionNotStarted extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -6,4 +7,4 @@
class UserRightException extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -6,4 +7,4 @@
class DictException extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -8,9 +9,9 @@ class DictExceptionMissingString extends DictException
{
public function __construct($sLanguageCode, $sStringCode)
{
$aContext = array();
$aContext = [];
$aContext['language_code'] = $sLanguageCode;
$aContext['string_code'] = $sStringCode;
parent::__construct('Missing localized string', $aContext);
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -8,8 +9,8 @@ class DictExceptionUnknownLanguage extends DictException
{
public function __construct($sLanguageCode)
{
$aContext = array();
$aContext = [];
$aContext['language_code'] = $sLanguageCode;
parent::__construct('Unknown localization language', $aContext);
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -6,5 +7,4 @@
class iTopXmlException extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -20,7 +21,7 @@ class MySQLException extends CoreException
$aContext['mysql_errno'] = $oException->getCode();
$this->code = $oException->getCode();
$aContext['mysql_error'] = $oException->getMessage();
} else if ($oMysqli != null) {
} elseif ($oMysqli != null) {
$aContext['mysql_errno'] = $oMysqli->errno;
$this->code = $oMysqli->errno;
$aContext['mysql_error'] = $oMysqli->error;
@@ -36,4 +37,4 @@ class MySQLException extends CoreException
error_reporting(0);
}
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -19,14 +20,14 @@ class MySQLHasGoneAwayException extends MySQLException
*/
public static function getErrorCodes()
{
return array(
return [
2006,
2013,
);
];
}
public function __construct($sIssue, $aContext)
{
parent::__construct($sIssue, $aContext, null);
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -9,5 +10,4 @@
*/
class MySQLNoTransactionException extends MySQLException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -11,5 +12,4 @@
*/
class MySQLQueryHasNoResultException extends MySQLException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -9,5 +10,4 @@
*/
class MySQLTransactionNotClosedException extends MySQLException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -12,4 +13,4 @@
*/
class ProcessException extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -13,4 +14,4 @@
*/
class ProcessFatalException extends CoreException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -9,4 +10,4 @@
*/
class ProcessInvalidConfigException extends ProcessException
{
}
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -22,8 +23,8 @@
* @author Olivier DAIN <olivier.dain@combodo.com>
* @since 3.0.0 N°3588
*/
class FindStylesheetObject{
class FindStylesheetObject
{
//file URIs
private $aStylesheetFileURIs;
@@ -64,7 +65,7 @@ class FindStylesheetObject{
return $this->aStylesheetFileURIs;
}
public function GetLastModified() : int
public function GetLastModified(): int
{
return $this->iLastModified;
}
@@ -92,7 +93,8 @@ class FindStylesheetObject{
$this->sLastStyleSheetPath = $sStylesheetFilePath;
}
public function AlreadyFetched(string $sStylesheetFilePath) : bool {
public function AlreadyFetched(string $sStylesheetFilePath): bool
{
return in_array($sStylesheetFilePath, $this->aAllStylesheetFilePaths);
}
@@ -111,4 +113,4 @@ class FindStylesheetObject{
{
$this->sLastStyleSheetPath = "";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -18,23 +18,17 @@ class LoginBasic extends AbstractLoginFSMExtension
*/
public function ListSupportedLoginModes()
{
return array('basic');
return ['basic'];
}
protected function OnModeDetection(&$iErrorCode)
{
if (!Session::IsSet('login_mode'))
{
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
{
if (!Session::IsSet('login_mode')) {
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) {
Session::Set('login_mode', 'basic');
}
elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
{
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
Session::Set('login_mode', 'basic');
}
elseif (isset($_SERVER['PHP_AUTH_USER']))
{
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
Session::Set('login_mode', 'basic');
}
}
@@ -43,22 +37,18 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnReadCredentials(&$iErrorCode)
{
if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'basic')
{
if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'basic') {
list($sAuthUser) = $this->GetAuthUserAndPassword();
Session::Set('login_temp_auth_user', $sAuthUser);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'basic')
{
if (Session::Get('login_mode') == 'basic') {
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
{
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -69,8 +59,7 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnCredentialsOK(&$iErrorCode)
{
if (Session::Get('login_mode') == 'basic')
{
if (Session::Get('login_mode') == 'basic') {
LoginWebPage::OnLoginSuccess(Session::Get('auth_user'), 'internal', Session::Get('login_mode'));
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -78,13 +67,11 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnError(&$iErrorCode)
{
if (Session::Get('login_mode') == 'basic')
{
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
if (Session::Get('login_mode') == 'basic') {
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -92,8 +79,7 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnConnected(&$iErrorCode)
{
if (Session::Get('login_mode') == 'basic')
{
if (Session::Get('login_mode') == 'basic') {
Session::Set('can_logoff', true);
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
@@ -105,42 +91,33 @@ class LoginBasic extends AbstractLoginFSMExtension
$sAuthUser = '';
$sAuthPwd = null;
$sAuthorization = '';
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
{
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) {
$sAuthorization = $_SERVER['HTTP_AUTHORIZATION'];
}
elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
{
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
$sAuthorization = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
}
if (!empty($sAuthorization))
{
if (!empty($sAuthorization)) {
list($sAuthUser, $sAuthPwd) = explode(':', base64_decode(substr($sAuthorization, 6)));
}
else
{
if (isset($_SERVER['PHP_AUTH_USER']))
{
} else {
if (isset($_SERVER['PHP_AUTH_USER'])) {
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
// Unfortunately, the RFC is not clear about the encoding...
// IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8
// So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base
if (!LoginWebPage::LooksLikeUTF8($sAuthUser))
{
if (!LoginWebPage::LooksLikeUTF8($sAuthUser)) {
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser);
}
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
if (!LoginWebPage::LooksLikeUTF8($sAuthPwd))
{
if (!LoginWebPage::LooksLikeUTF8($sAuthPwd)) {
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd);
}
}
}
return array($sAuthUser, $sAuthPwd);
return [$sAuthUser, $sAuthPwd];
}
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -18,7 +19,7 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
*/
public function ListSupportedLoginModes()
{
return array('before');
return ['before'];
}
protected function OnStart(&$iErrorCode)
@@ -31,13 +32,10 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
$sProposedLoginMode = utils::ReadParam('login_mode', '');
$index = array_search($sProposedLoginMode, $aAllowedLoginTypes);
if ($index !== false)
{
if ($index !== false) {
// Force login mode
Session::Set('login_mode', $sProposedLoginMode);
}
else
{
} else {
Session::Unset('login_mode');
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -49,8 +47,7 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
$sProposedLoginMode = utils::ReadParam('login_mode', '');
$index = array_search($sProposedLoginMode, $aAllowedLoginTypes);
if ($index !== false)
{
if ($index !== false) {
// Force login mode
LoginWebPage::SetLoginModeAndReload($sProposedLoginMode);
} else {
@@ -69,8 +66,6 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
*/
class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExtension
{
/**
* Must be executed after the other login plugins
*
@@ -78,19 +73,16 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
*/
public function ListSupportedLoginModes()
{
return array('after');
return ['after'];
}
protected function OnError(&$iErrorCode)
{
self::ResetLoginSession();
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
if ($iOnExit === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
elseif ($iOnExit == LoginWebPage::EXIT_HTTP_401)
{
} elseif ($iOnExit == LoginWebPage::EXIT_HTTP_401) {
LoginWebPage::HTTP401Error(); // Error, exit
}
// LoginWebPage::EXIT_PROMPT
@@ -99,13 +91,12 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
protected function OnCredentialsOk(&$iErrorCode)
{
if (!Session::IsSet('login_mode'))
{
// N°6358 - if EXIT_RETURN was asked, send an error
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
if (!Session::IsSet('login_mode')) {
// N°6358 - if EXIT_RETURN was asked, send an error
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
// If no plugin validated the user, exit
self::ResetLoginSession();
@@ -125,7 +116,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
protected function OnConnected(&$iErrorCode)
{
Session::Unset('login_temp_auth_user');
if (is_null(UserRights::GetUserObject())){
if (is_null(UserRights::GetUserObject())) {
//N°7085 avoid infinite loop
IssueLog::Error("No user logged in. exit");
exit(-1);
@@ -137,10 +128,8 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
private static function ResetLoginSession()
{
LoginWebPage::ResetSession();
foreach (Session::ListVariables() as $sKey)
{
if (utils::StartsWith($sKey, 'login_'))
{
foreach (Session::ListVariables() as $sKey) {
if (utils::StartsWith($sKey, 'login_')) {
Session::Unset($sKey);
}
}

View File

@@ -11,7 +11,6 @@ use Combodo\iTop\Application\Helper\Session;
class LoginExternal extends AbstractLoginFSMExtension
{
/**
* Return the list of supported login modes for this plugin
*
@@ -19,16 +18,14 @@ class LoginExternal extends AbstractLoginFSMExtension
*/
public function ListSupportedLoginModes()
{
return array('external');
return ['external'];
}
protected function OnModeDetection(&$iErrorCode)
{
if (!Session::IsSet('login_mode'))
{
if (!Session::IsSet('login_mode')) {
$sAuthUser = $this->GetAuthUser();
if ($sAuthUser && (strlen($sAuthUser) > 0))
{
if ($sAuthUser && (strlen($sAuthUser) > 0)) {
Session::Set('login_mode', 'external');
}
}
@@ -37,11 +34,9 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'external')
{
if (Session::Get('login_mode') == 'external') {
$sAuthUser = $this->GetAuthUser();
if (!UserRights::CheckCredentials($sAuthUser, '', Session::Get('login_mode'), 'external'))
{
if (!UserRights::CheckCredentials($sAuthUser, '', Session::Get('login_mode'), 'external')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -52,8 +47,7 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnCredentialsOK(&$iErrorCode)
{
if (Session::Get('login_mode') == 'external')
{
if (Session::Get('login_mode') == 'external') {
LoginWebPage::OnLoginSuccess(Session::Get('auth_user'), 'external', Session::Get('login_mode'));
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -61,8 +55,7 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnConnected(&$iErrorCode)
{
if (Session::Get('login_mode') == 'external')
{
if (Session::Get('login_mode') == 'external') {
Session::Set('can_logoff', false);
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
@@ -71,13 +64,11 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnError(&$iErrorCode)
{
if (Session::Get('login_mode') == 'external')
{
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
if (Session::Get('login_mode') == 'external') {
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;

View File

@@ -23,7 +23,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
public function ListSupportedLoginModes()
{
return array('form');
return ['form'];
}
/**
@@ -34,19 +34,17 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'form') {
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd))
{
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
{
if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd)) {
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER)) {
// X-Combodo-Ajax is a special header automatically added to all ajax requests
// Let's reply that we're currently logged-out
header('HTTP/1.0 401 Unauthorized');
exit;
}
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
// No credentials yet, display the form
$oPage = LoginWebPage::NewLoginWebPage();
@@ -66,12 +64,10 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'form')
{
if (Session::Get('login_mode') == 'form') {
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
{
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -85,8 +81,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnCredentialsOK(&$iErrorCode)
{
if (Session::Get('login_mode') == 'form')
{
if (Session::Get('login_mode') == 'form') {
// Store 'auth_user' in session for further use
LoginWebPage::OnLoginSuccess(Session::Get('auth_user'), 'internal', Session::Get('login_mode'));
}
@@ -98,8 +93,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnError(&$iErrorCode)
{
if (Session::Get('login_mode') == 'form')
{
if (Session::Get('login_mode') == 'form') {
$this->bForceFormOnError = true;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -110,8 +104,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnConnected(&$iErrorCode)
{
if (Session::Get('login_mode') == 'form')
{
if (Session::Get('login_mode') == 'form') {
Session::Set('can_logoff', true);
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
@@ -131,24 +124,23 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
$sAuthPwd = utils::ReadParam('suggest_pwd', '', true, 'raw_data');
$aData = array(
$aData = [
'sAuthUser' => $sAuthUser,
'sAuthPwd' => $sAuthPwd,
);
];
$oLoginContext->AddBlockExtension('login_input', new LoginBlockExtension('extensionblock/loginforminput.html.twig', $aData));
$oLoginContext->AddBlockExtension('login_submit', new LoginBlockExtension('extensionblock/loginformsubmit.html.twig'));
$oLoginContext->AddBlockExtension('login_form_footer', new LoginBlockExtension('extensionblock/loginformfooter.html.twig'));
$bEnableResetPassword = MetaModel::GetConfig()->Get('forgot_password');
$sResetPasswordUrl = MetaModel::GetConfig()->Get('forgot_password.url');
if ($sResetPasswordUrl == '')
{
$sResetPasswordUrl = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?loginop=forgot_pwd';
if ($sResetPasswordUrl == '') {
$sResetPasswordUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=forgot_pwd';
}
$aData = array(
$aData = [
'bEnableResetPassword' => $bEnableResetPassword,
'sResetPasswordUrl' => $sResetPasswordUrl,
);
];
$oLoginContext->AddBlockExtension('login_links', new LoginBlockExtension('extensionblock/loginformlinks.html.twig', $aData));
return $oLoginContext;

View File

@@ -6,7 +6,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\Branding;
use Combodo\iTop\Application\TwigBase\Twig\Extension;
use Combodo\iTop\Application\WebPage\NiceWebPage;
@@ -41,11 +40,11 @@ class LoginTwigContext
*/
public function __construct()
{
$this->aBlockExtension = array();
$this->aPostedVars = array();
$this->aBlockExtension = [];
$this->aPostedVars = [];
$this->sTwigLoaderPath = null;
$this->aCSSFiles = array();
$this->aJsFiles = array();
$this->aCSSFiles = [];
$this->aJsFiles = [];
$this->sTwigNameSpace = null;
}
@@ -179,7 +178,7 @@ class LoginBlockExtension
* @param array $aData Data given to the twig template (into the variable {{ aData }})
* @api
*/
public function __construct($sTwig, $aData = array())
public function __construct($sTwig, $aData = [])
{
$this->sTwig = $sTwig;
$this->aData = $aData;
@@ -210,21 +209,18 @@ class LoginTwigRenderer
public function __construct()
{
$this->aLoginPluginList = LoginWebPage::GetLoginPluginList('iLoginUIExtension', false);
$this->aPluginFormData = array();
$aTwigLoaders = array();
$this->aPostedVars = array();
foreach ($this->aLoginPluginList as $oLoginPlugin)
{
$this->aPluginFormData = [];
$aTwigLoaders = [];
$this->aPostedVars = [];
foreach ($this->aLoginPluginList as $oLoginPlugin) {
/** @var \iLoginUIExtension $oLoginPlugin */
$oLoginContext = $oLoginPlugin->GetTwigContext();
if (is_null($oLoginContext))
{
if (is_null($oLoginContext)) {
continue;
}
$this->aPluginFormData[] = $oLoginContext;
$sTwigLoaderPath = $oLoginContext->GetTwigLoaderPath();
if ($sTwigLoaderPath != null)
{
if ($sTwigLoaderPath != null) {
$oExtensionLoader = new FilesystemLoader();
$oExtensionLoader->setPaths($sTwigLoaderPath);
$aTwigLoaders[] = $oExtensionLoader;
@@ -232,8 +228,8 @@ class LoginTwigRenderer
$this->aPostedVars = array_merge($this->aPostedVars, $oLoginContext->GetPostedVars());
}
$oCoreLoader = new FilesystemLoader(array(), APPROOT.'templates');
$aCoreTemplatesPaths = array('pages/login', 'pages/login/password');
$oCoreLoader = new FilesystemLoader([], APPROOT.'templates');
$aCoreTemplatesPaths = ['pages/login', 'pages/login/password'];
// Having this path declared after the plugins let the plugins replace the core templates
$oCoreLoader->setPaths($aCoreTemplatesPaths);
// Having the core templates accessible within a different namespace offer the possibility to extend them while replacing them
@@ -251,19 +247,19 @@ class LoginTwigRenderer
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
$sDisplayIcon = Branding::GetLoginLogoAbsoluteUrl();
$aVars = array(
$aVars = [
'sAppRootUrl' => utils::GetAbsoluteUrlAppRoot(),
'aPluginFormData' => $this->GetPluginFormData(),
'sItopVersion' => ITOP_VERSION,
'sVersionShort' => $sVersionShort,
'sIconUrl' => $sIconUrl,
'sDisplayIcon' => $sDisplayIcon,
);
];
return $aVars;
}
public function Render(NiceWebPage $oPage, $sTwigFile, $aVars = array())
public function Render(NiceWebPage $oPage, $sTwigFile, $aVars = [])
{
$oTemplate = $this->GetTwig()->load($sTwigFile);
$oPage->add($oTemplate->renderBlock('body', $aVars));
@@ -272,17 +268,14 @@ class LoginTwigRenderer
$oPage->add_style($oTemplate->renderBlock('css', $aVars));
// Render CSS links
foreach ($this->aPluginFormData as $oFormData)
{
foreach ($this->aPluginFormData as $oFormData) {
/** @var \LoginTwigContext $oFormData */
$aCSSFiles = $oFormData->GetCSSFiles();
foreach ($aCSSFiles as $sCSSFile)
{
foreach ($aCSSFiles as $sCSSFile) {
$oPage->LinkStylesheetFromURI($sCSSFile);
}
$aJsFiles = $oFormData->GetJsFiles();
foreach ($aJsFiles as $sJsFile)
{
foreach ($aJsFiles as $sJsFile) {
$oPage->LinkScriptFromURI($sJsFile);
}

View File

@@ -23,17 +23,15 @@ class LoginURL extends AbstractLoginFSMExtension
*/
public function ListSupportedLoginModes()
{
return array('url');
return ['url'];
}
protected function OnModeDetection(&$iErrorCode)
{
if (!Session::IsSet('login_mode') && !$this->bErrorOccurred)
{
if (!Session::IsSet('login_mode') && !$this->bErrorOccurred) {
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (!empty($sAuthUser) && !empty($sAuthPwd))
{
if (!empty($sAuthUser) && !empty($sAuthPwd)) {
Session::Set('login_mode', 'url');
}
}
@@ -42,8 +40,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnReadCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
Session::Set('login_temp_auth_user', utils::ReadParam('auth_user', '', false, 'raw_data'));
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -51,12 +48,10 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
{
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -67,8 +62,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnCredentialsOK(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
LoginWebPage::OnLoginSuccess(Session::Get('auth_user'), 'internal', Session::Get('login_mode'));
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -76,8 +70,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnError(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
$this->bErrorOccurred = true;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -85,8 +78,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnConnected(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
Session::Set('can_logoff', true);
return LoginWebPage::CheckLoggedUser($iErrorCode);
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class LoginWebPage
*
@@ -37,33 +37,33 @@ use Combodo\iTop\Service\Events\EventService;
class LoginWebPage extends NiceWebPage
{
const EXIT_PROMPT = 0;
const EXIT_HTTP_401 = 1;
const EXIT_RETURN = 2;
const EXIT_CODE_OK = 0;
const EXIT_CODE_MISSINGLOGIN = 1;
const EXIT_CODE_MISSINGPASSWORD = 2;
const EXIT_CODE_WRONGCREDENTIALS = 3;
const EXIT_CODE_MUSTBEADMIN = 4;
const EXIT_CODE_PORTALUSERNOTAUTHORIZED = 5;
const EXIT_CODE_NOTAUTHORIZED = 6;
public const EXIT_PROMPT = 0;
public const EXIT_HTTP_401 = 1;
public const EXIT_RETURN = 2;
public const EXIT_CODE_OK = 0;
public const EXIT_CODE_MISSINGLOGIN = 1;
public const EXIT_CODE_MISSINGPASSWORD = 2;
public const EXIT_CODE_WRONGCREDENTIALS = 3;
public const EXIT_CODE_MUSTBEADMIN = 4;
public const EXIT_CODE_PORTALUSERNOTAUTHORIZED = 5;
public const EXIT_CODE_NOTAUTHORIZED = 6;
// Login FSM States
const LOGIN_STATE_START = 'start'; // Entry state
const LOGIN_STATE_MODE_DETECTION = 'login mode detection'; // Detect which login plugin to use
const LOGIN_STATE_READ_CREDENTIALS = 'read credentials'; // Read the credentials
const LOGIN_STATE_CHECK_CREDENTIALS = 'check credentials'; // Check if the credentials are valid
const LOGIN_STATE_CREDENTIALS_OK = 'credentials ok'; // User provisioning
const LOGIN_STATE_USER_OK = 'user ok'; // Additional check (2FA)
const LOGIN_STATE_CONNECTED = 'connected'; // User connected
const LOGIN_STATE_SET_ERROR = 'prepare for error'; // Internal state to trigger ERROR state
const LOGIN_STATE_ERROR = 'error'; // An error occurred, next state will be NONE
public const LOGIN_STATE_START = 'start'; // Entry state
public const LOGIN_STATE_MODE_DETECTION = 'login mode detection'; // Detect which login plugin to use
public const LOGIN_STATE_READ_CREDENTIALS = 'read credentials'; // Read the credentials
public const LOGIN_STATE_CHECK_CREDENTIALS = 'check credentials'; // Check if the credentials are valid
public const LOGIN_STATE_CREDENTIALS_OK = 'credentials ok'; // User provisioning
public const LOGIN_STATE_USER_OK = 'user ok'; // Additional check (2FA)
public const LOGIN_STATE_CONNECTED = 'connected'; // User connected
public const LOGIN_STATE_SET_ERROR = 'prepare for error'; // Internal state to trigger ERROR state
public const LOGIN_STATE_ERROR = 'error'; // An error occurred, next state will be NONE
// Login FSM Returns
const LOGIN_FSM_RETURN = 0; // End the FSM OK (connected)
const LOGIN_FSM_ERROR = 1; // Error signaled
const LOGIN_FSM_CONTINUE = 2; // Continue FSM
public const LOGIN_FSM_RETURN = 0; // End the FSM OK (connected)
public const LOGIN_FSM_ERROR = 1; // Error signaled
public const LOGIN_FSM_CONTINUE = 2; // Continue FSM
protected static $sHandlerClass = __class__;
private static $iOnExit;
@@ -78,7 +78,7 @@ class LoginWebPage extends NiceWebPage
*/
public static function NewLoginWebPage()
{
return new self::$sHandlerClass;
return new self::$sHandlerClass();
}
protected static $m_sLoginFailedMessage = '';
@@ -94,7 +94,7 @@ class LoginWebPage extends NiceWebPage
$this->no_cache();
$this->add_http_headers();
}
public function SetStyleSheet()
{
$this->LinkStylesheetFromAppRoot('css/login.css');
@@ -128,23 +128,18 @@ class LoginWebPage extends NiceWebPage
$oProfilesSet = $oUser->Get('profile_list');
//delete old profiles
$aExistingProfiles = [];
while ($oProfile = $oProfilesSet->Fetch())
{
while ($oProfile = $oProfilesSet->Fetch()) {
array_push($aExistingProfiles, $oProfile->Get('profileid'));
$iArrayKey = array_search($oProfile->Get('profileid'), $aProfiles);
if (!$iArrayKey)
{
if (!$iArrayKey) {
$oProfilesSet->RemoveItem($oProfile->Get('profileid'));
}
else
{
} else {
unset($aProfiles[$iArrayKey]);
}
}
//add profiles not already linked with user
foreach ($aProfiles as $iProfileId)
{
$oProfilesSet->AddItem(MetaModel::NewObject('URP_UserProfile', array('profileid' => $iProfileId, 'reason' => $sOrigin)));
foreach ($aProfiles as $iProfileId) {
$oProfilesSet->AddItem(MetaModel::NewObject('URP_UserProfile', ['profileid' => $iProfileId, 'reason' => $sOrigin]));
}
$oUser->Set('profile_list', $oProfilesSet);
}
@@ -154,56 +149,49 @@ class LoginWebPage extends NiceWebPage
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
$sDisplayIcon = Branding::GetLoginLogoAbsoluteUrl();
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES,
self::PAGES_CHARSET)."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities(
$sIconUrl,
ENT_QUOTES,
self::PAGES_CHARSET
)."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
}
public function DisplayLoginForm($bFailedLogin = false)
{
$oTwigContext = new LoginTwigRenderer();
$aPostedVars = array_merge(array('login_mode', 'loginop'), $oTwigContext->GetPostedVars());
$aPostedVars = array_merge(['login_mode', 'loginop'], $oTwigContext->GetPostedVars());
$sMessage = Dict::S('UI:Login:IdentifyYourself');
// Error message
if ($bFailedLogin)
{
if (self::$m_sLoginFailedMessage != '')
{
if ($bFailedLogin) {
if (self::$m_sLoginFailedMessage != '') {
$sMessage = self::$m_sLoginFailedMessage;
}
else
{
} else {
$sMessage = Dict::S('UI:Login:IncorrectLoginPassword');
}
}
// Keep the OTHER parameters posted
$aPreviousPostedVars = array();
foreach($_POST as $sPostedKey => $postedValue)
{
if (!in_array($sPostedKey, $aPostedVars))
{
if (is_array($postedValue))
{
foreach($postedValue as $sKey => $sValue)
{
$aPreviousPostedVars = [];
foreach ($_POST as $sPostedKey => $postedValue) {
if (!in_array($sPostedKey, $aPostedVars)) {
if (is_array($postedValue)) {
foreach ($postedValue as $sKey => $sValue) {
$sName = "{$sPostedKey}[{$sKey}]";
$aPreviousPostedVars[$sName] = $sValue;
}
}
else
{
} else {
$aPreviousPostedVars[$sPostedKey] = $postedValue;
}
}
}
$aVars = array(
$aVars = [
'bFailedLogin' => $bFailedLogin,
'sMessage' => $sMessage,
'aPreviousPostedVars' => $aPreviousPostedVars,
);
];
$aVars = array_merge($aVars, $oTwigContext->GetDefaultVars());
$oTwigContext->Render($this, 'login.html.twig', $aVars);
@@ -226,26 +214,21 @@ class LoginWebPage extends NiceWebPage
{
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
try
{
try {
UserRights::Login($sAuthUser); // Set the user's language (if possible!)
/** @var UserInternal $oUser */
$oUser = UserRights::GetUserObject();
/** @var UserInternal $oUser */
$oUser = UserRights::GetUserObject();
if ($oUser != null)
{
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token'))
{
if ($oUser != null) {
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token')) {
throw new Exception(Dict::S('UI:ResetPwd-Error-NotPossible'));
}
if (!$oUser->CanChangePassword())
{
if (!$oUser->CanChangePassword()) {
throw new Exception(Dict::S('UI:ResetPwd-Error-FixedPwd'));
}
$sTo = $oUser->GetResetPasswordEmail(); // throws Exceptions if not allowed
if ($sTo == '')
{
if ($sTo == '') {
throw new Exception(Dict::S('UI:ResetPwd-Error-NoEmail'));
}
@@ -265,8 +248,7 @@ class LoginWebPage extends NiceWebPage
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
$oEmail->SetBody(Dict::Format('UI:ResetPwd-EmailBody', $sResetUrl, $oUser->Get('login')));
$iRes = $oEmail->Send($aIssues, true /* force synchronous exec */);
switch ($iRes)
{
switch ($iRes) {
//case EMAIL_SEND_PENDING:
case EMAIL_SEND_OK:
break;
@@ -278,13 +260,10 @@ class LoginWebPage extends NiceWebPage
}
}
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$oTwigContext->Render($this, 'forgotpwdsent.html.twig', $aVars);
}
catch(Exception $e)
{
} catch (Exception $e) {
$this->DisplayForgotPwdForm(true, $e->getMessage());
}
}
@@ -304,22 +283,16 @@ class LoginWebPage extends NiceWebPage
$aVars['sToken'] = $sToken;
$aVars['sErrorMessage'] = $sErrorMessage;
if (($oUser == null))
{
if (($oUser == null)) {
$aVars['bNoUser'] = true;
}
else
{
} else {
$aVars['bNoUser'] = false;
$aVars['sUserName'] = $oUser->GetFriendlyName();
$oEncryptedToken = $oUser->Get('reset_pwd_token');
if (!$oEncryptedToken->CheckPassword($sToken))
{
if (!$oEncryptedToken->CheckPassword($sToken)) {
$aVars['bBadToken'] = true;
}
else
{
} else {
$aVars['bBadToken'] = false;
}
}
@@ -342,21 +315,15 @@ class LoginWebPage extends NiceWebPage
$aVars['sAuthUser'] = $sAuthUser;
$aVars['sToken'] = $sToken;
if (($oUser == null))
{
if (($oUser == null)) {
$aVars['bNoUser'] = true;
}
else
{
} else {
$aVars['bNoUser'] = false;
$oEncryptedToken = $oUser->Get('reset_pwd_token');
if (!$oEncryptedToken->CheckPassword($sToken))
{
if (!$oEncryptedToken->CheckPassword($sToken)) {
$aVars['bBadToken'] = true;
}
else
{
} else {
$aVars['bBadToken'] = false;
// Trash the token and change the password
$oUser->Set('reset_pwd_token', new ormPassword());
@@ -413,7 +380,7 @@ class LoginWebPage extends NiceWebPage
// Note: This will destroy the session, and not just the session data!
}
static function SecureConnectionRequired()
public static function SecureConnectionRequired()
{
return MetaModel::GetConfig()->GetSecureConnectionRequired();
}
@@ -423,7 +390,7 @@ class LoginWebPage extends NiceWebPage
* @param string $sString
* @return bool True if the string contains some typical UTF-8 multi-byte sequences
*/
static function LooksLikeUTF8($sString)
public static function LooksLikeUTF8($sString)
{
return preg_match('%(?:
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
@@ -446,22 +413,19 @@ class LoginWebPage extends NiceWebPage
protected static function Login($iOnExit)
{
self::$iOnExit = $iOnExit;
if (self::SecureConnectionRequired() && !utils::IsConnectionSecure())
{
if (self::SecureConnectionRequired() && !utils::IsConnectionSecure()) {
// Non secured URL... request for a secure connection
throw new Exception('Secure connection required!');
}
$bLoginDebug = MetaModel::GetConfig()->Get('login_debug');
if (Session::Get('login_state') == self::LOGIN_STATE_ERROR)
{
if (Session::Get('login_state') == self::LOGIN_STATE_ERROR) {
Session::Set('login_state', self::LOGIN_STATE_START);
}
$sLoginState = Session::Get('login_state');
$sSessionLog = '';
if ($bLoginDebug)
{
if ($bLoginDebug) {
IssueLog::Info("---------------------------------");
IssueLog::Info($_SERVER['REQUEST_URI']);
IssueLog::Info("--> Entering Login FSM with state: [$sLoginState]");
@@ -472,38 +436,30 @@ class LoginWebPage extends NiceWebPage
$iErrorCode = self::EXIT_CODE_OK;
// Finite state machine loop
while (true)
{
try
{
while (true) {
try {
$aLoginPlugins = self::GetLoginPluginList();
if (empty($aLoginPlugins))
{
if (empty($aLoginPlugins)) {
throw new Exception("Missing login classes");
}
/** @var iLoginFSMExtension $oLoginFSMExtensionInstance */
foreach ($aLoginPlugins as $oLoginFSMExtensionInstance)
{
if ($bLoginDebug)
{
foreach ($aLoginPlugins as $oLoginFSMExtensionInstance) {
if ($bLoginDebug) {
$sCurrSessionLog = session_id().' '.utils::GetSessionLog();
if ($sCurrSessionLog != $sSessionLog)
{
if ($sCurrSessionLog != $sSessionLog) {
$sSessionLog = $sCurrSessionLog;
IssueLog::Info("SESSION: $sSessionLog");
}
IssueLog::Info("Login: state: [$sLoginState] call: ".get_class($oLoginFSMExtensionInstance));
}
$iResponse = $oLoginFSMExtensionInstance->LoginAction($sLoginState, $iErrorCode);
if ($iResponse == self::LOGIN_FSM_RETURN)
{
if ($iResponse == self::LOGIN_FSM_RETURN) {
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['code' => $iErrorCode, 'state' => $sLoginState]));
Session::WriteClose();
return $iErrorCode; // Asked to exit FSM, generally login OK
}
if ($iResponse == self::LOGIN_FSM_ERROR)
{
if ($iResponse == self::LOGIN_FSM_ERROR) {
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['code' => $iErrorCode, 'state' => $sLoginState]));
$sLoginState = self::LOGIN_STATE_SET_ERROR; // Next state will be error
// An error was detected, skip the other plugins turn
@@ -515,9 +471,7 @@ class LoginWebPage extends NiceWebPage
// Every plugin has nothing else to do in this state, go forward
$sLoginState = self::AdvanceLoginFSMState($sLoginState);
Session::Set('login_state', $sLoginState);
}
catch (Exception $e)
{
} catch (Exception $e) {
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['state' => $_SESSION['login_state']]));
IssueLog::Error($e->getTraceAsString());
static::ResetSession();
@@ -537,30 +491,23 @@ class LoginWebPage extends NiceWebPage
*/
public static function GetLoginPluginList($sInterface = 'iLoginFSMExtension', $bFilterWithMode = true)
{
$aAllPlugins = array();
$aAllPlugins = [];
if ($bFilterWithMode)
{
if ($bFilterWithMode) {
$sCurrentLoginMode = Session::Get('login_mode', '');
}
else
{
} else {
$sCurrentLoginMode = '';
}
/** @var iLoginExtension $oLoginExtensionInstance */
foreach (MetaModel::EnumPlugins($sInterface) as $oLoginExtensionInstance)
{
foreach (MetaModel::EnumPlugins($sInterface) as $oLoginExtensionInstance) {
$aLoginModes = $oLoginExtensionInstance->ListSupportedLoginModes();
$aLoginModes = (is_array($aLoginModes) ? $aLoginModes : array());
foreach ($aLoginModes as $sLoginMode)
{
$aLoginModes = (is_array($aLoginModes) ? $aLoginModes : []);
foreach ($aLoginModes as $sLoginMode) {
// Keep only the plugins for the current login mode + before + after
if (empty($sCurrentLoginMode) || ($sLoginMode == $sCurrentLoginMode) || ($sLoginMode == 'before') || ($sLoginMode == 'after'))
{
if (!isset($aAllPlugins[$sLoginMode]))
{
$aAllPlugins[$sLoginMode] = array();
if (empty($sCurrentLoginMode) || ($sLoginMode == $sCurrentLoginMode) || ($sLoginMode == 'before') || ($sLoginMode == 'after')) {
if (!isset($aAllPlugins[$sLoginMode])) {
$aAllPlugins[$sLoginMode] = [];
}
$aAllPlugins[$sLoginMode][] = $oLoginExtensionInstance;
break; // Stop here to avoid registering a plugin twice
@@ -569,12 +516,10 @@ class LoginWebPage extends NiceWebPage
}
// Order and filter by the config list of allowed types (allowed_login_types)
$aAllowedLoginModes = array_merge(array('before'), MetaModel::GetConfig()->GetAllowedLoginTypes(), array('after'));
$aPlugins = array();
foreach ($aAllowedLoginModes as $sAllowedMode)
{
if (isset($aAllPlugins[$sAllowedMode]))
{
$aAllowedLoginModes = array_merge(['before'], MetaModel::GetConfig()->GetAllowedLoginTypes(), ['after']);
$aPlugins = [];
foreach ($aAllowedLoginModes as $sAllowedMode) {
if (isset($aAllPlugins[$sAllowedMode])) {
$aPlugins = array_merge($aPlugins, $aAllPlugins[$sAllowedMode]);
}
}
@@ -590,8 +535,7 @@ class LoginWebPage extends NiceWebPage
*/
private static function AdvanceLoginFSMState($sLoginState)
{
switch ($sLoginState)
{
switch ($sLoginState) {
case self::LOGIN_STATE_START:
return self::LOGIN_STATE_MODE_DETECTION;
@@ -638,8 +582,7 @@ class LoginWebPage extends NiceWebPage
public static function CheckUser($sAuthUser, $sAuthPassword = '', $sAuthentication = 'external')
{
$oUser = self::FindUser($sAuthUser, true, ucfirst(strtolower($sAuthentication)));
if (is_null($oUser))
{
if (is_null($oUser)) {
return false;
}
@@ -668,8 +611,7 @@ class LoginWebPage extends NiceWebPage
{
// User is Ok, let's save it in the session and proceed with normal login
$bLoginSuccess = UserRights::Login($sAuthUser, $sAuthentication); // Login & set the user's language
if (!$bLoginSuccess)
{
if (!$bLoginSuccess) {
throw new Exception("Bad user");
}
if (MetaModel::GetConfig()->Get('log_usage')) {
@@ -696,12 +638,10 @@ class LoginWebPage extends NiceWebPage
*/
public static function CheckLoggedUser(&$iErrorCode)
{
if (Session::IsSet('auth_user'))
{
if (Session::IsSet('auth_user')) {
// Already authenticated
$bRet = UserRights::Login(Session::Get('auth_user')); // Login & set the user's language
if ($bRet)
{
if ($bRet) {
$iErrorCode = self::EXIT_CODE_OK;
return self::LOGIN_FSM_RETURN;
}
@@ -727,8 +667,7 @@ class LoginWebPage extends NiceWebPage
public static function SetLoginModeAndReload($sNewLoginMode)
{
if (Session::Get('login_mode') == $sNewLoginMode)
{
if (Session::Get('login_mode') == $sNewLoginMode) {
return;
}
Session::Set('login_mode', $sNewLoginMode);
@@ -738,8 +677,7 @@ class LoginWebPage extends NiceWebPage
public static function HTTPReload()
{
$sOriginURL = utils::GetCurrentAbsoluteUrl();
if (!utils::StartsWith($sOriginURL, utils::GetAbsoluteUrlAppRoot()))
{
if (!utils::StartsWith($sOriginURL, utils::GetAbsoluteUrlAppRoot())) {
// If the found URL does not start with the configured AppRoot URL
$sOriginURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php';
}
@@ -753,7 +691,6 @@ class LoginWebPage extends NiceWebPage
exit;
}
/**
* Provisioning API: Find a User
*
@@ -767,33 +704,28 @@ class LoginWebPage extends NiceWebPage
*/
public static function FindUser($sAuthUser, $bMustBeValid = true, $sType = 'External')
{
try
{
$aArgs = array('login' => $sAuthUser);
try {
$aArgs = ['login' => $sAuthUser];
$sUserClass = "User$sType";
$oSearch = DBObjectSearch::FromOQL("SELECT $sUserClass WHERE login = :login");
if ($bMustBeValid)
{
if ($bMustBeValid) {
$oSearch->AddCondition('status', 'enabled');
}
$oSet = new DBObjectSet($oSearch, array(), $aArgs);
if ($oSet->CountExceeds(0))
{
$oSet = new DBObjectSet($oSearch, [], $aArgs);
if ($oSet->CountExceeds(0)) {
/** @var User $oUser */
$oUser = $oSet->Fetch();
return $oUser;
}
}
catch (Exception $e)
{
} catch (Exception $e) {
IssueLog::Error($e->getMessage());
}
return null;
}
/**
* Provisioning API: Find a Person by email
* Provisioning API: Find a Person by email
*
* @api
*
@@ -805,19 +737,15 @@ class LoginWebPage extends NiceWebPage
{
/** @var \Person $oPerson */
$oPerson = null;
try
{
try {
$oSearch = new DBObjectSearch('Person');
$oSearch->AddCondition('email', $sEmail);
$oSet = new DBObjectSet($oSearch);
if ($oSet->CountExceeds(1))
{
if ($oSet->CountExceeds(1)) {
throw new Exception(Dict::S('UI:Login:Error:MultipleContactsHaveSameEmail'));
}
$oPerson = $oSet->Fetch();
}
catch (Exception $e)
{
} catch (Exception $e) {
IssueLog::Error($e->getMessage());
}
return $oPerson;
@@ -836,16 +764,14 @@ class LoginWebPage extends NiceWebPage
*
* @return \Person
*/
public static function ProvisionPerson($sFirstName, $sLastName, $sEmail, $sOrganization, $aAdditionalParams = array())
public static function ProvisionPerson($sFirstName, $sLastName, $sEmail, $sOrganization, $aAdditionalParams = [])
{
/** @var Person $oPerson */
$oPerson = null;
try
{
try {
CMDBObject::SetTrackOrigin('custom-extension');
$sInfo = 'External User provisioning';
if (Session::IsSet('login_mode'))
{
if (Session::IsSet('login_mode')) {
$sInfo .= " (".Session::Get('login_mode').")";
}
CMDBObject::SetTrackInfo($sInfo);
@@ -855,19 +781,15 @@ class LoginWebPage extends NiceWebPage
$oPerson->Set('name', $sLastName);
$oPerson->Set('email', $sEmail);
$oOrg = MetaModel::GetObjectByName('Organization', $sOrganization, false);
if (is_null($oOrg))
{
if (is_null($oOrg)) {
throw new Exception(Dict::S('UI:Login:Error:WrongOrganizationName'));
}
$oPerson->Set('org_id', $oOrg->GetKey());
foreach ($aAdditionalParams as $sAttCode => $sValue)
{
foreach ($aAdditionalParams as $sAttCode => $sValue) {
$oPerson->Set($sAttCode, $sValue);
}
$oPerson->DBInsert();
}
catch (Exception $e)
{
} catch (Exception $e) {
IssueLog::Error($e->getMessage());
}
return $oPerson;
@@ -886,27 +808,23 @@ class LoginWebPage extends NiceWebPage
*/
public static function ProvisionUser($sAuthUser, $oPerson, $aRequestedProfiles)
{
if (!MetaModel::IsValidClass('URP_Profiles'))
{
if (!MetaModel::IsValidClass('URP_Profiles')) {
IssueLog::Error("URP_Profiles is not a valid class. Automatic creation of Users is not supported in this context, sorry.");
return null;
}
/** @var UserExternal $oUser */
$oUser = null;
try
{
try {
CMDBObject::SetTrackOrigin('custom-extension');
$sInfo = 'External User provisioning';
if (Session::IsSet('login_mode'))
{
if (Session::IsSet('login_mode')) {
$sInfo .= " (".Session::Get('login_mode').")";
}
CMDBObject::SetTrackInfo($sInfo);
$oUser = MetaModel::GetObjectByName('UserExternal', $sAuthUser, false);
if (is_null($oUser))
{
if (is_null($oUser)) {
$oUser = MetaModel::NewObject('UserExternal');
$oUser->Set('login', $sAuthUser);
$oUser->Set('contactid', $oPerson->GetKey());
@@ -916,41 +834,33 @@ class LoginWebPage extends NiceWebPage
// read all the existing profiles
$oProfilesSearch = new DBObjectSearch('URP_Profiles');
$oProfilesSet = new DBObjectSet($oProfilesSearch);
$aAllProfiles = array();
while ($oProfile = $oProfilesSet->Fetch())
{
$aAllProfiles = [];
while ($oProfile = $oProfilesSet->Fetch()) {
$aAllProfiles[mb_strtolower($oProfile->GetName())] = $oProfile->GetKey();
}
$aProfiles = array();
foreach ($aRequestedProfiles as $sRequestedProfile)
{
$aProfiles = [];
foreach ($aRequestedProfiles as $sRequestedProfile) {
$sRequestedProfile = mb_strtolower($sRequestedProfile);
if (isset($aAllProfiles[$sRequestedProfile]))
{
if (isset($aAllProfiles[$sRequestedProfile])) {
$aProfiles[] = $aAllProfiles[$sRequestedProfile];
}
}
if (empty($aProfiles))
{
if (empty($aProfiles)) {
throw new Exception(Dict::S('UI:Login:Error:NoValidProfiles'));
}
// Now synchronize the profiles
$sOrigin = 'External User provisioning';
if (Session::IsSet('login_mode'))
{
if (Session::IsSet('login_mode')) {
$sOrigin .= " (".Session::Get('login_mode').")";
}
$aExistingProfiles = self::SynchronizeProfiles($oUser, $aProfiles, $sOrigin);
if ($oUser->IsModified())
{
if ($oUser->IsModified()) {
$oUser->DBWrite();
}
}
catch (Exception $e)
{
} catch (Exception $e) {
IssueLog::Error($e->getMessage());
}
@@ -961,26 +871,18 @@ class LoginWebPage extends NiceWebPage
* Overridable: depending on the user, head toward a dedicated portal
* @param string|null $sRequestedPortalId
* @param int $iOnExit How to complete the call: redirect or return a code
*/
*/
protected static function ChangeLocation($sRequestedPortalId = null, $iOnExit = self::EXIT_PROMPT)
{
$ret = call_user_func(array(self::$sHandlerClass, 'Dispatch'), $sRequestedPortalId);
if ($ret === true)
{
$ret = call_user_func([self::$sHandlerClass, 'Dispatch'], $sRequestedPortalId);
if ($ret === true) {
return self::EXIT_CODE_OK;
}
else if($ret === false)
{
} elseif ($ret === false) {
throw new Exception('Nowhere to go: Your combination of user Profiles denies you access to any '.ITOP_APPLICATION_SHORT.' portal. Please contact your administrator');
}
else
{
if ($iOnExit == self::EXIT_RETURN)
{
} else {
if ($iOnExit == self::EXIT_RETURN) {
return self::EXIT_CODE_PORTALUSERNOTAUTHORIZED;
}
else
{
} else {
// No rights to be here, redirect to the portal
header('Location: '.$ret);
die();
@@ -1002,7 +904,7 @@ class LoginWebPage extends NiceWebPage
* @return int|mixed|string
* @throws \Exception
*/
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
public static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
{
$sRequestedPortalId = $bIsAllowedToPortalUsers ? 'legacy_portal' : 'backoffice';
return self::DoLoginEx($sRequestedPortalId, $bMustBeAdmin, $iOnExit);
@@ -1019,23 +921,18 @@ class LoginWebPage extends NiceWebPage
* @return int|mixed|string
* @throws \Exception
*/
static function DoLoginEx($sRequestedPortalId = null, $bMustBeAdmin = false, $iOnExit = self::EXIT_PROMPT)
public static function DoLoginEx($sRequestedPortalId = null, $bMustBeAdmin = false, $iOnExit = self::EXIT_PROMPT)
{
$operation = utils::ReadParam('loginop', '');
$sMessage = self::HandleOperations($operation); // May exit directly
$iRet = self::Login($iOnExit);
if ($iRet == self::EXIT_CODE_OK)
{
if ($bMustBeAdmin && !UserRights::IsAdministrator())
{
if ($iOnExit == self::EXIT_RETURN)
{
if ($iRet == self::EXIT_CODE_OK) {
if ($bMustBeAdmin && !UserRights::IsAdministrator()) {
if ($iOnExit == self::EXIT_RETURN) {
return self::EXIT_CODE_MUSTBEADMIN;
}
else
{
} else {
require_once(APPROOT.'/setup/setuppage.class.inc.php');
$oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
@@ -1044,69 +941,52 @@ class LoginWebPage extends NiceWebPage
exit;
}
}
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
$iRet = call_user_func([self::$sHandlerClass, 'ChangeLocation'], $sRequestedPortalId, $iOnExit);
}
if ($iOnExit == self::EXIT_RETURN)
{
if ($iOnExit == self::EXIT_RETURN) {
return $iRet;
}
else
{
} else {
return $sMessage;
}
}
}
protected static function HandleOperations($operation)
{
$sMessage = ''; // most of the operations never return, but some can return a message to be displayed
if ($operation == 'logoff')
{
if ($operation == 'logoff') {
self::ResetSession();
$oPage = self::NewLoginWebPage();
$oPage->DisplayLoginForm(false /* not a failed attempt */);
$oPage->output();
exit;
}
else if ($operation == 'forgot_pwd')
{
} elseif ($operation == 'forgot_pwd') {
$oPage = self::NewLoginWebPage();
$oPage->DisplayForgotPwdForm();
$oPage->output();
exit;
}
else if ($operation == 'forgot_pwd_go')
{
} elseif ($operation == 'forgot_pwd_go') {
$oPage = self::NewLoginWebPage();
$oPage->ForgotPwdGo();
$oPage->output();
exit;
}
else if ($operation == 'reset_pwd')
{
} elseif ($operation == 'reset_pwd') {
$oPage = self::NewLoginWebPage();
$oPage->DisplayResetPwdForm();
$oPage->output();
exit;
}
else if ($operation == 'do_reset_pwd')
{
} elseif ($operation == 'do_reset_pwd') {
try {
$oPage = self::NewLoginWebPage();
$oPage->DoResetPassword();
}
catch (CoreCannotSaveObjectException $e)
{
} catch (CoreCannotSaveObjectException $e) {
$oPage = self::NewLoginWebPage();
$oPage->DisplayResetPwdForm($e->getIssue());
}
$oPage->output();
exit;
}
else if ($operation == 'change_pwd')
{
if (Session::IsSet('auth_user'))
{
} elseif ($operation == 'change_pwd') {
if (Session::IsSet('auth_user')) {
$sAuthUser = Session::Get('auth_user');
$sIssue = Session::Get('pwd_issue');
Session::Unset('pwd_issue');
@@ -1118,16 +998,13 @@ class LoginWebPage extends NiceWebPage
$oPage->output();
exit;
}
}
else if ($operation == 'check_pwd_policy')
{
} elseif ($operation == 'check_pwd_policy') {
$sAuthUser = Session::Get('auth_user');
UserRights::Login($sAuthUser); // Set the user's language
$aPwdMap = array();
$aPwdMap = [];
foreach (array('new_pwd', 'retype_new_pwd') as $postedPwd)
{
foreach (['new_pwd', 'retype_new_pwd'] as $postedPwd) {
$oUser = new UserLocal();
$oUser->ValidatePassword($_POST[$postedPwd]);
@@ -1137,27 +1014,21 @@ class LoginWebPage extends NiceWebPage
echo json_encode($aPwdMap);
die();
}
if ($operation == 'do_change_pwd')
{
if (Session::IsSet('auth_user'))
{
if ($operation == 'do_change_pwd') {
if (Session::IsSet('auth_user')) {
$sAuthUser = Session::Get('auth_user');
UserRights::Login($sAuthUser); // Set the user's language
$sOldPwd = utils::ReadPostedParam('old_pwd', '', 'raw_data');
$sNewPwd = utils::ReadPostedParam('new_pwd', '', 'raw_data');
try
{
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
{
try {
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd)))) {
$oPage = self::NewLoginWebPage();
$oPage->DisplayChangePwdForm(true); // old pwd was wrong
$oPage->output();
exit;
}
}
catch (CoreCannotSaveObjectException $e)
{
} catch (CoreCannotSaveObjectException $e) {
$oPage = self::NewLoginWebPage();
$oPage->DisplayChangePwdForm(true, $e->getIssue()); // password policy was not met.
$oPage->output();
@@ -1168,26 +1039,27 @@ class LoginWebPage extends NiceWebPage
}
return $sMessage;
}
protected static function Dispatch($sRequestedPortalId)
{
if ($sRequestedPortalId === null) return true; // allowed to any portal => return true
if ($sRequestedPortalId === null) {
return true;
} // allowed to any portal => return true
$aPortalsConf = PortalDispatcherData::GetData();
$aDispatchers = array();
foreach($aPortalsConf as $sPortalId => $aConf)
{
$aDispatchers = [];
foreach ($aPortalsConf as $sPortalId => $aConf) {
$sHandlerClass = $aConf['handler'];
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
}
if (array_key_exists($sRequestedPortalId, $aDispatchers) && $aDispatchers[$sRequestedPortalId]->IsUserAllowed())
{
if (array_key_exists($sRequestedPortalId, $aDispatchers) && $aDispatchers[$sRequestedPortalId]->IsUserAllowed()) {
return true;
}
foreach($aDispatchers as $sPortalId => $oDispatcher)
{
if ($oDispatcher->IsUserAllowed()) return $oDispatcher->GetUrl();
foreach ($aDispatchers as $sPortalId => $oDispatcher) {
if ($oDispatcher->IsUserAllowed()) {
return $oDispatcher->GetUrl();
}
}
return false; // nothing matched !!
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -17,7 +18,6 @@
* You should have received a copy of the GNU Affero General Public License
*/
//
// Maintenance message display functions
// Only included by approot.inc.php
@@ -33,21 +33,17 @@ function _MaintenanceSetupPageMessage($sTitle, $sMessage)
{
// Web Page
@include_once(APPROOT.'setup/setuppage.class.inc.php');
if (class_exists('SetupPage'))
{
if (class_exists('SetupPage')) {
$oP = new ErrorPage($sTitle);
$oP->p("<h2 class=\"center\">$sMessage</h2>");
$oP->add_ready_script(
<<<JS
<<<JS
// Reload in 30s to check if maintenance is over
setTimeout(function(){ window.location.reload(); }, 30000);
JS
);
$oP->output();
}
else
{
} else {
_MaintenanceTextMessage($sMessage);
}
}
@@ -78,14 +74,13 @@ function _MaintenanceHtmlMessage($sMessage)
*/
function _MaintenanceJsonMessage($sTitle, $sMessage)
{
if (class_exists('JsonPage'))
{
if (class_exists('JsonPage')) {
$oP = new JsonPage($sTitle);
$oP->add_header('Access-Control-Allow-Origin: *');
$aMessage = [
'code' => 100,
'message' =>$sMessage
'message' => $sMessage,
];
$oP->AddData($aMessage);

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -34,44 +35,44 @@ interface iNewsroomProvider
* return bool
*/
public function IsApplicable(User $oUser = null);
/**
* The human readable (localized) label for this provider
* @return string
*/
public function GetLabel();
/**
* The URL to query (from the browser, using jsonp) to fetch all unread messages
* @return string
*/
public function GetFetchURL();
/**
* The URL to navigate to in order to display all messages
* @return string
*/
public function GetViewAllURL();
/**
* The URL to query(from the browser, using jsonp) to mark all unread messages as read
* @return string
*/
public function GetMarkAllAsReadURL();
/**
* Return the URL to configure the preferences for this provider or null is there is nothing to configure
* @return string|null
*/
public function GetPreferencesUrl();
/**
* Return an array key => value to be replaced in URL of the messages
* Example: '%itop_root%' => utils::GetAbsoluteUrlAppRoot();
* @return string[]
*/
public function GetPlaceholders();
/**
* The duration between to refreshes of the cache (in seconds)
* @return int
@@ -90,19 +91,19 @@ abstract class NewsroomProviderBase implements iNewsroomProvider
* @var Config
*/
protected $oConfig;
public function __construct()
{
$this->oConfig = null;
}
/**
* {@inheritDoc}
* @see iNewsroomProvider::SetConfig()
*/
public function SetConfig(Config $oConfig)
{
$this->oConfig = $oConfig;
$this->oConfig = $oConfig;
}
/**
@@ -118,42 +119,42 @@ abstract class NewsroomProviderBase implements iNewsroomProvider
* {@inheritDoc}
* @see iNewsroomProvider::GetLabel()
*/
public abstract function GetLabel();
abstract public function GetLabel();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetFetchURL()
*/
public abstract function GetFetchURL();
abstract public function GetFetchURL();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetMarkAllURL()
*/
public abstract function GetMarkAllAsReadURL();
abstract public function GetMarkAllAsReadURL();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetViewAllURL()
*/
public abstract function GetViewAllURL();
abstract public function GetViewAllURL();
public function IsApplicable(User $oUser = null)
{
return false;
}
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetPlaceholders()
*/
public function GetPlaceholders()
{
return array(); // By default, empty set of placeholders
return []; // By default, empty set of placeholders
}
public function GetTTL()
{
return 10*60; // Refresh every 10 minutes
return 10 * 60; // Refresh every 10 minutes
}
}

View File

@@ -1,9 +1,10 @@
<?php
class PortalDispatcher
{
protected $sPortalid;
protected $aData;
public function __construct($sPortalId)
{
$this->sPortalid = $sPortalId;
@@ -20,53 +21,45 @@ class PortalDispatcher
{
$bRet = true;
$aProfiles = UserRights::ListProfiles($oUser);
foreach($this->aData['deny'] as $sDeniedProfile)
{
foreach ($this->aData['deny'] as $sDeniedProfile) {
// If one denied profile is present, it's enough => return false
if (in_array($sDeniedProfile, $aProfiles))
{
if (in_array($sDeniedProfile, $aProfiles)) {
return false;
}
}
// If there are some "allow" profiles, then by default the result is false
// since the user must have at least one of the profiles to be allowed
if (count($this->aData['allow']) > 0)
{
if (count($this->aData['allow']) > 0) {
$bRet = false;
}
foreach($this->aData['allow'] as $sAllowProfile)
{
foreach ($this->aData['allow'] as $sAllowProfile) {
// If one "allow" profile is present, it's enough => return true
if (in_array($sAllowProfile, $aProfiles))
{
if (in_array($sAllowProfile, $aProfiles)) {
return true;
}
}
return $bRet;
}
public function GetURL()
{
$aOverloads = MetaModel::GetConfig()->Get('portal_dispatch_urls');
if (array_key_exists($this->sPortalid, $aOverloads))
{
if (array_key_exists($this->sPortalid, $aOverloads)) {
$sRet = $aOverloads[$this->sPortalid];
}
else
{
} else {
$sRet = utils::GetAbsoluteUrlAppRoot().$this->aData['url'];
}
return $sRet;
}
public function GetLabel()
{
return Dict::S('portal:'.$this->sPortalid);
}
public function GetRank()
{
return $this->aData['rank'];
}
}
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -18,7 +19,6 @@
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
use Combodo\iTop\Application\WebPage\WebPage;
/**
* Persistent class Shortcut and derived
* Shortcuts of any kind
@@ -31,32 +31,32 @@ abstract class Shortcut extends DBObject implements iDisplay
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "gui,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_shortcut",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
);
];
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("context", array("allowed_values"=>null, "sql"=>"context", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", ["targetclass" => "User", "allowed_values" => null, "sql" => "user_id", "is_null_allowed" => true, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("name", ["allowed_values" => null, "sql" => "name", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeText("context", ["allowed_values" => null, "sql" => "context", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'context')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('name')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', ['name', 'context']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['name']); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
abstract public function RenderContent(WebPage $oPage, $aExtraParams = array());
abstract public function RenderContent(WebPage $oPage, $aExtraParams = []);
protected function OnInsert()
{
@@ -74,14 +74,14 @@ abstract class Shortcut extends DBObject implements iDisplay
$oForm->AddField($oField);
$oForm->Render($oPage);
$oPage->add('</div>');
$sDialogTitle = Dict::S('UI:ShortcutRenameDlg:Title');
$sOkButtonLabel = Dict::S('UI:Button:Ok');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$iShortcut = $this->GetKey();
$oPage->add_ready_script(
<<<EOF
<<<EOF
function ShortcutRenameOK()
{
var oForm = $(this).find('form');
@@ -137,13 +137,13 @@ EOF
return '';
}
function DisplayDetails(WebPage $oPage, $bEditMode = false)
public function DisplayDetails(WebPage $oPage, $bEditMode = false)
{
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = [])
{
return array();
return [];
}
// End of the minimal implementation of iDisplay
}
@@ -152,61 +152,56 @@ class ShortcutOQL extends Shortcut
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "gui,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_shortcut_oql",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("auto_reload", array("allowed_values"=>new ValueSetEnum('none,custom'), "sql"=>"auto_reload", "default_value"=>"none", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("auto_reload_sec", array("allowed_values"=>null, "sql"=>"auto_reload_sec", "default_value"=>60, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOQL("oql", ["allowed_values" => null, "sql" => "oql", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("auto_reload", ["allowed_values" => new ValueSetEnum('none,custom'), "sql" => "auto_reload", "default_value" => "none", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeInteger("auto_reload_sec", ["allowed_values" => null, "sql" => "auto_reload_sec", "default_value" => 60, "is_null_allowed" => false, "depends_on" => []]));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'context', 'oql')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('name')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', ['name', 'context', 'oql']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['name']); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
$oPage->set_title($this->Get('name'));
switch($this->Get('auto_reload'))
{
case 'custom':
$iRate = (int)$this->Get('auto_reload_sec');
if ($iRate > 0)
{
// Must a string otherwise it can be evaluated to 'true' and defaults to "standard" refresh rate!
$aExtraParams['auto_reload'] = (string)$iRate;
}
break;
switch ($this->Get('auto_reload')) {
case 'custom':
$iRate = (int)$this->Get('auto_reload_sec');
if ($iRate > 0) {
// Must a string otherwise it can be evaluated to 'true' and defaults to "standard" refresh rate!
$aExtraParams['auto_reload'] = (string)$iRate;
}
break;
default:
case 'none':
default:
case 'none':
}
$bSearchPane = true;
$bSearchOpen = true;
try
{
try {
OQLMenuNode::RenderOQLSearch($this->Get('oql'), $this->Get('name'), 'shortcut_'.$this->GetKey(), $bSearchPane, $bSearchOpen, $oPage, $aExtraParams, true);
}
catch (Exception $e)
{
} catch (Exception $e) {
throw new Exception("The OQL shortcut '".$this->Get('name')."' (id: ".$this->GetKey().") could not be displayed: ".$e->getMessage());
}
}
public function CloneTableSettings($sTableSettings)
@@ -226,17 +221,14 @@ class ShortcutOQL extends Shortcut
// Find a unique default name
// -> The class of the query + an index if necessary
if ($sOQL == null)
{
if ($sOQL == null) {
$sDefault = '';
}
else
{
} else {
$oBMSearch = new DBObjectSearch('Shortcut');
$oBMSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oBMSet = new DBObjectSet($oBMSearch);
$aNames = $oBMSet->GetColumnAsArray('name');
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSearch = DBObjectSearch::FromOQL($sOQL);
$sDefault = utils::MakeUniqueName($oSearch->GetClass(), $aNames);
}
@@ -276,18 +268,18 @@ class ShortcutOQL extends Shortcut
$oForm->Render($oPage);
$oPage->add('</div>');
$sDialogTitle = Dict::S('UI:ShortcutListDlg:Title');
$sOkButtonLabel = Dict::S('UI:Button:Ok');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sRateTitle = addslashes(Dict::Format('Class:ShortcutOQL/Attribute:auto_reload_sec/tip', MetaModel::GetConfig()->Get('min_reload_interval')));
$oPage->add_ready_script(
<<<JS
<<<JS
// Note: the title gets deleted by the validation mechanism
$("#attr_auto_reload_sec").attr('data-tooltip-content', '$sRateTitle');
CombodoTooltip.InitTooltipFromMarkup($("#attr_auto_reload_sec"));

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -23,7 +24,6 @@ require_once(APPROOT.'core/contexttag.class.inc.php');
require_once(APPROOT.'core/kpi.class.inc.php');
require_once(APPROOT.'setup/setuputils.class.inc.php');
/**
* File to include to initialize the datamodel in memory
*
@@ -36,12 +36,10 @@ ExecutionKPI::EnableMemory(1);
// This storage is freed on error (case of allowed memory exhausted)
$sReservedMemory = str_repeat('*', 1024 * 1024);
register_shutdown_function(function()
{
register_shutdown_function(function () {
global $sReservedMemory;
$sReservedMemory = null;
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
{
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR)) {
// Remove stack trace from MySQLException (since 2.7.2 see N°3174)
$sMessage = $err['message'];
if (strpos($sMessage, 'MySQLException') !== false) {
@@ -71,38 +69,30 @@ $oKPI->ComputeAndReport("Session Start");
$sSwitchEnv = utils::ReadParam('switch_env', null);
$bAllowCache = true;
if (($sSwitchEnv != null) && file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE) &&( Session::Get('itop_env') !== $sSwitchEnv))
{
if (($sSwitchEnv != null) && file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE) && (Session::Get('itop_env') !== $sSwitchEnv)) {
Session::Set('itop_env', $sSwitchEnv);
$sEnv = $sSwitchEnv;
$bAllowCache = false;
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
if (function_exists('opcache_reset'))
{
// Zend opcode cache
opcache_reset();
}
if (function_exists('apc_clear_cache'))
{
// APC(u) cache
apc_clear_cache();
}
$bAllowCache = false;
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
if (function_exists('opcache_reset')) {
// Zend opcode cache
opcache_reset();
}
if (function_exists('apc_clear_cache')) {
// APC(u) cache
apc_clear_cache();
}
// TODO: reset the credentials as well ??
}
else if (Session::IsSet('itop_env'))
{
} elseif (Session::IsSet('itop_env')) {
$sEnv = Session::Get('itop_env');
}
else
{
} else {
$sEnv = ITOP_DEFAULT_ENV;
Session::Set('itop_env', ITOP_DEFAULT_ENV);
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
try {
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
} catch (MySQLException $e) {
IssueLog::Debug($e->getMessage());
throw new MySQLException('Could not connect to the DB server', []);
}
catch (MySQLException $e) {
IssueLog::Debug($e->getMessage());
throw new MySQLException('Could not connect to the DB server', []);
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -25,7 +26,7 @@
*/
class ThemeHandler
{
const IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg'];
public const IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg'];
/** @var \CompileCSSService */
private static $oCompileCSSService;
@@ -50,7 +51,7 @@ class ThemeHandler
'imports' => [],
'stylesheets' => [
'main' => '../css/backoffice/main.scss',
]
],
],
];
}
@@ -63,8 +64,7 @@ class ThemeHandler
{
try {
$sThemeId = utils::GetConfig()->Get('backoffice_default_theme');
}
catch (CoreException $oCompileException) {
} catch (CoreException $oCompileException) {
// Fallback on our default theme in case the config. is not available yet
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$sThemeId = $aDefaultTheme['name'];
@@ -85,8 +85,7 @@ class ThemeHandler
if (true === utils::GetConfig()->Get('user_preferences.allow_backoffice_theme_override')) {
$sThemeId = appUserPreferences::GetPref('backoffice_theme', null);
}
}
catch (Exception $oException) {
} catch (Exception $oException) {
// Do nothing, already handled by $sThemeId null by default
}
@@ -201,8 +200,7 @@ class ThemeHandler
if (static::ShouldThemeSignatureCheckBeForced($sThemeId)) {
static::CompileTheme($sThemeId);
}
}
catch (CoreException $oCompileException) {
} catch (CoreException $oCompileException) {
// Fallback on our default theme (should always be compilable) in case the previous theme doesn't exists
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$sThemeId = $aDefaultTheme['name'];
@@ -258,13 +256,16 @@ class ThemeHandler
* @throws \CoreException
* @return boolean: indicate whether theme compilation occured
*/
public static function CompileTheme($sThemeId, $bSetup=false, $sSetupCompilationTimestamp="", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null) {
public static function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp = "", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null)
{
if ($sSetupCompilationTimestamp === "") {
$sSetupCompilationTimestamp = microtime(true);
}
$sSetupCompilationTimestampInSecunds = (strpos($sSetupCompilationTimestamp, '.') !== false) ? explode('.',
$sSetupCompilationTimestamp)[0] : $sSetupCompilationTimestamp;
$sSetupCompilationTimestampInSecunds = (strpos($sSetupCompilationTimestamp, '.') !== false) ? explode(
'.',
$sSetupCompilationTimestamp
)[0] : $sSetupCompilationTimestamp;
$sEnv = APPROOT.'env-'.utils::GetCurrentEnvironment().'/';
@@ -317,7 +318,7 @@ class ThemeHandler
}
}
foreach ($oFindStylesheetObject->GetStylesheetFileURIs() as $sStylesheet){
foreach ($oFindStylesheetObject->GetStylesheetFileURIs() as $sStylesheet) {
$sTmpThemeScssContent .= '@import "'.$sStylesheet.'";'."\n";
}
@@ -329,11 +330,9 @@ class ThemeHandler
$iStyleLastModified = $oFindStylesheetObject->GetLastModified();
$aIncludedImages=static::GetIncludedImages($aThemeParametersWithVersion, $oFindStylesheetObject->GetAllStylesheetPaths(), $sThemeId);
foreach ($aIncludedImages as $sImage)
{
if (is_file($sImage))
{
$aIncludedImages = static::GetIncludedImages($aThemeParametersWithVersion, $oFindStylesheetObject->GetAllStylesheetPaths(), $sThemeId);
foreach ($aIncludedImages as $sImage) {
if (is_file($sImage)) {
$iStylesheetLastModified = @filemtime($sImage);
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
}
@@ -342,34 +341,28 @@ class ThemeHandler
// Checking if our compiled css is outdated
$iFilemetime = @filemtime($sThemeCssPath);
$bFileExists = file_exists($sThemeCssPath);
$bVarSignatureChanged=false;
if ($bFileExists && $bSetup)
{
$bVarSignatureChanged = false;
if ($bFileExists && $bSetup) {
$sPrecompiledSignature = static::GetSignature($sThemeCssPath);
//check variable signature has changed which is independant from any file modification
if (!empty($sPrecompiledSignature)){
if (!empty($sPrecompiledSignature)) {
$sPreviousVariableSignature = static::GetVarSignature($sPrecompiledSignature);
$sCurrentVariableSignature = md5(json_encode($aThemeParameters['variables']));
$bVarSignatureChanged= ($sPreviousVariableSignature!==$sCurrentVariableSignature);
$bVarSignatureChanged = ($sPreviousVariableSignature !== $sCurrentVariableSignature);
}
}
if (!$bFileExists || $bVarSignatureChanged || (is_writable($sThemeFolderPath) && ($iFilemetime < $iStyleLastModified)))
{
if (!$bFileExists || $bVarSignatureChanged || (is_writable($sThemeFolderPath) && ($iFilemetime < $iStyleLastModified))) {
// Dates don't match. Second chance: check if the already compiled stylesheet exists and is consistent based on its signature
$sActualSignature = static::ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages);
if ($bFileExists && !$bSetup)
{
if ($bFileExists && !$bSetup) {
$sPrecompiledSignature = static::GetSignature($sThemeCssPath);
}
if (!empty($sPrecompiledSignature) && $sActualSignature == $sPrecompiledSignature)
{
if (!empty($sPrecompiledSignature) && $sActualSignature == $sPrecompiledSignature) {
touch($sThemeCssPath); // Stylesheet is up to date, mark it as more recent to speedup next time
}
else
{
} else {
// Alas, we really need to recompile
// Add the signature to the generated CSS file so that the file can be used as a precompiled stylesheet if needed
$sSignatureComment =
@@ -381,14 +374,16 @@ $sActualSignature
*/
CSS;
if (!static::$oCompileCSSService)
{
if (!static::$oCompileCSSService) {
static::$oCompileCSSService = new CompileCSSService();
}
//store it again to change $version with latest compiled time
SetupLog::Info("Compiling theme $sThemeId...");
$sTmpThemeCssContent = static::$oCompileCSSService->CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths,
$aThemeParametersWithVersion);
$sTmpThemeCssContent = static::$oCompileCSSService->CompileCSSFromSASS(
$sTmpThemeScssContent,
$aImportsPaths,
$aThemeParametersWithVersion
);
SetupLog::Info("$sThemeId theme compilation done.");
file_put_contents($sThemeFolderPath.'/theme-parameters.json', json_encode($aThemeParameters));
file_put_contents($sThemeCssPath, $sSignatureComment.$sTmpThemeCssContent);
@@ -413,13 +408,14 @@ CSS;
* @return string
* @throws \Exception
*/
public static function ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages) {
public static function ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages)
{
$aSignature = [
'variables' => md5(json_encode($aThemeParameters['variables'])),
'stylesheets' => [],
'variable_imports' => [],
'images' => [],
'utility_imports' => []
'utility_imports' => [],
];
$oFindStylesheetObject = new FindStylesheetObject();
@@ -461,8 +457,7 @@ CSS;
}
}
foreach ($aIncludedImages as $sImage)
{
foreach ($aIncludedImages as $sImage) {
if (is_file($sImage)) {
$sUri = str_replace(self::GetAppRootWithSlashes(), '', $sImage);
$aSignature['images'][$sUri] = md5_file($sImage);
@@ -489,7 +484,7 @@ CSS;
$aCompleteUrls = [];
$aToCompleteUrls = [];
$aMissingVariables = [];
$aFoundVariables = ['version'=>''];
$aFoundVariables = ['version' => ''];
$aMap = [
'aCompleteUrls' => $aCompleteUrls,
'aToCompleteUrls' => $aToCompleteUrls,
@@ -497,14 +492,11 @@ CSS;
'aFoundVariables' => $aFoundVariables,
];
foreach ($aStylesheetFiles as $sStylesheetFile)
{
foreach ($aStylesheetFiles as $sStylesheetFile) {
$aRes = static::GetAllUrlFromScss($aThemeParametersVariables, $sStylesheetFile);
/** @var array $aVal */
foreach($aMap as $key => $aVal)
{
if (array_key_exists($key, $aMap))
{
foreach ($aMap as $key => $aVal) {
if (array_key_exists($key, $aMap)) {
$aMap[$key] = array_merge($aVal, $aRes[$key]);
}
}
@@ -513,17 +505,14 @@ CSS;
$aMap = static::ResolveUncompleteUrlsFromScss($aMap, $aThemeParametersVariables, $aStylesheetFiles);
$aImages = [];
foreach ($aMap ['aCompleteUrls'] as $sUri => $sUrl)
{
foreach ($aMap ['aCompleteUrls'] as $sUri => $sUrl) {
$sImg = $sUrl;
if (preg_match("/(.*)\?/", $sUrl, $aMatches))
{
$sImg=$aMatches[1];
if (preg_match("/(.*)\?/", $sUrl, $aMatches)) {
$sImg = $aMatches[1];
}
if (static::HasImageExtension($sImg)
&& ! array_key_exists($sImg, $aImages))
{
&& ! array_key_exists($sImg, $aImages)) {
$sFilePath = utils::RealPath($sImg, APPROOT);
if ($sFilePath !== false) {
$sFilePathWithSlashes = str_replace('\\', '/', $sFilePath);
@@ -554,8 +543,8 @@ CSS;
*/
public static function CanonicalizePath($path)
{
$path = explode('/', str_replace('//','/', $path));
$stack = array();
$path = explode('/', str_replace('//', '/', $path));
$stack = [];
foreach ($path as $seg) {
if ($seg == '..') {
// Ignore this segment, remove last segment from stack
@@ -585,25 +574,23 @@ CSS;
*/
public static function ResolveUncompleteUrlsFromScss($aMap, $aThemeParametersVariables, $aStylesheetFile)
{
$sContent="";
foreach ($aStylesheetFile as $sStylesheetFile)
{
if (is_file($sStylesheetFile))
{
$sContent .= '\n' . file_get_contents($sStylesheetFile);
$sContent = "";
foreach ($aStylesheetFile as $sStylesheetFile) {
if (is_file($sStylesheetFile)) {
$sContent .= '\n'.file_get_contents($sStylesheetFile);
}
}
$aMissingVariables=$aMap['aMissingVariables'];
$aFoundVariables=$aMap['aFoundVariables'];
$aToCompleteUrls=$aMap['aToCompleteUrls'];
$aCompleteUrls=$aMap['aCompleteUrls'];
$aMissingVariables = $aMap['aMissingVariables'];
$aFoundVariables = $aMap['aFoundVariables'];
$aToCompleteUrls = $aMap['aToCompleteUrls'];
$aCompleteUrls = $aMap['aCompleteUrls'];
list($aMissingVariables, $aFoundVariables) = static::FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent, true);
list($aToCompleteUrls, $aCompleteUrls) = static::ResolveUrls($aFoundVariables, $aToCompleteUrls, $aCompleteUrls);
$aMap['aMissingVariables']=$aMissingVariables;
$aMap['aFoundVariables']=$aFoundVariables;
$aMap['aToCompleteUrls']=$aToCompleteUrls;
$aMap['aCompleteUrls']=$aCompleteUrls;
$aMap['aMissingVariables'] = $aMissingVariables;
$aMap['aFoundVariables'] = $aFoundVariables;
$aMap['aToCompleteUrls'] = $aToCompleteUrls;
$aMap['aCompleteUrls'] = $aCompleteUrls;
return $aMap;
}
@@ -619,43 +606,29 @@ CSS;
*
* @return array
*/
public static function FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent, $bForceEmptyValueWhenNotFound=false)
public static function FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent, $bForceEmptyValueWhenNotFound = false)
{
$aNewMissingVars = [];
if (!empty($aMissingVariables))
{
foreach ($aMissingVariables as $var)
{
if (array_key_exists($var, $aThemeParametersVariables))
{
if (!empty($aMissingVariables)) {
foreach ($aMissingVariables as $var) {
if (array_key_exists($var, $aThemeParametersVariables)) {
$aFoundVariables[$var] = $aThemeParametersVariables[$var];
}
else
{
if (preg_match_all("/\\\$$var\s*:\s*[\"']{0,1}(.*)[\"']{0,1};/", $sContent, $aValues))
{
} else {
if (preg_match_all("/\\\$$var\s*:\s*[\"']{0,1}(.*)[\"']{0,1};/", $sContent, $aValues)) {
$sValue = $aValues[1][0];
if (preg_match_all("/([^!]+)!/", $sValue, $aSubValues))
{
if (preg_match_all("/([^!]+)!/", $sValue, $aSubValues)) {
$sValue = trim($aSubValues[1][0], ' "\'');
}
if (strpos($sValue, '$') === false)
{
if (strpos($sValue, '$') === false) {
$aFoundVariables[$var] = $sValue;
}
else{
} else {
$aNewMissingVars[] = $var;
}
}
else
{
if ($bForceEmptyValueWhenNotFound)
{
} else {
if ($bForceEmptyValueWhenNotFound) {
$aFoundVariables[$var] = '';
}
else
{
} else {
$aNewMissingVars[] = $var;
}
}
@@ -676,32 +649,23 @@ CSS;
*/
public static function ResolveUrls($aFoundVariables, array $aToCompleteUrls, array $aCompleteUrls)
{
if (!empty($aFoundVariables))
{
if (!empty($aFoundVariables)) {
$aFoundVariablesWithEmptyValue = [];
foreach ($aFoundVariables as $aFoundVariable => $sValue)
{
foreach ($aFoundVariables as $aFoundVariable => $sValue) {
$aFoundVariablesWithEmptyValue[$aFoundVariable] = '';
}
foreach ($aToCompleteUrls as $sUrlTemplate)
{
foreach ($aToCompleteUrls as $sUrlTemplate) {
unset($aToCompleteUrls[$sUrlTemplate]);
$sResolvedUrl = static::ResolveUrl($sUrlTemplate, $aFoundVariables);
if ($sResolvedUrl == false)
{
if ($sResolvedUrl == false) {
$aToCompleteUrls[$sUrlTemplate] = $sUrlTemplate;
}
else
{
} else {
$sUri = static::ResolveUrl($sUrlTemplate, $aFoundVariablesWithEmptyValue);
$aExplodedUri = explode('?', $sUri);
if (empty($aExplodedUri))
{
if (empty($aExplodedUri)) {
$aCompleteUrls[$sUri] = $sResolvedUrl;
}
else
{
} else {
$aCompleteUrls[$aExplodedUri[0]] = $sResolvedUrl;
}
}
@@ -726,41 +690,31 @@ CSS;
$aMissingVariables = [];
$aFoundVariables = [];
if (is_file($sStylesheetFile))
{
if (is_file($sStylesheetFile)) {
$sContent = file_get_contents($sStylesheetFile);
if (preg_match_all("/url\s*\((.*)\)/", $sContent, $aMatches))
{
foreach ($aMatches[1] as $path)
{
if (preg_match_all("/url\s*\((.*)\)/", $sContent, $aMatches)) {
foreach ($aMatches[1] as $path) {
$iRemainingClosingParenthesisPos = strpos($path, ')');
if ($iRemainingClosingParenthesisPos !== false){
if ($iRemainingClosingParenthesisPos !== false) {
$path = substr($path, 0, $iRemainingClosingParenthesisPos);
}
if (!array_key_exists($path, $aCompleteUrls)
&& !array_key_exists($path, $aToCompleteUrls))
{
if (preg_match_all("/\\$([\w\-_]+)/", $path, $aCurrentVars))
{
&& !array_key_exists($path, $aToCompleteUrls)) {
if (preg_match_all("/\\$([\w\-_]+)/", $path, $aCurrentVars)) {
/** @var string $aCurrentVars */
foreach ($aCurrentVars[1] as $var)
{
if (!array_key_exists($var, $aMissingVariables))
{
foreach ($aCurrentVars[1] as $var) {
if (!array_key_exists($var, $aMissingVariables)) {
$aMissingVariables[$var] = $var;
}
}
$aToCompleteUrls[$path] = $path;
}
else
{
} else {
$aCompleteUrls[$path] = trim($path, "\"'");
}
}
}
}
if (!empty($aMissingVariables))
{
if (!empty($aMissingVariables)) {
list($aMissingVariables, $aFoundVariables) = static::FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent);
list($aToCompleteUrls, $aCompleteUrls) = static::ResolveUrls($aFoundVariables, $aToCompleteUrls, $aCompleteUrls);
}
@@ -770,7 +724,7 @@ CSS;
'aCompleteUrls' => $aCompleteUrls,
'aToCompleteUrls' => $aToCompleteUrls,
'aMissingVariables' => $aMissingVariables,
'aFoundVariables' => $aFoundVariables
'aFoundVariables' => $aFoundVariables,
];
}
@@ -784,23 +738,21 @@ CSS;
*/
public static function ResolveUrl($sUrlTemplate, $aFoundVariables)
{
$aPattern= [];
$aReplacement= [];
foreach ($aFoundVariables as $aFoundVariable => $aFoundVariableValue)
{
$aPattern = [];
$aReplacement = [];
foreach ($aFoundVariables as $aFoundVariable => $aFoundVariableValue) {
//XX + $key + YY
$aPattern[]="/['\"]\s*\+\s*\\\$" . $aFoundVariable . "[\s\+]+\s*['\"]/";
$aReplacement[]=$aFoundVariableValue;
$aPattern[] = "/['\"]\s*\+\s*\\\$".$aFoundVariable."[\s\+]+\s*['\"]/";
$aReplacement[] = $aFoundVariableValue;
//$key + YY
$aPattern[]="/\\\$" . $aFoundVariable. "[\s\+]+\s*['\"]/";
$aReplacement[]=$aFoundVariableValue;
$aPattern[] = "/\\\$".$aFoundVariable."[\s\+]+\s*['\"]/";
$aReplacement[] = $aFoundVariableValue;
//XX + $key
$aPattern[]="/['\"]\s*[\+\s]+\\\$" . $aFoundVariable . "$/";
$aReplacement[]=$aFoundVariableValue;
$aPattern[] = "/['\"]\s*[\+\s]+\\\$".$aFoundVariable."$/";
$aReplacement[] = $aFoundVariableValue;
}
$sResolvedUrl=preg_replace($aPattern, $aReplacement, $sUrlTemplate);
if (strpos($sResolvedUrl, "+")!==false)
{
$sResolvedUrl = preg_replace($aPattern, $aReplacement, $sUrlTemplate);
if (strpos($sResolvedUrl, "+") !== false) {
return false;
}
return trim($sResolvedUrl, "\"'");
@@ -814,17 +766,14 @@ CSS;
*/
private static function HasImageExtension($path)
{
foreach (static::IMAGE_EXTENSIONS as $sExt)
{
if (endsWith($path, $sExt))
{
foreach (static::IMAGE_EXTENSIONS as $sExt) {
if (endsWith($path, $sExt)) {
return true;
}
}
return false;
}
/**
* @since 3.0.0 N°2982
* Extract the signature for a generated CSS file.
@@ -843,16 +792,13 @@ CSS;
$iCount = 0;
$sPreviousLine = '';
$hFile = @fopen($sFilepath, "r");
if ($hFile !== false)
{
if ($hFile !== false) {
$sLine = '';
do
{
do {
$iCount++;
$sPreviousLine = $sLine;
$sLine = rtrim(fgets($hFile)); // Remove the trailing \n
}
while (($sLine !== false) && ($sLine != '=== SIGNATURE END ===') && ($iCount <= 100));
} while (($sLine !== false) && ($sLine != '=== SIGNATURE END ===') && ($iCount <= 100));
fclose($hFile);
}
return $sPreviousLine;
@@ -867,8 +813,7 @@ CSS;
public static function GetVarSignature($JsonSignature)
{
$aJsonArray = json_decode($JsonSignature, true);
if (array_key_exists('variables', $aJsonArray))
{
if (array_key_exists('variables', $aJsonArray)) {
return $aJsonArray['variables'];
}
return false;
@@ -892,23 +837,22 @@ CSS;
$oFindStylesheetObject->ResetLastStyleSheet();
}
foreach($aImportsPaths as $sPath)
{
foreach ($aImportsPaths as $sPath) {
$sAlterableFileURI = $sFileURI;
$sFilePath = $sPath.'/'.$sAlterableFileURI;
$sImportedFile = realpath($sFilePath);
if ($sImportedFile === false){
if ($sImportedFile === false) {
// Handle shortcut syntax : @import "typo" ;
// file matched: typo.scss
$sFilePath2 = "$sFilePath.scss";
$sImportedFile = realpath($sFilePath2);
if ($sImportedFile){
if ($sImportedFile) {
self::FindStylesheetFile("$sAlterableFileURI.scss", [ $sPath ], $oFindStylesheetObject, $bImports);
$sImportedFile = false;
}
}
if ($sImportedFile === false){
if ($sImportedFile === false) {
// Handle shortcut syntax : @import "typo" ;
// file matched: _typo.scss
$sShortCut = substr($sFilePath, strrpos($sFilePath, '/') + 1);
@@ -918,11 +862,10 @@ CSS;
}
if ((file_exists($sImportedFile))
&& (!$oFindStylesheetObject->AlreadyFetched($sImportedFile)))
{
if ($bImports){
&& (!$oFindStylesheetObject->AlreadyFetched($sImportedFile))) {
if ($bImports) {
$oFindStylesheetObject->AddImport($sAlterableFileURI, $sImportedFile);
}else{
} else {
$oFindStylesheetObject->AddStylesheet($sAlterableFileURI, $sImportedFile);
}
$oFindStylesheetObject->UpdateLastModified($sImportedFile);
@@ -930,8 +873,8 @@ CSS;
//Regexp matching on all included scss files : @import 'XXX.scss';
$sDirUri = dirname($sAlterableFileURI);
preg_match_all('/@import \s*[\"\']([^\"\']*)\s*[\"\']\s*;/', file_get_contents($sImportedFile), $aMatches);
if ( (is_array($aMatches)) && (count($aMatches)!==0) ){
foreach ($aMatches[1] as $sImportedFile){
if ((is_array($aMatches)) && (count($aMatches) !== 0)) {
foreach ($aMatches[1] as $sImportedFile) {
self::FindStylesheetFile("$sDirUri/$sImportedFile", [ $sPath ], $oFindStylesheetObject, true);
}
}
@@ -952,8 +895,7 @@ CSS;
{
$iPos = strrpos($sSubject, $sSearch);
if($iPos !== false)
{
if ($iPos !== false) {
$sSubject = substr_replace($sSubject, $sReplace, $iPos, strlen($sSearch));
}
@@ -982,18 +924,14 @@ CSS;
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp, $aImportsPaths)
{
$aThemeParametersVariable = [];
if (array_key_exists('variables', $aThemeParameters))
{
if (is_array($aThemeParameters['variables']))
{
if (array_key_exists('variables', $aThemeParameters)) {
if (is_array($aThemeParameters['variables'])) {
$aThemeParametersVariable = array_merge([], $aThemeParameters['variables']);
}
}
if (array_key_exists('variable_imports', $aThemeParameters))
{
if (is_array($aThemeParameters['variable_imports']))
{
if (array_key_exists('variable_imports', $aThemeParameters)) {
if (is_array($aThemeParameters['variable_imports'])) {
$aThemeParametersVariable = array_merge($aThemeParametersVariable, static::GetVariablesFromFile($aThemeParameters['variable_imports'], $aImportsPaths));
}
}
@@ -1009,11 +947,11 @@ CSS;
* @return array
* @since 3.0.0 N°3593
*/
public static function GetVariablesFromFile($aVariableFiles, $aImportsPaths){
public static function GetVariablesFromFile($aVariableFiles, $aImportsPaths)
{
$aVariablesResults = [];
foreach ($aVariableFiles as $sVariableFile)
{
foreach($aImportsPaths as $sPath) {
foreach ($aVariableFiles as $sVariableFile) {
foreach ($aImportsPaths as $sPath) {
$sFilePath = $sPath.'/'.$sVariableFile;
$sImportedFile = realpath($sFilePath);
if ($sImportedFile !== false) {
@@ -1029,9 +967,8 @@ CSS;
}
}
}
array_map( function($sVariableValue) { return ltrim($sVariableValue); }, $aVariablesResults );
array_map(function ($sVariableValue) { return ltrim($sVariableValue); }, $aVariablesResults);
return $aVariablesResults;
}
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -29,7 +30,8 @@ class ThemeHandlerService
{
}
public function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp = "", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null){
public function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp = "", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null)
{
return ThemeHandler::CompileTheme($sThemeId, $bSetup, $sSetupCompilationTimestamp, $aThemeParameters, $aImportsPaths, $sWorkingPath);
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -54,8 +55,8 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
*/
class UIExtKeyWidget
{
const ENUM_OUTPUT_FORMAT_CSV = 'csv';
const ENUM_OUTPUT_FORMAT_JSON = 'json';
public const ENUM_OUTPUT_FORMAT_CSV = 'csv';
public const ENUM_OUTPUT_FORMAT_JSON = 'json';
protected $iId;
protected $sTargetClass;
@@ -87,10 +88,20 @@ class UIExtKeyWidget
* @since 2.7.7 3.0.1 3.1.0 N°3129 Add default value for $aArgs for PHP 8.0 compat
*/
public static function DisplayFromAttCode(
$oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '',
$aArgs = [], $bSearchMode = false, &$sInputType = ''
)
{
$oPage,
$sAttCode,
$sClass,
$sTitle,
$oAllowedValues,
$value,
$iInputId,
$bMandatory,
$sFieldName = '',
$sFormPrefix = '',
$aArgs = [],
$bSearchMode = false,
&$sInputType = ''
) {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sTargetClass = $oAttDef->GetTargetClass();
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
@@ -102,8 +113,7 @@ class UIExtKeyWidget
}
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode);
if (!$bSearchMode) {
switch ($sDisplayStyle)
{
switch ($sDisplayStyle) {
case 'radio':
case 'radio_horizontal':
case 'radio_vertical':
@@ -114,12 +124,38 @@ class UIExtKeyWidget
case 'select':
case 'list':
default:
return $oWidget->DisplaySelect($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value,
$bMandatory, $sFieldName, $sFormPrefix, $aArgs, $sInputType);
return $oWidget->DisplaySelect(
$oPage,
$iMaxComboLength,
$bAllowTargetCreation,
$sTitle,
$oAllowedValues,
$value,
$bMandatory,
$sFieldName,
$sFormPrefix,
$aArgs,
$sInputType
);
}
} else {
return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId,
$bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle, true, $sInputType);
return $oWidget->Display(
$oPage,
$iMaxComboLength,
$bAllowTargetCreation,
$sTitle,
$oAllowedValues,
$value,
$iInputId,
$bMandatory,
$sFieldName,
$sFormPrefix,
$aArgs,
null,
$sDisplayStyle,
true,
$sInputType
);
}
}
@@ -158,7 +194,7 @@ class UIExtKeyWidget
* @since 3.0.0 N°2508 - Include Obsolescence icon within list and autocomplete
* @since 3.0.0 N°3750 new $sInputType parameter
*/
public function DisplaySelect(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), &$sInputType = '')
public function DisplaySelect(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = [], &$sInputType = '')
{
$sTitle = addslashes($sTitle);
$oPage->LinkScriptFromAppRoot('js/extkeywidget.js');
@@ -194,7 +230,7 @@ class UIExtKeyWidget
$bIsAutocomplete = $oAllowedValues->CountExceeds($iMaxComboLength);
$sWrapperCssClass = $bIsAutocomplete ? 'ibo-input-select-autocomplete-wrapper' : 'ibo-input-select-wrapper';
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons $sWrapperCssClass\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\" data-accessibility-selectize-label=\"$sTitle\">";
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
if (!$bIsAutocomplete) {
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
@@ -281,21 +317,17 @@ EOF
$oPage->add_ready_script("$('#$this->iId').one('validate', function() { $(this).trigger('change'); } );");
}
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\">";
}
else
{
} else {
// Too many choices, use an autocomplete
// Check that the given value is allowed
$oSearch = $oAllowedValues->GetFilter();
$oSearch->AddCondition('id', $value);
$oSet = new DBObjectSet($oSearch);
if ($oSet->Count() == 0)
{
if ($oSet->Count() == 0) {
$value = null;
}
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
{
if (is_null($value) || ($value == 0)) { // Null values are displayed as ''
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
} else {
$sDisplayValue = $this->GetObjectName($value);
@@ -376,36 +408,30 @@ JS
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey\">";
if (is_null($oAllowedValues))
{
if (is_null($oAllowedValues)) {
throw new Exception('Implementation: null value for allowed values definition');
}
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
if (!$oAllowedValues->CountExceeds($iMaxComboLength))
{
if (!$oAllowedValues->CountExceeds($iMaxComboLength)) {
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
$sValidationField = null;
$bVertical = ($sDisplayStyle != 'radio_horizontal');
$bExtensions = false;
$oAllowedValues->Rewind();
$aAllowedValues = array();
while($oObj = $oAllowedValues->Fetch())
{
$aAllowedValues = [];
while ($oObj = $oAllowedValues->Fetch()) {
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
}
$sHTMLValue .= $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", false /* $bMandatory will be placed manually */, $bVertical, $sValidationField);
$aEventsList[] ='change';
}
else
{
$aEventsList[] = 'change';
} else {
$sHTMLValue .= "unable to display. Too much values";
}
$sHTMLValue .= '<div class="ibo-input-select--action-buttons">';
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
{
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false) {
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"><i class=\"fas fa-sitemap\"></i></div></span>";
$oPage->add_ready_script(
<<<JS
@@ -416,8 +442,7 @@ JS
JS
);
}
if ($bCreate && $bExtensions)
{
if ($bCreate && $bExtensions) {
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"><i class=\"fas fa-plus\"></i></div></span>";
@@ -471,7 +496,7 @@ JS
*
* @since 3.0.0 N°3750 new $sInputType parameter
*/
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true, &$sInputType = '')
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = [], $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true, &$sInputType = '')
{
if (!is_null($bSearchMode)) {
$this->bSearchMode = $bSearchMode;
@@ -521,7 +546,7 @@ JS
$bVertical = ($sDisplayStyle != 'radio_horizontal');
$bExtensions = false;
$oAllowedValues->Rewind();
$aAllowedValues = array();
$aAllowedValues = [];
while ($oObj = $oAllowedValues->Fetch()) {
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
}
@@ -568,22 +593,22 @@ EOF
}
$sHTMLValue .= "<option value=\"$key\" $sSelected>$display_value</option>\n";
}
$sHTMLValue .= "</select>\n";
$sHTMLValue .= "</div>\n";
$sHTMLValue .= "</select>\n";
$sHTMLValue .= "</div>\n";
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_RAW;
if (($this->bSearchMode) && $bSearchMultiple) {
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_MULTIPLE_CHOICES;
$aOptions = array(
'header' => true,
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
'selectedList' => 1,
);
$sJSOptions = json_encode($aOptions);
$oPage->add_ready_script("$('.multiselect').multiselect($sJSOptions);");
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_RAW;
if (($this->bSearchMode) && $bSearchMultiple) {
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_MULTIPLE_CHOICES;
$aOptions = [
'header' => true,
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
'selectedList' => 1,
];
$sJSOptions = json_encode($aOptions);
$oPage->add_ready_script("$('.multiselect').multiselect($sJSOptions);");
}
$oPage->add_ready_script(
<<<EOF
@@ -606,8 +631,7 @@ EOF
$value = null;
}
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
{
if (is_null($value) || ($value == 0)) { // Null values are displayed as ''
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
} else {
$sDisplayValue = $this->GetObjectName($value);
@@ -673,20 +697,22 @@ JS
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
/** @var \DBObject $oCurrObject */
$aArgs = $oCurrObject->ToArgsForQuery();
$aParams = array('query_params' => $aArgs);
$aParams = ['query_params' => $aArgs];
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aArgs);
$oFilter = $oSet->GetFilter();
} else if (!empty($this->sFilter)) {
$aParams = array();
} elseif (!empty($this->sFilter)) {
$aParams = [];
$oFilter = DBObjectSearch::FromOQL($this->sFilter);
} else {
$aParams = array();
$aParams = [];
$oFilter = new DBObjectSearch($this->sTargetClass);
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, 'dtc_'.$this->iId,
array(
$oPage->AddUiBlock($oBlock->GetDisplay(
$oPage,
'dtc_'.$this->iId,
[
'menu' => false,
'currentId' => $this->iId,
'table_id' => "dr_{$this->iId}",
@@ -694,12 +720,13 @@ JS
'selection_mode' => true,
'selection_type' => 'single',
'cssCount' => '#count_'.$this->iId.'_results',
)
]
));
$sCancel = Dict::S('UI:Button:Cancel');
$sOK = Dict::S('UI:Button:Ok');
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
$oPage->add(<<<HTML
$oPage->add(
<<<HTML
<form id="fr_{$this->iId}" OnSubmit="return oACWidget_{$this->iId}.DoOk();">
<div id="dr_{$this->iId}">
<div><p>{$sEmptyList}</p></div>
@@ -711,7 +738,8 @@ HTML
);
$sDialogTitleSanitized = addslashes(utils::HtmlToText($sTitle));
$oPage->add_ready_script(<<<JS
$oPage->add_ready_script(
<<<JS
$('#ac_dlg_{$this->iId}').dialog({
width: $(window).width()*0.8,
height: $(window).height()*0.8,
@@ -751,14 +779,12 @@ JS
*/
public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null)
{
if (is_null($sFilter))
{
if (is_null($sFilter)) {
throw new Exception('Implementation: null value for allowed values definition');
}
$oFilter = DBObjectSearch::FromOQL($sFilter);
if (strlen($sRemoteClass) > 0)
{
if (strlen($sRemoteClass) > 0) {
$oFilter->ChangeClass($sRemoteClass);
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
@@ -766,26 +792,26 @@ JS
// Current extkey value, so we can display event if it is not available anymore (eg. archived).
$iCurrentExtKeyId = (is_null($oObj)) ? 0 : $oObj->Get($this->sAttCode);
$oBlock = new DisplayBlock($oFilter, 'list_search', false, array('query_params' => array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId)));
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId.'_results', 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
$oBlock = new DisplayBlock($oFilter, 'list_search', false, ['query_params' => ['this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId]]);
$oBlock->Display($oP, $this->iId.'_results', ['this' => $oObj, 'cssCount' => '#count_'.$this->iId.'_results', 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode]); // Don't display the 'Actions' menu on the results
}
/**
* Search for objects to be selected
*
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sFilter The OQL expression used to define/limit limit the scope of possible values
* @param DBObject $oObj The current object for the OQL context
* @param string $sContains The text of the autocomplete to filter the results
* @param string $sOutputFormat
* @param null $sOperation for the values @see ValueSetObjects->LoadValues() not used since 3.0.0
*
* @throws CoreException
* @throws OQLException
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $oObj for PHP 8.0 compatibility
*/
public function AutoComplete(WebPage $oP, $sFilter, $oObj, $sContains, $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV, $sOperation = null )
/**
* Search for objects to be selected
*
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sFilter The OQL expression used to define/limit limit the scope of possible values
* @param DBObject $oObj The current object for the OQL context
* @param string $sContains The text of the autocomplete to filter the results
* @param string $sOutputFormat
* @param null $sOperation for the values @see ValueSetObjects->LoadValues() not used since 3.0.0
*
* @throws CoreException
* @throws OQLException
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $oObj for PHP 8.0 compatibility
*/
public function AutoComplete(WebPage $oP, $sFilter, $oObj, $sContains, $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV, $sOperation = null)
{
if (is_null($sFilter)) {
throw new Exception('Implementation: null value for allowed values definition');
@@ -799,38 +825,32 @@ JS
$oValuesSet->SetSort(false);
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oValuesSet->SetLimit($iMax);
$aValuesStartWith = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
$aValuesStartWith = $oValuesSet->GetValuesForAutocomplete(['this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId], $sContains, 'start_with');
asort($aValuesStartWith);
$aValues = $aValuesStartWith;
if (sizeof($aValues) < $iMax) {
$aValuesContains = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
$aValuesContains = $oValuesSet->GetValuesForAutocomplete(['this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId], $sContains, 'contains');
asort($aValuesContains);
$iSize = sizeof($aValues);
foreach ($aValuesContains as $sKey => $sFriendlyName)
{
if (!isset($aValues[$sKey]))
{
foreach ($aValuesContains as $sKey => $sFriendlyName) {
if (!isset($aValues[$sKey])) {
$aValues[$sKey] = $sFriendlyName;
if (++$iSize >= $iMax)
{
if (++$iSize >= $iMax) {
break;
}
}
}
}
elseif (!in_array($sContains, $aValues))
{
$aValuesEquals = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals');
} elseif (!in_array($sContains, $aValues)) {
$aValuesEquals = $oValuesSet->GetValuesForAutocomplete(['this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId], $sContains, 'equals');
// Note: Here we cannot use array_merge as it would reindex the numeric keys starting from 0 when keys are actually the objects ID.
// As a workaround we use array_replace as it does preserve numeric keys. It's ok if some values from $aValuesEquals are replaced with values from $aValues as they contain the same data.
$aValues = array_replace($aValuesEquals, $aValues);
}
switch($sOutputFormat)
{
switch ($sOutputFormat) {
case static::ENUM_OUTPUT_FORMAT_JSON:
$aJsonMap = array();
$aJsonMap = [];
foreach ($aValues as $sKey => $aValue) {
$aElt = ['value' => $sKey, 'label' => utils::EscapeHtml($aValue['label']), 'obsolescence_flag' => $aValue['obsolescence_flag']];
if ($aValue['additional_field'] != '') {
@@ -851,8 +871,7 @@ JS
break;
case static::ENUM_OUTPUT_FORMAT_CSV:
foreach($aValues as $sKey => $aValue)
{
foreach ($aValues as $sKey => $aValue) {
$oP->add(trim($aValue['label'])."\t".$sKey."\n");
}
break;
@@ -874,7 +893,7 @@ JS
*/
public function GetObjectName($iObjId, $sFormAttCode = null)
{
$aModifierProps = array();
$aModifierProps = [];
$aModifierProps['UserRightsGetSelectFilter']['bSearchMode'] = $this->bSearchMode;
$oObj = MetaModel::GetObject($this->sTargetClass, $iObjId, false, false, $aModifierProps);
@@ -884,9 +903,7 @@ JS
} else {
return $oObj->Get($sFormAttCode);
}
}
else
{
} else {
return '';
}
}
@@ -902,30 +919,29 @@ JS
*/
public function GetClassSelectionForm(WebPage $oPage)
{
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
// and that the current user is allowed to create objects of this class
$aSubClasses = MetaModel::EnumChildClasses($this->sTargetClass, ENUM_CHILD_CLASSES_ALL);
$aPossibleClasses = array();
foreach($aSubClasses as $sCandidateClass)
{
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
{
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
}
}
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
// and that the current user is allowed to create objects of this class
$aSubClasses = MetaModel::EnumChildClasses($this->sTargetClass, ENUM_CHILD_CLASSES_ALL);
$aPossibleClasses = [];
foreach ($aSubClasses as $sCandidateClass) {
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES)) {
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
}
}
$sClassLabel = MetaModel::GetName($this->sTargetClass);
$sDialogTitle = Dict::Format('UI:CreationTitle_Class', $sClassLabel);;
$oBlock = UIContentBlockUIBlockFactory::MakeStandard('ac_create_'.$this->iId,['ibo-is-visible']);
$sDialogTitle = Dict::Format('UI:CreationTitle_Class', $sClassLabel);
;
$oBlock = UIContentBlockUIBlockFactory::MakeStandard('ac_create_'.$this->iId, ['ibo-is-visible']);
$oPage->AddSubBlock($oBlock);
$oClassForm = FormUIBlockFactory::MakeStandard();
$oBlock->AddSubBlock($oClassForm);
$oClassForm->AddSubBlock(cmdbAbstractObject::DisplayBlockSelectClassToCreate( $sClassLabel, $this->sTargetClass, $aPossibleClasses));
$oClassForm->AddSubBlock(cmdbAbstractObject::DisplayBlockSelectClassToCreate($sClassLabel, $this->sTargetClass, $aPossibleClasses));
$sDialogTitleEscaped = addslashes($sDialogTitle);
$oPage->add_ready_script("$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitleEscaped'});\n");
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').removeAttr('onsubmit');");
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').find('select').attr('id', 'ac_create_{$this->iId}_select');");
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').on('submit.uilinksWizard', oACWidget_{$this->iId}.DoSelectObjectClass);");
$oPage->add_ready_script("$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitleEscaped'});\n");
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').removeAttr('onsubmit');");
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').find('select').attr('id', 'ac_create_{$this->iId}_select');");
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').on('submit.uilinksWizard', oACWidget_{$this->iId}.DoSelectObjectClass);");
}
/**
@@ -941,16 +957,14 @@ JS
$oAppContext->InitObjectFromContext($oNewObj);
$oNewObj->PrefillForm('creation_from_extkey', $aPrefillFormParam);
// 2nd set the default values from the constraint on the external key... if any
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
{
if (($oCurrObject != null) && ($this->sAttCode != '')) {
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
$aParams = array('this' => $oCurrObject);
$aParams = ['this' => $oCurrObject];
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aParams);
$aConsts = $oSet->ListConstantFields();
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
if (isset($aConsts[$sClassAlias]))
{
foreach($aConsts[$sClassAlias] as $sAttCode => $value) {
if (isset($aConsts[$sClassAlias])) {
foreach ($aConsts[$sClassAlias] as $sAttCode => $value) {
$oNewObj->Set($sAttCode, $value);
}
}
@@ -962,32 +976,35 @@ JS
$sClassLabel = MetaModel::GetName($this->sTargetClass);
$sHeaderTitleEscaped = utils::EscapeHtml(Dict::Format('UI:CreationTitle_Class', $sClassLabel));
$oPage->add(<<<HTML
$oPage->add(
<<<HTML
<div id="ac_create_{$this->iId}" title="{$sHeaderTitleEscaped}">
<div id="dcr_{$this->iId}">
HTML
);
$aFormExtraParams = array(
$aFormExtraParams = [
'formPrefix' => $this->iId,
'noRelations' => true,
);
];
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
FormHelper::DisableAttributeBlobInputs($this->sTargetClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oNewObj)){
if (FormHelper::HasMandatoryAttributeBlobInputs($oNewObj)) {
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), $aFormExtraParams);
$oPage->add(<<<HTML
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, [], $aFormExtraParams);
$oPage->add(
<<<HTML
</div>
</div>
HTML
);
$oPage->add_ready_script(<<<JS
$oPage->add_ready_script(
<<<JS
$('#ac_create_{$this->iId}').dialog({ width: $(window).width() * 0.6, height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true});
$('#dcr_{$this->iId} form').removeAttr('onsubmit');
$('#dcr_{$this->iId} form').find('button[type="submit"]').on('click', oACWidget_{$this->iId}.DoCreateObject);
@@ -1003,14 +1020,13 @@ JS
$sDialogTitle = addslashes(Dict::Format('UI:HierarchyOf_Class', MetaModel::GetName($this->sTargetClass)));
$oPage->add('<div id="dlg_tree_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div style="margin-bottom:5px;" id="tree_'.$this->iId.'">');
$oPage->add('<table style="width:100%"><tr><td>');
if (is_null($sFilter))
{
if (is_null($sFilter)) {
throw new Exception('Implementation: null value for allowed values definition');
}
$oFilter = DBObjectSearch::FromOQL($sFilter);
$oFilter = DBObjectSearch::FromOQL($sFilter);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj, 'current_extkey_id' => $currValue));
$oSet = new DBObjectSet($oFilter, [], ['this' => $oObj, 'current_extkey_id' => $currValue]);
$oSet->SetShowObsoleteData(utils::ShowObsoleteData());
@@ -1020,17 +1036,17 @@ JS
$oPage->add('</td></tr></table>');
$oPage->add('</div>');
if ($bHasChildLeafs)
{
if ($bHasChildLeafs) {
$oPage->add('<span class="treecontrol ibo-button-group" id="treecontrolid"><a class="ibo-button ibo-is-regular ibo-is-neutral" href="?#">'.Dict::S("UI:Treeview:CollapseAll").'</a><a class="ibo-button ibo-is-regular ibo-is-neutral" href="?#">'.Dict::S("UI:Treeview:ExpandAll").'</a></span>');
}
$oPage->add('</div></div>');
$sOkButtonLabel = Dict::S('UI:Button:Ok');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$oPage->add_ready_script("\$('#tree_$this->iId ul').treeview({ control: '#treecontrolid', persist: 'false'});\n");
$oPage->add_ready_script(<<<JS
$oPage->add_ready_script(
<<<JS
$('#dlg_tree_$this->iId').dialog({
width: 'auto',
height: 'auto',
@@ -1069,8 +1085,7 @@ JS
*/
public function DoCreateObject($oPage)
{
try
{
try {
$oObj = MetaModel::NewObject($this->sTargetClass);
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
if (count($aErrors) == 0) {
@@ -1088,13 +1103,12 @@ JS
]);
$oObj->DBInsertNoReload();
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
return ['name' => $oObj->GetName(), 'id' => $oObj->GetKey()];
} else {
return array('error' => implode(' ', $aErrors), 'id' => 0);
return ['error' => implode(' ', $aErrors), 'id' => 0];
}
}
catch (Exception $e) {
return array('error' => $e->getMessage(), 'id' => 0);
} catch (Exception $e) {
return ['error' => $e->getMessage(), 'id' => 0];
}
}
@@ -1110,32 +1124,27 @@ JS
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
function DumpTree($oP, $oSet, $sParentAttCode, $currValue)
public function DumpTree($oP, $oSet, $sParentAttCode, $currValue)
{
$aTree = array();
$aNodes = array();
while($oObj = $oSet->Fetch())
{
$aTree = [];
$aNodes = [];
while ($oObj = $oSet->Fetch()) {
$iParentId = $oObj->Get($sParentAttCode);
if (!isset($aTree[$iParentId]))
{
$aTree[$iParentId] = array();
if (!isset($aTree[$iParentId])) {
$aTree[$iParentId] = [];
}
$aTree[$iParentId][$oObj->GetKey()] = $oObj->GetName();
$aNodes[$oObj->GetKey()] = $oObj;
}
$aParents = array_keys($aTree);
$aRoots = array();
foreach($aParents as $id)
{
if (!array_key_exists($id, $aNodes))
{
$aRoots = [];
foreach ($aParents as $id) {
if (!array_key_exists($id, $aNodes)) {
$aRoots[] = $id;
}
}
foreach($aRoots as $iRootId)
{
foreach ($aRoots as $iRootId) {
$this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
}
@@ -1143,28 +1152,22 @@ JS
return !$bHasOnlyRootNodes;
}
function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
public function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
{
$bSelect = true;
$bMultiple = false;
$sSelect = '';
if (array_key_exists($iRootId, $aTree))
{
if (array_key_exists($iRootId, $aTree)) {
$aSortedRoots = $aTree[$iRootId];
asort($aSortedRoots);
$oP->add("<ul>\n");
$fUniqueId = microtime(true);
foreach($aSortedRoots as $id => $sName)
{
if ($bSelect)
{
foreach ($aSortedRoots as $id => $sName) {
if ($bSelect) {
$sChecked = ($aNodes[$id]->GetKey() == $currValue) ? 'checked' : '';
if ($bMultiple)
{
if ($bMultiple) {
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="checkbox" value="'.$aNodes[$id]->GetKey().'" name="selectObject[]" '.$sChecked.'>&nbsp;';
}
else
{
} else {
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'>&nbsp;';
}
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -30,7 +31,7 @@ use Combodo\iTop\Renderer\BlockRenderer;
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UIHTMLEditorWidget
class UIHTMLEditorWidget
{
protected $m_iId;
protected $m_oAttDef;
@@ -41,7 +42,7 @@ class UIHTMLEditorWidget
protected $m_sValidationField;
protected $m_sValue;
protected $m_sMandatory;
public function __construct($iInputId, $oAttDef, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $sValue, $sMandatory)
{
$this->m_iId = $iInputId;
@@ -54,7 +55,7 @@ class UIHTMLEditorWidget
$this->m_sMandatory = $sMandatory;
$this->m_sFieldPrefix = $sFieldPrefix;
}
/**
* Get the HTML fragment corresponding to the HTML editor widget
*
@@ -63,7 +64,7 @@ class UIHTMLEditorWidget
*
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, array $aArgs = array()) : string
public function Display(WebPage $oPage, array $aArgs = []): string
{
$iId = $this->m_iId;
$sCode = $this->m_sAttCode.$this->m_sNameSuffix;

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -38,39 +39,35 @@ class UILinksWidgetDirect
$this->sAttCode = $sAttCode;
$this->sInputid = $sInputId;
$this->sNameSuffix = $sNameSuffix;
$this->aZlist = array();
$this->aZlist = [];
$this->sLinkedClass = '';
// Compute the list of attributes visible from the given objet:
// All the attributes from the "list" Zlist of the Link class except
// the ExternalKey that points to the current object and its related external fields
$oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$this->sLinkedClass = $oLinksetDef->GetLinkedClass();
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
switch($oLinksetDef->GetEditMode())
{
switch ($oLinksetDef->GetEditMode()) {
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'details'));
break;
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'details'));
break;
default:
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'list'));
array_unshift($aZList, 'friendlyname');
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'list'));
array_unshift($aZList, 'friendlyname');
}
foreach($aZList as $sLinkedAttCode)
{
if ($sLinkedAttCode != $sExtKeyToMe)
{
foreach ($aZList as $sLinkedAttCode) {
if ($sLinkedAttCode != $sExtKeyToMe) {
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
if ((!$oAttDef->IsExternalField() || ($oAttDef->GetKeyAttCode() != $sExtKeyToMe)) &&
(!$oAttDef->IsLinkSet()) )
{
(!$oAttDef->IsLinkSet())) {
$this->aZlist[] = $sLinkedAttCode;
}
}
}
}
/**
@@ -101,21 +98,17 @@ class UILinksWidgetDirect
$sRealClass = '';
//$oPage->add('<div class="wizContainer" style="vertical-align:top;"><div>');
$aSubClasses = MetaModel::EnumChildClasses($this->sLinkedClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
$aPossibleClasses = array();
foreach($aSubClasses as $sCandidateClass)
{
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
{
if ($sCandidateClass == $sProposedRealClass)
{
$aPossibleClasses = [];
foreach ($aSubClasses as $sCandidateClass) {
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES)) {
if ($sCandidateClass == $sProposedRealClass) {
$sRealClass = $sProposedRealClass;
}
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
}
}
// Only one of the subclasses can be instantiated...
if (count($aPossibleClasses) == 1)
{
if (count($aPossibleClasses) == 1) {
$aKeys = array_keys($aPossibleClasses);
$sRealClass = $aKeys[0];
}
@@ -123,11 +116,11 @@ class UILinksWidgetDirect
if ($sRealClass != '') {
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$aFieldsFlags = array($sExtKeyToMe => OPT_ATT_HIDDEN);
$aFieldsFlags = [$sExtKeyToMe => OPT_ATT_HIDDEN];
$oObj = DBObject::MakeDefaultInstance($sRealClass);
$aPrefillParam = array('source_obj' => $oSourceObj);
$aPrefillParam = ['source_obj' => $oSourceObj];
$oObj->PrefillForm('creation_from_editinplace', $aPrefillParam);
$aFormExtraParams = array(
$aFormExtraParams = [
'formPrefix' => $this->sInputid,
'noRelations' => true,
'fieldsFlags' => $aFieldsFlags,
@@ -140,25 +133,22 @@ class UILinksWidgetDirect
JS
,
],
);
];
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
FormHelper::DisableAttributeBlobInputs($sRealClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oObj)){
if (FormHelper::HasMandatoryAttributeBlobInputs($oObj)) {
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);
}
else
{
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, [], $aFormExtraParams);
} else {
$sClassLabel = MetaModel::GetName($this->sLinkedClass);
$oPage->add('<p>'.Dict::Format('UI:SelectTheTypeOf_Class_ToCreate', $sClassLabel));
$oPage->add('<nobr><select name="class">');
asort($aPossibleClasses);
foreach($aPossibleClasses as $sClassName => $sClassLabel)
{
foreach ($aPossibleClasses as $sClassName => $sClassLabel) {
$oPage->add("<option value=\"$sClassName\">$sClassLabel</option>");
}
$oPage->add('</select>');
@@ -178,7 +168,7 @@ JS
* @throws \MissingQueryArgument
* @throws \OQLException
*/
public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked, $aPrefillFormParam = array())
public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked, $aPrefillFormParam = [])
{
//$oPage->add("<div class=\"wizContainer\" style=\"vertical-align:top;\">\n");
@@ -199,8 +189,7 @@ JS
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinkSetDef->GetValuesDef();
if ($valuesDef === null)
{
if ($valuesDef === null) {
$oFilter = new DBObjectSearch($this->sLinkedClass);
} else {
if (!$valuesDef instanceof ValueSetObjects) {
@@ -218,8 +207,10 @@ JS
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
}
$oBlock = new DisplayBlock($oFilter, 'search', false);
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}",
array(
$oPage->AddUiBlock($oBlock->GetDisplay(
$oPage,
"SearchFormToAdd_{$this->sInputid}",
[
'result_list_outer_selector' => "SearchResultsToAdd_{$this->sInputid}",
'table_id' => "add_{$this->sInputid}",
'table_inner_id' => "ResultsToAdd_{$this->sInputid}",
@@ -227,13 +218,14 @@ JS
'cssCount' => "#count_{$this->sInputid}",
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sHiddenCriteria,
)
]
));
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sCancel = Dict::S('UI:Button:Cancel');
$sAdd = Dict::S('UI:Button:Add');
$oPage->add(<<<HTML
$oPage->add(
<<<HTML
<form id="ObjectsAddForm_{$this->sInputid}">
<div id="SearchResultsToAdd_{$this->sInputid}">
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
@@ -256,52 +248,43 @@ HTML
* @throws \CoreException
* @throws \OQLException
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null, $aPrefillFormParam = array())
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = [], $oCurrentObj = null, $aPrefillFormParam = [])
{
if ($sRemoteClass == '')
{
if ($sRemoteClass == '') {
$sRemoteClass = $this->sLinkedClass;
}
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinkSetDef->GetValuesDef();
if ($valuesDef === null)
{
if ($valuesDef === null) {
$oFilter = new DBObjectSearch($sRemoteClass);
}
else
{
if (!$valuesDef instanceof ValueSetObjects)
{
} else {
if (!$valuesDef instanceof ValueSetObjects) {
throw new Exception('Error: only ValueSetObjects are supported for "allowed_values" in AttributeLinkedSet ('.$this->sClass.'/'.$this->sAttCode.').');
}
$oFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression());
}
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($sRemoteClass, $this->sClass))
{
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($sRemoteClass, $this->sClass)) {
// Prevent linking to self if the linked object is of the same family
// and already present in the database
if (!$oCurrentObj->IsNew())
{
if (!$oCurrentObj->IsNew()) {
$oFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
}
}
if ($oCurrentObj != null)
{
if ($oCurrentObj != null) {
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
$oFilter->SetInternalParams($aArgs);
$aPrefillFormParam['filter'] = $oFilter;
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
}
if (count($aAlreadyLinked) > 0)
{
if (count($aAlreadyLinked) > 0) {
$oFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", array('menu' => false, 'cssCount'=> '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid)); // Don't display the 'Actions' menu on the results
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", ['menu' => false, 'cssCount' => '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid]); // Don't display the 'Actions' menu on the results
}
/**
@@ -311,29 +294,28 @@ HTML
public function DoAddObjects(WebPage $oP, $oFullSetFilter)
{
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
foreach($aLinkedObjectIds as $iObjectId)
{
foreach ($aLinkedObjectIds as $iObjectId) {
$oLinkObj = MetaModel::GetObject($this->sLinkedClass, $iObjectId);
$oP->add($this->GetObjectRow($oP, $oLinkObj, $oLinkObj->GetKey()));
}
}
public function GetObjectModificationDlg()
{
}
public function GetTableConfig()
{
$aAttribs = array();
$aAttribs['form::select'] = array(
$aAttribs = [];
$aAttribs['form::select'] = [
'label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);oWidget".$this->sInputid.".directlinks('instance')._onSelectChange();\" class=\"checkAll\"></input>",
'description' => Dict::S('UI:SelectAllToggle+'),
);
];
foreach ($this->aZlist as $sLinkedAttCode) {
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
$aAttribs[$sLinkedAttCode] = array('label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint());
$aAttribs[$sLinkedAttCode] = ['label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint()];
}
return $aAttribs;
@@ -348,13 +330,12 @@ HTML
*/
public function GetRow($oPage, $sRealClass, $aValues, $iTempId)
{
if ($sRealClass == '')
{
if ($sRealClass == '') {
$sRealClass = $this->sLinkedClass;
}
$oLinkObj = new $sRealClass();
$oLinkObj->UpdateObjectFromPostedForm($this->sInputid);
return $this->GetObjectRow($oPage, $oLinkObj, $iTempId);
}
@@ -367,13 +348,12 @@ HTML
protected function GetObjectRow($oPage, $oLinkObj, $iTempId)
{
$aAttribs = $this->GetTableConfig();
$aRow = array();
$aRow = [];
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.($iTempId).'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
foreach ($this->aZlist as $sLinkedAttCode) {
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
return $oPage->GetTableRow($aRow, $aAttribs);
return $oPage->GetTableRow($aRow, $aAttribs);
}
/**
@@ -386,23 +366,21 @@ HTML
*/
public function GetFormRow($oPage, $sRealClass, $aValues, $iTempId)
{
if ($sRealClass == '')
{
if ($sRealClass == '') {
$sRealClass = $this->sLinkedClass;
}
$oLinkObj = new $sRealClass();
$oLinkObj->UpdateObjectFromPostedForm($this->sInputid);
$aAttribs = $this->GetTableConfig();
$aRow = array();
$aRow = [];
$aRow[] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.($iTempId).'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
foreach ($this->aZlist as $sLinkedAttCode) {
$aRow[] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
return $aRow;
}
/**
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
* @param DBObject $oSourceObj
@@ -413,27 +391,23 @@ HTML
$oAppContext = new ApplicationContext();
$sSrcClass = get_class($oSourceObj);
$sDestClass = $oSearch->GetClass();
foreach($oAppContext->GetNames() as $key)
{
foreach ($oAppContext->GetNames() as $key) {
// Find the value of the object corresponding to each 'context' parameter
$aCallSpec = array($sSrcClass, 'MapContextParam');
$aCallSpec = [$sSrcClass, 'MapContextParam'];
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode)) {
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class
// and sets its value as the default value for the search condition
$aCallSpec = array($sDestClass, 'MapContextParam');
$aCallSpec = [$sDestClass, 'MapContextParam'];
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue)) {
@@ -443,7 +417,6 @@ HTML
}
}
public function GetClass(): string
{
return $this->sClass;

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -54,7 +55,7 @@ class UILinksWidget
$this->m_sNameSuffix = $sNameSuffix;
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
$this->m_aEditableFields = array();
$this->m_aEditableFields = [];
/** @var AttributeLinkedSetIndirect $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
@@ -67,34 +68,33 @@ class UILinksWidget
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
$this->m_aEditableFields = array();
$this->m_aTableConfig = array();
$this->m_aTableConfig['form::checkbox'] = array(
$this->m_aEditableFields = [];
$this->m_aTableConfig = [];
$this->m_aTableConfig['form::checkbox'] = [
'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_sInputId.".OnSelectChange();\">",
'description' => Dict::S('UI:SelectAllToggle+'),
);
];
$aLnkAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($sClass, $sAttCode);
foreach ($aLnkAttDefsToDisplay as $oLnkAttDef)
{
foreach ($aLnkAttDefsToDisplay as $oLnkAttDef) {
$sLnkAttCode = $oLnkAttDef->GetCode();
$this->m_aEditableFields[] = $sLnkAttCode;
$this->m_aTableConfig[$sLnkAttCode] = array('label' => $oLnkAttDef->GetLabel(), 'description' => $oLnkAttDef->GetDescription());
$this->m_aTableConfig[$sLnkAttCode] = ['label' => $oLnkAttDef->GetLabel(), 'description' => $oLnkAttDef->GetDescription()];
}
$this->m_aTableConfig['static::key'] = array(
$this->m_aTableConfig['static::key'] = [
'label' => MetaModel::GetName($this->m_sRemoteClass),
'description' => MetaModel::GetClassDescription($this->m_sRemoteClass),
);
];
$this->m_aEditableFields[] = $this->m_sExtKeyToRemote;
$aRemoteAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($this->m_sRemoteClass);
foreach ($aRemoteAttDefsToDisplay as $oRemoteAttDef) {
$sRemoteAttCode = $oRemoteAttDef->GetCode();
$this->m_aTableConfig['static::'.$sRemoteAttCode] = array(
$this->m_aTableConfig['static::'.$sRemoteAttCode] = [
'label' => $oRemoteAttDef->GetLabel(),
'description' => $oRemoteAttDef->GetDescription(),
);
];
}
}
@@ -105,7 +105,6 @@ class UILinksWidget
return ($bSafe) ? utils::GetSafeId($sFieldId) : $sFieldId;
}
/**
* Display the table with the form for editing all the links at once
*
@@ -119,7 +118,6 @@ class UILinksWidget
return DataTableUIBlockFactory::MakeForForm("{$this->m_sAttCode}{$this->m_sNameSuffix}", $aConfig, $aData);
}
/**
* Get the HTML fragment corresponding to the linkset editing widget
*
@@ -157,7 +155,7 @@ class UILinksWidget
* @throws DictExceptionMissingString
* @throws Exception
*/
public function GetObjectPickerDialog($oPage, $oCurrentObj, $sJson, $aAlreadyLinkedIds = array(), $aPrefillFormParam = array())
public function GetObjectPickerDialog($oPage, $oCurrentObj, $sJson, $aAlreadyLinkedIds = [], $aPrefillFormParam = [])
{
$oAlreadyLinkedFilter = new DBObjectSearch($this->m_sRemoteClass);
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0) {
@@ -183,7 +181,9 @@ class UILinksWidget
$sLinkedSetId = $oBlock->oUILinksWidget->GetLinkedSetId();
$oDisplayBlock = new DisplayBlock($oFilter, 'search', false);
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay($oPage, "SearchFormToAdd_{$sLinkedSetId}",
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay(
$oPage,
"SearchFormToAdd_{$sLinkedSetId}",
[
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$sLinkedSetId}",
@@ -195,7 +195,8 @@ class UILinksWidget
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sAlreadyLinkedExpression,
'submit_on_load' => false,
]));
]
));
$oBlock->AddForm();
}
@@ -212,25 +213,21 @@ class UILinksWidget
* @throws \CoreException
* @throws \Exception
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = array(), $oCurrentObj = null)
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = [], $oCurrentObj = null)
{
if ($sRemoteClass != '')
{
if ($sRemoteClass != '') {
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
$oFilter = new DBObjectSearch($sRemoteClass);
}
else
{
} else {
// No remote class specified use the one defined in the linkedset
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
}
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
{
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0) {
$oFilter->AddCondition('id', $aAlreadyLinkedIds, 'NOTIN');
}
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'table_id' => 'add_'.$this->m_sAttCode)); // Don't display the 'Actions' menu on the results
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", ['menu' => false, 'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'table_id' => 'add_'.$this->m_sAttCode]); // Don't display the 'Actions' menu on the results
}
/**
@@ -251,7 +248,7 @@ class UILinksWidget
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
if (is_object($oLinkedObj)) {
$oBlock = new BlockIndirectLinkSetEditTable($this);
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, [], $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$oRow = new FormTableRow("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aRow, -$iAdditionId);
$oP->AddUiBlock($oRow);
$iAdditionId++;
@@ -280,7 +277,7 @@ class UILinksWidget
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
if (is_object($oLinkedObj)) {
$oBlock = new BlockIndirectLinkSetEditTable($this);
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId, false /* Default value */, $bAllowRemoteExtKeyEdit); // Not yet created link get negative Ids
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, [], $oCurrentObj, $iAdditionId, false /* Default value */, $bAllowRemoteExtKeyEdit); // Not yet created link get negative Ids
$aData = [];
foreach ($aRow as $item) {
$aData[] = $item;
@@ -307,37 +304,30 @@ class UILinksWidget
$oAppContext = new ApplicationContext();
$sSrcClass = get_class($oSourceObj);
$sDestClass = $oSearch->GetClass();
foreach($oAppContext->GetNames() as $key)
{
foreach ($oAppContext->GetNames() as $key) {
// Find the value of the object corresponding to each 'context' parameter
$aCallSpec = array($sSrcClass, 'MapContextParam');
$aCallSpec = [$sSrcClass, 'MapContextParam'];
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode)) {
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class
// and sets its value as the default value for the search condition
$aCallSpec = array($sDestClass, 'MapContextParam');
$aCallSpec = [$sDestClass, 'MapContextParam'];
$sAttCode = '';
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
{
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue)) {
// Add Hierarchical condition if hierarchical key
$oAttDef = MetaModel::GetAttributeDef($sDestClass, $sAttCode);
if (isset($oAttDef) && ($oAttDef->IsExternalKey()))
{
try
{
if (isset($oAttDef) && ($oAttDef->IsExternalKey())) {
try {
/** @var AttributeExternalKey $oAttDef */
$sTargetClass = $oAttDef->GetTargetClass();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
@@ -348,8 +338,7 @@ class UILinksWidget
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
}
}
catch (Exception $e) {
} catch (Exception $e) {
}
} else {
$oSearch->AddCondition($sAttCode, $defaultValue);

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -28,13 +29,13 @@ use Combodo\iTop\Application\WebPage\WebPage;
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UIPasswordWidget
class UIPasswordWidget
{
protected static $iWidgetIndex = 0;
protected $sAttCode;
protected $sNameSuffix;
protected $iId;
public function __construct($sAttCode, $iInputId, $sNameSuffix = '')
{
self::$iWidgetIndex++;
@@ -42,14 +43,14 @@ class UIPasswordWidget
$this->sNameSuffix = $sNameSuffix;
$this->iId = $iInputId;
}
/**
* Get the HTML fragment corresponding to the linkset editing widget
* @param WebPage $oP The web page used for all the output
* @param Hash $aArgs Extra context arguments
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, $aArgs = array())
public function Display(WebPage $oPage, $aArgs = [])
{
$oPage->add_dict_entry('UI:Component:Input:Password:DoesNotMatch');
@@ -94,4 +95,3 @@ class UIPasswordWidget
return $sHtmlValue;
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
/**
*
* Copyright (C) 2010-2024 Combodo SAS
@@ -20,7 +21,6 @@
*
*/
use Combodo\iTop\Application\WebPage\WebPage;
require_once(APPROOT.'/application/displayblock.class.inc.php');
@@ -46,8 +46,10 @@ class UISearchFormForeignKeys
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
$oBlock = new DisplayBlock($oFilter, 'search', false);
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_iInputId}",
array(
$oPage->AddUiBlock($oBlock->GetDisplay(
$oPage,
"SearchFormToAdd_{$this->m_iInputId}",
[
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$this->m_iInputId}",
'table_id' => "add_{$this->m_iInputId}",
@@ -55,12 +57,14 @@ class UISearchFormForeignKeys
'selection_mode' => true,
'cssCount' => "#count_{$this->m_iInputId}",
'query_params' => $oFilter->GetInternalParams(),
)));
]
));
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sCancel = Dict::S('UI:Button:Cancel');
$sAdd = Dict::S('UI:Button:Add');
$oPage->add(<<<HTML
$oPage->add(
<<<HTML
<form id="ObjectsAddForm_{$this->m_iInputId}">
<div id="SearchResultsToAdd_{$this->m_iInputId}" style="vertical-align:top;height:100%;overflow:auto;padding:0;border:0;">
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
@@ -101,20 +105,17 @@ $('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});
$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);
$('#SearchFormToAdd_{$this->m_iInputId}').on('resize', oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);
JS
);
);
}
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
{
try
{
try {
$aLinkedObjects = utils::ReadMultipleSelectionWithFriendlyname($oFullSetFilter);
$oPage->add(json_encode($aLinkedObjects));
}
catch (CoreException $e)
{
} catch (CoreException $e) {
http_response_code(500);
$oPage->add(json_encode(array('error' => $e->GetMessage())));
$oPage->add(json_encode(['error' => $e->GetMessage()]));
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
}
}
@@ -129,20 +130,20 @@ JS
*/
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
{
if ($sRemoteClass != '')
{
if ($sRemoteClass != '') {
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
$oFilter = new DBObjectSearch($sRemoteClass);
}
else
{
} else {
// No remote class specified use the one defined in the linkedset
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->m_iInputId}",
array('menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"));
$oBlock->Display(
$oP,
"ResultsToAdd_{$this->m_iInputId}",
['menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"]
);
}
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -17,7 +18,6 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\WebPage\iTopWebPage;
/**
* Class UIWizard
*
@@ -31,74 +31,73 @@ class UIWizard
protected $m_sClass;
protected $m_sTargetState;
protected $m_aWizardSteps;
public function __construct($oPage, $sClass, $sTargetState = '')
{
$this->m_oPage = $oPage;
$this->m_sClass = $sClass;
if (empty($sTargetState))
{
if (empty($sTargetState)) {
$sTargetState = MetaModel::GetDefaultState($sClass);
}
$this->m_sTargetState = $sTargetState;
$this->m_aWizardSteps = $this->ComputeWizardStructure();
}
public function GetObjectClass() { return $this->m_sClass; }
public function GetTargetState() { return $this->m_sTargetState; }
public function GetWizardStructure() { return $this->m_aWizardSteps; }
public function GetObjectClass()
{
return $this->m_sClass;
}
public function GetTargetState()
{
return $this->m_sTargetState;
}
public function GetWizardStructure()
{
return $this->m_aWizardSteps;
}
/**
* Displays one step of the wizard
*/
public function DisplayWizardStep($aStep, $iStepIndex, &$iMaxInputId, &$aFieldsMap, $bFinishEnabled = false, $aArgs = array())
*/
public function DisplayWizardStep($aStep, $iStepIndex, &$iMaxInputId, &$aFieldsMap, $bFinishEnabled = false, $aArgs = [])
{
if ($iStepIndex == 1) // one big form that contains everything, to make sure that the uploaded files are posted too
{
if ($iStepIndex == 1) { // one big form that contains everything, to make sure that the uploaded files are posted too
$this->m_oPage->add("<form method=\"post\" enctype=\"multipart/form-data\" action=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php\">\n");
}
$this->m_oPage->add("<div class=\"wizContainer\" id=\"wizStep$iStepIndex\" style=\"display:none;\">\n");
$this->m_oPage->add("<a name=\"step$iStepIndex\" />\n");
$aStates = MetaModel::EnumStates($this->m_sClass);
$aDetails = array();
$aDetails = [];
$sJSHandlerCode = ''; // Javascript code to be executed each time this step of the wizard is entered
foreach($aStep as $sAttCode)
{
foreach ($aStep as $sAttCode) {
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oAttDef->IsWritable())
{
if ($oAttDef->IsWritable()) {
$sAttLabel = $oAttDef->GetLabel();
$iOptions = isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode]) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT))
{
$aFields[$sAttCode] = array();
foreach($aPrerequisites as $sCode)
{
if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) {
$aFields[$sAttCode] = [];
foreach ($aPrerequisites as $sCode) {
$aFields[$sAttCode][$sCode] = '';
}
}
if (count($aPrerequisites) > 0)
{
if (count($aPrerequisites) > 0) {
$aOptions[] = 'Prerequisites: '.implode(', ', $aPrerequisites);
}
$sFieldFlag = (($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE)) || (!$oAttDef->IsNullAllowed()) )? ' <span class="hilite">*</span>' : '';
$sFieldFlag = (($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE)) || (!$oAttDef->IsNullAllowed())) ? ' <span class="hilite">*</span>' : '';
$oDefaultValuesSet = $oAttDef->GetDefaultValue(/* $oObject->ToArgs() */); // @@@ TO DO: get the object's current value if the object exists
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($this->m_oPage, $this->m_sClass, $sAttCode, $oAttDef, $oDefaultValuesSet, '', "att_$iMaxInputId", '', $iOptions, $aArgs);
$aFieldsMap["att_$iMaxInputId"] = $sAttCode;
$aDetails[] = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().$sFieldFlag.'</span>', 'value' => "<span id=\"field_att_$iMaxInputId\">$sHTMLValue</span>");
if ($oAttDef->GetValuesDef() != null)
{
$aDetails[] = ['label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().$sFieldFlag.'</span>', 'value' => "<span id=\"field_att_$iMaxInputId\">$sHTMLValue</span>"];
if ($oAttDef->GetValuesDef() != null) {
$sJSHandlerCode .= "\toWizardHelper.RequestAllowedValues('$sAttCode');\n";
}
if ($oAttDef->GetDefaultValue() != null)
{
if ($oAttDef->GetDefaultValue() != null) {
$sJSHandlerCode .= "\toWizardHelper.RequestDefaultValue('$sAttCode');\n";
}
if ($oAttDef->IsLinkSet())
{
if ($oAttDef->IsLinkSet()) {
$sJSHandlerCode .= "\toLinkWidgetatt_$iMaxInputId.Init();";
}
$iMaxInputId++;
@@ -126,11 +125,11 @@ $sJSHandlerCode
}
");
$this->m_oPage->add("</div>\n\n");
}
}
/**
* Display the final step of the wizard: a confirmation screen
*/
*/
public function DisplayFinalStep($iStepIndex, $aFieldsMap)
{
$oAppContext = new ApplicationContext();
@@ -141,136 +140,116 @@ $sJSHandlerCode
$this->m_oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\" />\n");
$this->m_oPage->add("<input type=\"hidden\" id=\"wizard_json_obj\" name=\"json_obj\" value=\"\" />\n");
$sScript = "function OnEnterStep$iStepIndex() {\n";
foreach($aFieldsMap as $iInputId => $sAttCode)
{
$sScript .= "\toWizardHelper.UpdateCurrentValue('$sAttCode');\n";
foreach ($aFieldsMap as $iInputId => $sAttCode) {
$sScript .= "\toWizardHelper.UpdateCurrentValue('$sAttCode');\n";
}
$sScript .= "\toWizardHelper.Preview('object_preview');\n";
$sScript .= "\t$('#wizard_json_obj').val(oWizardHelper.ToJSON());\n";
$sScript .= "\toWizardHelper.Preview('object_preview');\n";
$sScript .= "\t$('#wizard_json_obj').val(oWizardHelper.ToJSON());\n";
$sScript .= "}\n";
$this->m_oPage->add_script($sScript);
$this->m_oPage->add("<div id=\"object_preview\">\n");
$this->m_oPage->add("</div>\n");
$this->m_oPage->add($oAppContext->GetForForm());
$this->m_oPage->add($oAppContext->GetForForm());
$this->m_oPage->add("<input type=\"button\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"GoToStep($iStepIndex, $iStepIndex - 1)\" />");
$this->m_oPage->add("<input type=\"submit\" value=\"Create ".MetaModel::GetName($this->m_sClass)."\" />\n");
$this->m_oPage->add("</div>\n");
$this->m_oPage->add("</form>\n");
}
}
/**
* Compute the order of the fields & pages in the wizard
* @param $oPage iTopWebPage The current page (used to display error messages)
* @param $oPage iTopWebPage The current page (used to display error messages)
* @param $sClass string Name of the class
* @param $sStateCode string Code of the target state of the object
* @return hash Two dimensional array: each element represents the list of fields for a given page
* @return hash Two dimensional array: each element represents the list of fields for a given page
*/
protected function ComputeWizardStructure()
{
$aWizardSteps = array( 'mandatory' => array(), 'optional' => array());
$aFieldsDone = array(); // Store all the fields that are already covered by a previous step of the wizard
$aWizardSteps = [ 'mandatory' => [], 'optional' => []];
$aFieldsDone = []; // Store all the fields that are already covered by a previous step of the wizard
$aStates = MetaModel::EnumStates($this->m_sClass);
$sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass);
$aMandatoryAttributes = array();
// Some attributes are always mandatory independently of the state machine (if any)
foreach(MetaModel::GetAttributesList($this->m_sClass) as $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if (!$oAttDef->IsExternalField() && !$oAttDef->IsNullAllowed() &&
$oAttDef->IsWritable() && ($sAttCode != $sStateAttCode) )
{
$aMandatoryAttributes[$sAttCode] = OPT_ATT_MANDATORY;
}
}
// Now check the attributes that are mandatory in the specified state
if ( (!empty($this->m_sTargetState)) && (count($aStates[$this->m_sTargetState]['attribute_list']) > 0) )
{
$aMandatoryAttributes = [];
// Some attributes are always mandatory independently of the state machine (if any)
foreach (MetaModel::GetAttributesList($this->m_sClass) as $sAttCode) {
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if (!$oAttDef->IsExternalField() && !$oAttDef->IsNullAllowed() &&
$oAttDef->IsWritable() && ($sAttCode != $sStateAttCode)) {
$aMandatoryAttributes[$sAttCode] = OPT_ATT_MANDATORY;
}
}
// Now check the attributes that are mandatory in the specified state
if ((!empty($this->m_sTargetState)) && (count($aStates[$this->m_sTargetState]['attribute_list']) > 0)) {
// Check all the fields that *must* be included in the wizard for this
// particular target state
$aFields = array();
foreach($aStates[$this->m_sTargetState]['attribute_list'] as $sAttCode => $iOptions)
{
if ( (isset($aMandatoryAttributes[$sAttCode])) &&
($aMandatoryAttributes[$sAttCode] & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) )
{
$aFields = [];
foreach ($aStates[$this->m_sTargetState]['attribute_list'] as $sAttCode => $iOptions) {
if ((isset($aMandatoryAttributes[$sAttCode])) &&
($aMandatoryAttributes[$sAttCode] & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT))) {
$aMandatoryAttributes[$sAttCode] |= $iOptions;
}
else
{
} else {
$aMandatoryAttributes[$sAttCode] = $iOptions;
}
}
}
// Check all the fields that *must* be included in the wizard
// i.e. all mandatory, must-change or must-prompt fields that are
// not also read-only or hidden.
// Some fields may be required (null not allowed) from the database
// perspective, but hidden or read-only from the user interface perspective
$aFields = array();
foreach($aMandatoryAttributes as $sAttCode => $iOptions)
{
if ( ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) &&
!($iOptions & (OPT_ATT_READONLY | OPT_ATT_HIDDEN)) )
{
// perspective, but hidden or read-only from the user interface perspective
$aFields = [];
foreach ($aMandatoryAttributes as $sAttCode => $iOptions) {
if (($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) &&
!($iOptions & (OPT_ATT_READONLY | OPT_ATT_HIDDEN))) {
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
$aFields[$sAttCode] = array();
foreach($aPrerequisites as $sCode)
{
$aFields[$sAttCode] = [];
foreach ($aPrerequisites as $sCode) {
$aFields[$sAttCode][$sCode] = '';
}
}
}
// Now use the dependencies between the fields to order them
// Start from the order of the 'details'
$aList = MetaModel::FlattenZlist(MetaModel::GetZListItems($this->m_sClass, 'details'));
$index = 0;
$aOrder = array();
foreach($aFields as $sAttCode => $void)
{
$aOrder[$sAttCode] = 999; // At the end of the list...
$aOrder = [];
foreach ($aFields as $sAttCode => $void) {
$aOrder[$sAttCode] = 999; // At the end of the list...
}
foreach($aList as $sAttCode)
{
if (array_key_exists($sAttCode, $aFields))
{
foreach ($aList as $sAttCode) {
if (array_key_exists($sAttCode, $aFields)) {
$aOrder[$sAttCode] = $index;
}
$index++;
}
foreach($aFields as $sAttCode => $aDependencies)
{
foreach ($aFields as $sAttCode => $aDependencies) {
// All fields with no remaining dependencies can be entered at this
// step of the wizard
if (count($aDependencies) > 0)
{
if (count($aDependencies) > 0) {
$iMaxPos = 0;
// Remove this field from the dependencies of the other fields
foreach($aDependencies as $sDependentAttCode => $void)
{
foreach ($aDependencies as $sDependentAttCode => $void) {
// position the current field after the ones it depends on
$iMaxPos = max($iMaxPos, 1+$aOrder[$sDependentAttCode]);
$iMaxPos = max($iMaxPos, 1 + $aOrder[$sDependentAttCode]);
}
}
}
asort($aOrder);
$aCurrentStep = array();
foreach($aOrder as $sAttCode => $rank)
{
$aCurrentStep = [];
foreach ($aOrder as $sAttCode => $rank) {
$aCurrentStep[] = $sAttCode;
$aFieldsDone[$sAttCode] = '';
}
$aWizardSteps['mandatory'][] = $aCurrentStep;
// Now computes the steps to fill the optional fields
$aFields = array(); // reset
foreach(MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode=>$oAttDef)
{
$aFields = []; // reset
foreach (MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode => $oAttDef) {
$iOptions = (isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode])) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
if (($sStateAttCode != $sAttCode) &&
(!$oAttDef->IsExternalField()) &&
@@ -282,7 +261,7 @@ $sJSHandlerCode
// are removed from the 'optional' part of the wizard
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
$aFields[$sAttCode] = array();
$aFields[$sAttCode] = [];
foreach ($aPrerequisites as $sCode) {
if (!isset($aFieldsDone[$sCode])) {
// retain only the dependencies that were not covered
@@ -293,28 +272,23 @@ $sJSHandlerCode
}
}
// Now use the dependencies between the fields to order them
while(count($aFields) > 0)
{
$aCurrentStep = array();
foreach($aFields as $sAttCode => $aDependencies)
{
while (count($aFields) > 0) {
$aCurrentStep = [];
foreach ($aFields as $sAttCode => $aDependencies) {
// All fields with no remaining dependencies can be entered at this
// step of the wizard
if (count($aDependencies) == 0)
{
if (count($aDependencies) == 0) {
$aCurrentStep[] = $sAttCode;
$aFieldsDone[$sAttCode] = '';
unset($aFields[$sAttCode]);
// Remove this field from the dependencies of the other fields
foreach($aFields as $sUpdatedCode => $aDummy)
{
foreach ($aFields as $sUpdatedCode => $aDummy) {
// remove the dependency
unset($aFields[$sUpdatedCode][$sAttCode]);
}
}
}
if (count($aCurrentStep) == 0)
{
if (count($aCurrentStep) == 0) {
// This step of the wizard would contain NO field !
$this->m_oPage->add(Dict::S('UI:Error:WizardCircularReferenceInDependencies'));
print_r($aFields);
@@ -323,7 +297,6 @@ $sJSHandlerCode
$aWizardSteps['optional'][] = $aCurrentStep;
}
return $aWizardSteps;
}
}
}
?>

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -31,40 +32,39 @@ class UserDashboard extends DBObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => array('user_id', 'menu_code'),
"name_attcode" => ['user_id', 'menu_code'],
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_app_dashboards",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("menu_code", array("allowed_values"=>null, "sql"=>"menu_code", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("contents", array("allowed_values"=>null, "sql"=>"contents", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
];
MetaModel::Init_SetZListItems('default_search', array (
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", ["targetclass" => "User", "allowed_values" => null, "sql" => "user_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("menu_code", ["allowed_values" => null, "sql" => "menu_code", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeText("contents", ["allowed_values" => null, "sql" => "contents", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_SetZListItems('default_search', [
0 => 'user_id',
1 => 'menu_code',
));
MetaModel::Init_SetZListItems('list', array (
]);
MetaModel::Init_SetZListItems('list', [
0 => 'user_id',
1 => 'menu_code',
));
]);
}
/**
* Overloading this function here to secure a fix done right before the release
* The real fix should be to implement this verb in DBObject
* The real fix should be to implement this verb in DBObject
*/
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
$this->DBDelete($oDeletionPlan);
}
}
?>

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -227,13 +228,12 @@ class appUserPreferences extends DBObject
// No prefs (yet) for this user, create the object
$oObj = new appUserPreferences();
$oObj->Set('userid', $sUserId);
$oObj->Set('preferences', array()); // Default preferences: an empty array
$oObj->Set('preferences', []); // Default preferences: an empty array
try {
utils::PushArchiveMode(false);
$oObj->DBInsert();
utils::PopArchiveMode();
}
catch (Exception $e) {
} catch (Exception $e) {
// Ignore errors
}
}
@@ -245,25 +245,25 @@ class appUserPreferences extends DBObject
*/
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array("userid","login"),
"reconc_keys" => ["userid","login"],
"db_table" => "priv_app_preferences",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", array("allowed_values"=>null, "sql"=>"preferences", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "org_id")));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login")));
MetaModel::Init_SetZListItems('list', array('org_id','preferences'));
MetaModel::Init_SetZListItems('default_search', array('userid','login','org_id'));
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", ["targetclass" => "User", "allowed_values" => null, "sql" => "userid", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", ["allowed_values" => null, "sql" => "preferences", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", ["allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "org_id"]));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", ["allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login"]));
MetaModel::Init_SetZListItems('list', ['org_id','preferences']);
MetaModel::Init_SetZListItems('default_search', ['userid','login','org_id']);
}
/**

View File

@@ -1,43 +1,51 @@
<?php
/* @author Mark Jones
* @license MIT License
* */
if (!class_exists('ZipArchive')) { throw new Exception('ZipArchive not found'); }
if (!class_exists('ZipArchive')) {
throw new Exception('ZipArchive not found');
}
Class XLSXWriter
class XLSXWriter
{
//------------------------------------------------------------------
protected $author ='Doc Author';
protected $sheets_meta = array();
protected $shared_strings = array();//unique set
protected $author = 'Doc Author';
protected $sheets_meta = [];
protected $shared_strings = [];//unique set
protected $shared_string_count = 0;//count of non-unique references to the unique set
protected $temp_files = array();
protected $temp_files = [];
protected $date_format = 'YYYY-MM-DD';
protected $date_time_format = 'YYYY-MM-DD\ HH:MM:SS';
public function __construct(){}
public function setAuthor($author='') { $this->author=$author; }
public function __construct()
{
}
public function setAuthor($author = '')
{
$this->author = $author;
}
public function __destruct()
{
if (!empty($this->temp_files)) {
foreach($this->temp_files as $temp_file) {
foreach ($this->temp_files as $temp_file) {
@unlink($temp_file);
}
}
}
public function setDateFormat($date_format)
{
$this->date_format = $date_format;
}
public function setDateTimeFormat($date_time_format)
{
$this->date_time_format = $date_time_format;
}
protected function tempFilename()
{
$filename = tempnam(SetupUtils::GettmpDir(), 'xlsx_writer_');
@@ -64,120 +72,123 @@ Class XLSXWriter
{
@unlink($filename);//if the zip already exists, overwrite it
$zip = new ZipArchive();
if (empty($this->sheets_meta)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined."); return; }
if (!$zip->open($filename, ZipArchive::CREATE)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip."); return; }
if (empty($this->sheets_meta)) {
self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined.");
return;
}
if (!$zip->open($filename, ZipArchive::CREATE)) {
self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip.");
return;
}
$zip->addEmptyDir("docProps/");
$zip->addFromString("docProps/app.xml" , self::buildAppXML() );
$zip->addFromString("docProps/app.xml", self::buildAppXML());
$zip->addFromString("docProps/core.xml", self::buildCoreXML());
$zip->addEmptyDir("_rels/");
$zip->addFromString("_rels/.rels", self::buildRelationshipsXML());
$zip->addEmptyDir("xl/worksheets/");
foreach($this->sheets_meta as $sheet_meta) {
$zip->addFile($sheet_meta['filename'], "xl/worksheets/".$sheet_meta['xmlname'] );
foreach ($this->sheets_meta as $sheet_meta) {
$zip->addFile($sheet_meta['filename'], "xl/worksheets/".$sheet_meta['xmlname']);
}
if (!empty($this->shared_strings)) {
$zip->addFile($this->writeSharedStringsXML(), "xl/sharedStrings.xml" ); //$zip->addFromString("xl/sharedStrings.xml", self::buildSharedStringsXML() );
$zip->addFile($this->writeSharedStringsXML(), "xl/sharedStrings.xml"); //$zip->addFromString("xl/sharedStrings.xml", self::buildSharedStringsXML() );
}
$zip->addFromString("xl/workbook.xml" , self::buildWorkbookXML() );
$zip->addFile($this->writeStylesXML(), "xl/styles.xml" ); //$zip->addFromString("xl/styles.xml" , self::buildStylesXML() );
$zip->addFromString("[Content_Types].xml" , self::buildContentTypesXML() );
$zip->addFromString("xl/workbook.xml", self::buildWorkbookXML());
$zip->addFile($this->writeStylesXML(), "xl/styles.xml"); //$zip->addFromString("xl/styles.xml" , self::buildStylesXML() );
$zip->addFromString("[Content_Types].xml", self::buildContentTypesXML());
$zip->addEmptyDir("xl/_rels/");
$zip->addFromString("xl/_rels/workbook.xml.rels", self::buildWorkbookRelsXML() );
$zip->addFromString("xl/_rels/workbook.xml.rels", self::buildWorkbookRelsXML());
$zip->close();
}
public function writeSheet(array $data, $sheet_name='', array $header_types=array(), array $header_row=array() )
public function writeSheet(array $data, $sheet_name = '', array $header_types = [], array $header_row = [])
{
$data = empty($data) ? array( array('') ) : $data;
$data = empty($data) ? [ [''] ] : $data;
$sheet_filename = $this->tempFilename();
$sheet_default = 'Sheet'.(count($this->sheets_meta)+1);
$sheet_default = 'Sheet'.(count($this->sheets_meta) + 1);
$sheet_name = !empty($sheet_name) ? $sheet_name : $sheet_default;
$this->sheets_meta[] = array('filename'=>$sheet_filename, 'sheetname'=>$sheet_name ,'xmlname'=>strtolower($sheet_default).".xml" );
$this->sheets_meta[] = ['filename' => $sheet_filename, 'sheetname' => $sheet_name ,'xmlname' => strtolower($sheet_default).".xml" ];
$header_offset = empty($header_types) ? 0 : 1;
$row_count = count($data) + $header_offset;
$column_count = count($data[self::array_first_key($data)]);
$max_cell = self::xlsCell( $row_count-1, $column_count-1 );
$max_cell = self::xlsCell($row_count - 1, $column_count - 1);
$tabselected = count($this->sheets_meta)==1 ? 'true' : 'false';//only first sheet is selected
$tabselected = count($this->sheets_meta) == 1 ? 'true' : 'false';//only first sheet is selected
$cell_formats_arr = empty($header_types) ? array_fill(0, $column_count, 'string') : array_values($header_types);
if (empty($header_row) && !empty($header_types))
{
$header_row = empty($header_types) ? array() : array_keys($header_types);
if (empty($header_row) && !empty($header_types)) {
$header_row = empty($header_types) ? [] : array_keys($header_types);
}
$fd = fopen($sheet_filename, "w+");
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
fwrite($fd,'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd,'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">');
fwrite($fd, '<sheetPr filterMode="false">');
fwrite($fd, '<pageSetUpPr fitToPage="false"/>');
fwrite($fd, '</sheetPr>');
fwrite($fd, '<dimension ref="A1:'.$max_cell.'"/>');
fwrite($fd, '<sheetViews>');
fwrite($fd, '<sheetView colorId="64" defaultGridColor="true" rightToLeft="false" showFormulas="false" showGridLines="true" showOutlineSymbols="true" showRowColHeaders="true" showZeros="true" tabSelected="'.$tabselected.'" topLeftCell="A1" view="normal" windowProtection="false" workbookViewId="0" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100">');
fwrite($fd, '<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>');
fwrite($fd, '</sheetView>');
fwrite($fd, '</sheetViews>');
fwrite($fd, '<cols>');
fwrite($fd, '<col collapsed="false" hidden="false" max="1025" min="1" style="0" width="19"/>');
fwrite($fd, '</cols>');
fwrite($fd, '<sheetData>');
if (!empty($header_row))
{
if ($fd === false) {
self::log("write failed in ".__CLASS__."::".__FUNCTION__.".");
return;
}
fwrite($fd, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd, '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">');
fwrite($fd, '<sheetPr filterMode="false">');
fwrite($fd, '<pageSetUpPr fitToPage="false"/>');
fwrite($fd, '</sheetPr>');
fwrite($fd, '<dimension ref="A1:'.$max_cell.'"/>');
fwrite($fd, '<sheetViews>');
fwrite($fd, '<sheetView colorId="64" defaultGridColor="true" rightToLeft="false" showFormulas="false" showGridLines="true" showOutlineSymbols="true" showRowColHeaders="true" showZeros="true" tabSelected="'.$tabselected.'" topLeftCell="A1" view="normal" windowProtection="false" workbookViewId="0" zoomScale="100" zoomScaleNormal="100" zoomScalePageLayoutView="100">');
fwrite($fd, '<selection activeCell="A1" activeCellId="0" pane="topLeft" sqref="A1"/>');
fwrite($fd, '</sheetView>');
fwrite($fd, '</sheetViews>');
fwrite($fd, '<cols>');
fwrite($fd, '<col collapsed="false" hidden="false" max="1025" min="1" style="0" width="19"/>');
fwrite($fd, '</cols>');
fwrite($fd, '<sheetData>');
if (!empty($header_row)) {
fwrite($fd, '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.(1).'">');
foreach($header_row as $k=>$v)
{
$this->writeCell($fd, 0, $k, $v, $cell_format='string');
foreach ($header_row as $k => $v) {
$this->writeCell($fd, 0, $k, $v, $cell_format = 'string');
}
fwrite($fd, '</row>');
}
foreach($data as $i=>$row)
{
fwrite($fd, '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.($i+$header_offset+1).'">');
foreach($row as $k=>$v)
{
$this->writeCell($fd, $i+$header_offset, $k, $v, $cell_formats_arr[$k]);
foreach ($data as $i => $row) {
fwrite($fd, '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'.($i + $header_offset + 1).'">');
foreach ($row as $k => $v) {
$this->writeCell($fd, $i + $header_offset, $k, $v, $cell_formats_arr[$k]);
}
fwrite($fd, '</row>');
}
fwrite($fd, '</sheetData>');
fwrite($fd, '<printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"/>');
fwrite($fd, '<pageMargins left="0.5" right="0.5" top="1.0" bottom="1.0" header="0.5" footer="0.5"/>');
fwrite($fd, '<pageSetup blackAndWhite="false" cellComments="none" copies="1" draft="false" firstPageNumber="1" fitToHeight="1" fitToWidth="1" horizontalDpi="300" orientation="portrait" pageOrder="downThenOver" paperSize="1" scale="100" useFirstPageNumber="true" usePrinterDefaults="false" verticalDpi="300"/>');
fwrite($fd, '<headerFooter differentFirst="false" differentOddEven="false">');
fwrite($fd, '<oddHeader>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12&amp;A</oddHeader>');
fwrite($fd, '<oddFooter>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12Page &amp;P</oddFooter>');
fwrite($fd, '</headerFooter>');
fwrite($fd,'</worksheet>');
fwrite($fd, '</sheetData>');
fwrite($fd, '<printOptions headings="false" gridLines="false" gridLinesSet="true" horizontalCentered="false" verticalCentered="false"/>');
fwrite($fd, '<pageMargins left="0.5" right="0.5" top="1.0" bottom="1.0" header="0.5" footer="0.5"/>');
fwrite($fd, '<pageSetup blackAndWhite="false" cellComments="none" copies="1" draft="false" firstPageNumber="1" fitToHeight="1" fitToWidth="1" horizontalDpi="300" orientation="portrait" pageOrder="downThenOver" paperSize="1" scale="100" useFirstPageNumber="true" usePrinterDefaults="false" verticalDpi="300"/>');
fwrite($fd, '<headerFooter differentFirst="false" differentOddEven="false">');
fwrite($fd, '<oddHeader>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12&amp;A</oddHeader>');
fwrite($fd, '<oddFooter>&amp;C&amp;&quot;Times New Roman,Regular&quot;&amp;12Page &amp;P</oddFooter>');
fwrite($fd, '</headerFooter>');
fwrite($fd, '</worksheet>');
fclose($fd);
}
protected function writeCell($fd, $row_number, $column_number, $value, $cell_format)
{
static $styles = array('money'=>1,'dollar'=>1,'datetime'=>2,'date'=>3,'string'=>0);
static $styles = ['money' => 1,'dollar' => 1,'datetime' => 2,'date' => 3,'string' => 0];
$cell = self::xlsCell($row_number, $column_number);
$s = isset($styles[$cell_format]) && ($value !== '') ? $styles[$cell_format] : '0';
if (is_int($value) || is_float($value)) {
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value*1).'</v></c>');//int,float, etc
} else if (($cell_format=='date') && ($value != '')) {
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>');
} else if (($cell_format=='datetime') && ($value != '')) {
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
} else if ($value==''){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'"/>');
} else if ($value[0]=='='){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
} else if ($value!==''){
fwrite($fd,'<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');
fwrite($fd, '<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.($value * 1).'</v></c>');//int,float, etc
} elseif (($cell_format == 'date') && ($value != '')) {
fwrite($fd, '<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.intval(self::convert_date_time($value)).'</v></c>');
} elseif (($cell_format == 'datetime') && ($value != '')) {
fwrite($fd, '<c r="'.$cell.'" s="'.$s.'" t="n"><v>'.self::convert_date_time($value).'</v></c>');
} elseif ($value == '') {
fwrite($fd, '<c r="'.$cell.'" s="'.$s.'"/>');
} elseif ($value[0] == '=') {
fwrite($fd, '<c r="'.$cell.'" s="'.$s.'" t="s"><f>'.self::xmlspecialchars($value).'</f></c>');
} elseif ($value !== '') {
fwrite($fd, '<c r="'.$cell.'" s="'.$s.'" t="s"><v>'.self::xmlspecialchars($this->setSharedString($value)).'</v></c>');
}
}
@@ -185,62 +196,65 @@ Class XLSXWriter
{
$tempfile = $this->tempFilename();
$fd = fopen($tempfile, "w+");
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
if ($fd === false) {
self::log("write failed in ".__CLASS__."::".__FUNCTION__.".");
return;
}
fwrite($fd, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd, '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
fwrite($fd, '<numFmts count="4">');
fwrite($fd, '<numFmt formatCode="GENERAL" numFmtId="164"/>');
fwrite($fd, '<numFmt formatCode="[$$-1009]#,##0.00;[RED]\-[$$-1009]#,##0.00" numFmtId="165"/>');
fwrite($fd, '<numFmt formatCode="'.$this->date_time_format.'" numFmtId="166"/>');
fwrite($fd, '<numFmt formatCode="'.$this->date_format.'" numFmtId="167"/>');
fwrite($fd, '<numFmt formatCode="GENERAL" numFmtId="164"/>');
fwrite($fd, '<numFmt formatCode="[$$-1009]#,##0.00;[RED]\-[$$-1009]#,##0.00" numFmtId="165"/>');
fwrite($fd, '<numFmt formatCode="'.$this->date_time_format.'" numFmtId="166"/>');
fwrite($fd, '<numFmt formatCode="'.$this->date_format.'" numFmtId="167"/>');
fwrite($fd, '</numFmts>');
fwrite($fd, '<fonts count="4">');
fwrite($fd, '<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '<font><name val="Arial"/><family val="0"/><sz val="10"/></font>');
fwrite($fd, '</fonts>');
fwrite($fd, '<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>');
fwrite($fd, '<borders count="1"><border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border></borders>');
fwrite($fd, '<cellStyleXfs count="15">');
fwrite($fd, '<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">');
fwrite($fd, '<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>');
fwrite($fd, '<protection hidden="false" locked="true"/>');
fwrite($fd, '</xf>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<cellStyleXfs count="15">');
fwrite($fd, '<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">');
fwrite($fd, '<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>');
fwrite($fd, '<protection hidden="false" locked="true"/>');
fwrite($fd, '</xf>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="43"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="41"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"/>');
//fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>');
fwrite($fd, '</cellStyleXfs>');
fwrite($fd, '<cellXfs count="4">');
fwrite($fd, '<xf applyAlignment="1" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"><alignment wrapText="1"/></xf>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');
fwrite($fd, '</cellXfs>');
fwrite($fd, '<cellStyles count="1">');
fwrite($fd, '<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>');
fwrite($fd, '</cellStyleXfs>');
fwrite($fd, '<cellXfs count="4">');
fwrite($fd, '<xf applyAlignment="1" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="164" xfId="0"><alignment wrapText="1"/></xf>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="165" xfId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="166" xfId="0"/>');
fwrite($fd, '<xf applyAlignment="false" applyBorder="false" applyFont="false" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="167" xfId="0"/>');
fwrite($fd, '</cellXfs>');
fwrite($fd, '<cellStyles count="1">');
fwrite($fd, '<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>');
//fwrite($fd, '<cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>');
//fwrite($fd, '<cellStyle builtinId="6" customBuiltin="false" name="Comma [0]" xfId="16"/>');
//fwrite($fd, '<cellStyle builtinId="4" customBuiltin="false" name="Currency" xfId="17"/>');
//fwrite($fd, '<cellStyle builtinId="7" customBuiltin="false" name="Currency [0]" xfId="18"/>');
//fwrite($fd, '<cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>');
fwrite($fd, '</cellStyles>');
fwrite($fd, '</cellStyles>');
fwrite($fd, '</styleSheet>');
fclose($fd);
return $tempfile;
@@ -250,12 +264,9 @@ Class XLSXWriter
{
// Strip control characters which Excel does not seem to like...
$v = preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F]/u', '', $v);
if (isset($this->shared_strings[$v]))
{
if (isset($this->shared_strings[$v])) {
$string_value = $this->shared_strings[$v];
}
else
{
} else {
$string_value = count($this->shared_strings);
$this->shared_strings[$v] = $string_value;
}
@@ -267,13 +278,15 @@ Class XLSXWriter
{
$tempfile = $this->tempFilename();
$fd = fopen($tempfile, "w+");
if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; }
fwrite($fd,'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd,'<sst count="'.($this->shared_string_count).'" uniqueCount="'.count($this->shared_strings).'" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
foreach($this->shared_strings as $s=>$c)
{
fwrite($fd,'<si><t>'.self::xmlspecialchars($s).'</t></si>');
if ($fd === false) {
self::log("write failed in ".__CLASS__."::".__FUNCTION__.".");
return;
}
fwrite($fd, '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n");
fwrite($fd, '<sst count="'.($this->shared_string_count).'" uniqueCount="'.count($this->shared_strings).'" xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">');
foreach ($this->shared_strings as $s => $c) {
fwrite($fd, '<si><t>'.self::xmlspecialchars($s).'</t></si>');
}
fwrite($fd, '</sst>');
fclose($fd);
@@ -282,89 +295,89 @@ Class XLSXWriter
protected function buildAppXML()
{
$app_xml="";
$app_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$app_xml.='<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime></Properties>';
$app_xml = "";
$app_xml .= '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$app_xml .= '<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime></Properties>';
return $app_xml;
}
protected function buildCoreXML()
{
$core_xml="";
$core_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$core_xml.='<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
$core_xml.='<dcterms:created xsi:type="dcterms:W3CDTF">'.date("Y-m-d\TH:i:s.00\Z").'</dcterms:created>';//$date_time = '2013-07-25T15:54:37.00Z';
$core_xml.='<dc:creator>'.self::xmlspecialchars($this->author).'</dc:creator>';
$core_xml.='<cp:revision>0</cp:revision>';
$core_xml.='</cp:coreProperties>';
$core_xml = "";
$core_xml .= '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$core_xml .= '<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">';
$core_xml .= '<dcterms:created xsi:type="dcterms:W3CDTF">'.date("Y-m-d\TH:i:s.00\Z").'</dcterms:created>';//$date_time = '2013-07-25T15:54:37.00Z';
$core_xml .= '<dc:creator>'.self::xmlspecialchars($this->author).'</dc:creator>';
$core_xml .= '<cp:revision>0</cp:revision>';
$core_xml .= '</cp:coreProperties>';
return $core_xml;
}
protected function buildRelationshipsXML()
{
$rels_xml="";
$rels_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
$rels_xml.='<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
$rels_xml.='<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>';
$rels_xml.='<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>';
$rels_xml.='<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>';
$rels_xml.="\n";
$rels_xml.='</Relationships>';
$rels_xml = "";
$rels_xml .= '<?xml version="1.0" encoding="UTF-8"?>'."\n";
$rels_xml .= '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
$rels_xml .= '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>';
$rels_xml .= '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>';
$rels_xml .= '<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>';
$rels_xml .= "\n";
$rels_xml .= '</Relationships>';
return $rels_xml;
}
protected function buildWorkbookXML()
{
$workbook_xml="";
$workbook_xml.='<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$workbook_xml.='<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">';
$workbook_xml.='<fileVersion appName="Calc"/><workbookPr backupFile="false" showObjects="all" date1904="false"/><workbookProtection/>';
$workbook_xml.='<bookViews><workbookView activeTab="0" firstSheet="0" showHorizontalScroll="true" showSheetTabs="true" showVerticalScroll="true" tabRatio="212" windowHeight="8192" windowWidth="16384" xWindow="0" yWindow="0"/></bookViews>';
$workbook_xml.='<sheets>';
foreach($this->sheets_meta as $i=>$sheet_meta) {
$workbook_xml.='<sheet name="'.self::xmlspecialchars($sheet_meta['sheetname']).'" sheetId="'.($i+1).'" state="visible" r:id="rId'.($i+2).'"/>';
$workbook_xml = "";
$workbook_xml .= '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n";
$workbook_xml .= '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">';
$workbook_xml .= '<fileVersion appName="Calc"/><workbookPr backupFile="false" showObjects="all" date1904="false"/><workbookProtection/>';
$workbook_xml .= '<bookViews><workbookView activeTab="0" firstSheet="0" showHorizontalScroll="true" showSheetTabs="true" showVerticalScroll="true" tabRatio="212" windowHeight="8192" windowWidth="16384" xWindow="0" yWindow="0"/></bookViews>';
$workbook_xml .= '<sheets>';
foreach ($this->sheets_meta as $i => $sheet_meta) {
$workbook_xml .= '<sheet name="'.self::xmlspecialchars($sheet_meta['sheetname']).'" sheetId="'.($i + 1).'" state="visible" r:id="rId'.($i + 2).'"/>';
}
$workbook_xml.='</sheets>';
$workbook_xml.='<calcPr iterateCount="100" refMode="A1" iterate="false" iterateDelta="0.001"/></workbook>';
$workbook_xml .= '</sheets>';
$workbook_xml .= '<calcPr iterateCount="100" refMode="A1" iterate="false" iterateDelta="0.001"/></workbook>';
return $workbook_xml;
}
protected function buildWorkbookRelsXML()
{
$wkbkrels_xml="";
$wkbkrels_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
$wkbkrels_xml.='<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
$wkbkrels_xml.='<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>';
foreach($this->sheets_meta as $i=>$sheet_meta) {
$wkbkrels_xml.='<Relationship Id="rId'.($i+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/'.($sheet_meta['xmlname']).'"/>';
$wkbkrels_xml = "";
$wkbkrels_xml .= '<?xml version="1.0" encoding="UTF-8"?>'."\n";
$wkbkrels_xml .= '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">';
$wkbkrels_xml .= '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>';
foreach ($this->sheets_meta as $i => $sheet_meta) {
$wkbkrels_xml .= '<Relationship Id="rId'.($i + 2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/'.($sheet_meta['xmlname']).'"/>';
}
if (!empty($this->shared_strings)) {
$wkbkrels_xml.='<Relationship Id="rId'.(count($this->sheets_meta)+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>';
$wkbkrels_xml .= '<Relationship Id="rId'.(count($this->sheets_meta) + 2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/>';
}
$wkbkrels_xml.="\n";
$wkbkrels_xml.='</Relationships>';
$wkbkrels_xml .= "\n";
$wkbkrels_xml .= '</Relationships>';
return $wkbkrels_xml;
}
protected function buildContentTypesXML()
{
$content_types_xml="";
$content_types_xml.='<?xml version="1.0" encoding="UTF-8"?>'."\n";
$content_types_xml.='<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
$content_types_xml.='<Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
$content_types_xml.='<Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
foreach($this->sheets_meta as $i=>$sheet_meta) {
$content_types_xml.='<Override PartName="/xl/worksheets/'.($sheet_meta['xmlname']).'" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
$content_types_xml = "";
$content_types_xml .= '<?xml version="1.0" encoding="UTF-8"?>'."\n";
$content_types_xml .= '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
$content_types_xml .= '<Override PartName="/_rels/.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
$content_types_xml .= '<Override PartName="/xl/_rels/workbook.xml.rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>';
foreach ($this->sheets_meta as $i => $sheet_meta) {
$content_types_xml .= '<Override PartName="/xl/worksheets/'.($sheet_meta['xmlname']).'" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
}
if (!empty($this->shared_strings)) {
$content_types_xml.='<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>';
$content_types_xml .= '<Override PartName="/xl/sharedStrings.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"/>';
}
$content_types_xml.='<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>';
$content_types_xml.='<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>';
$content_types_xml.='<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>';
$content_types_xml.='<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>';
$content_types_xml.="\n";
$content_types_xml.='</Types>';
$content_types_xml .= '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>';
$content_types_xml .= '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/>';
$content_types_xml .= '<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>';
$content_types_xml .= '<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>';
$content_types_xml .= "\n";
$content_types_xml .= '</Types>';
return $content_types_xml;
}
@@ -377,10 +390,10 @@ Class XLSXWriter
public static function xlsCell($row_number, $column_number)
{
$n = $column_number;
for($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
$r = chr($n%26 + 0x41) . $r;
for ($r = ""; $n >= 0; $n = intval($n / 26) - 1) {
$r = chr($n % 26 + 0x41).$r;
}
return $r . ($row_number+1);
return $r.($row_number + 1);
}
//------------------------------------------------------------------
public static function log($string)
@@ -404,26 +417,30 @@ Class XLSXWriter
{
$days = 0; # Number of days since epoch
$seconds = 0; # Time expressed as fraction of 24h hours in seconds
$year=$month=$day=0;
$hour=$min =$sec=0;
$year = $month = $day = 0;
$hour = $min = $sec = 0;
$date_time = $date_input;
if (preg_match("/(\d{4})\-(\d{2})\-(\d{2})/", $date_time, $matches))
{
list($junk,$year,$month,$day) = $matches;
if (preg_match("/(\d{4})\-(\d{2})\-(\d{2})/", $date_time, $matches)) {
list($junk, $year, $month, $day) = $matches;
}
if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $date_time, $matches))
{
list($junk,$hour,$min,$sec) = $matches;
$seconds = ( $hour * 60 * 60 + $min * 60 + $sec ) / ( 24 * 60 * 60 );
if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $date_time, $matches)) {
list($junk, $hour, $min, $sec) = $matches;
$seconds = ($hour * 60 * 60 + $min * 60 + $sec) / (24 * 60 * 60);
}
//using 1900 as epoch, not 1904, ignoring 1904 special case
# Special cases for Excel.
if ("$year-$month-$day"=='1899-12-31') return $seconds ; # Excel 1900 epoch
if ("$year-$month-$day"=='1900-01-00') return $seconds ; # Excel 1900 epoch
if ("$year-$month-$day"=='1900-02-29') return 60 + $seconds ; # Excel false leapday
if ("$year-$month-$day" == '1899-12-31') {
return $seconds ;
} # Excel 1900 epoch
if ("$year-$month-$day" == '1900-01-00') {
return $seconds ;
} # Excel 1900 epoch
if ("$year-$month-$day" == '1900-02-29') {
return 60 + $seconds ;
} # Excel false leapday
# We calculate the date by calculating the number of days since the epoch
# and adjust for the number of leap days. We calculate the number of leap
@@ -435,33 +452,35 @@ Class XLSXWriter
$range = $year - $epoch;
# Set month days and check for leap year.
$leap = (($year % 400 == 0) || (($year % 4 == 0) && ($year % 100)) ) ? 1 : 0;
$mdays = array( 31, ($leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
$leap = (($year % 400 == 0) || (($year % 4 == 0) && ($year % 100))) ? 1 : 0;
$mdays = [ 31, ($leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
# Some boundary checks
if($year < $epoch || $year > 9999) return 0;
if($month < 1 || $month > 12) return 0;
if($day < 1 || $day > $mdays[ $month - 1 ]) return 0;
if ($year < $epoch || $year > 9999) {
return 0;
}
if ($month < 1 || $month > 12) {
return 0;
}
if ($day < 1 || $day > $mdays[ $month - 1 ]) {
return 0;
}
# Accumulate the number of days since the epoch.
$days = $day; # Add days for current month
$days += array_sum( array_slice($mdays, 0, $month-1 ) ); # Add days for past months
$days += array_sum(array_slice($mdays, 0, $month - 1)); # Add days for past months
$days += $range * 365; # Add days for past years
$days += intval( ( $range ) / 4 ); # Add leapdays
$days -= intval( ( $range + $offset ) / 100 ); # Subtract 100 year leapdays
$days += intval( ( $range + $offset + $norm ) / 400 ); # Add 400 year leapdays
$days += intval(($range) / 4); # Add leapdays
$days -= intval(($range + $offset) / 100); # Subtract 100 year leapdays
$days += intval(($range + $offset + $norm) / 400); # Add 400 year leapdays
$days -= $leap; # Already counted above
# Adjust for Excel erroneously treating 1900 as a leap year.
if ($days > 59) { $days++;}
if ($days > 59) {
$days++;
}
return $days + $seconds;
}
//------------------------------------------------------------------
}

View File

@@ -18,7 +18,6 @@
* You should have received a copy of the GNU Affero General Public License
*/
/**
* Checks PHP version
*
@@ -39,7 +38,6 @@ if (PHP_MAJOR_VERSION >= 7) {
exit(-1);
}
define('ITOP_DEFAULT_ENV', 'production');
define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
@@ -60,8 +58,7 @@ if (!isset($bBypassMaintenance)) {
$bBypassMaintenance = isset($_REQUEST['maintenance']) ? boolval($_REQUEST['maintenance']) : false;
}
if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
{
if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance) {
$sTitle = 'Maintenance';
$sMessage = 'This application is currently under maintenance.';
@@ -70,8 +67,7 @@ if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
include(APPROOT.'application/maintenancemsg.php');
$sSAPIName = strtoupper(trim(PHP_SAPI));
switch (true)
{
switch (true) {
case isset($_SERVER['REQUEST_URI']) && EndsWith($_SERVER['REQUEST_URI'], '/pages/ajax.searchform.php'):
_MaintenanceHtmlMessage($sMessage);
break;
@@ -102,6 +98,7 @@ if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
*
* @return bool
*/
function EndsWith($haystack, $needle) {
function EndsWith($haystack, $needle)
{
return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}

View File

@@ -1,2 +1,3 @@
<?php
echo 'Access denied';

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -67,9 +68,8 @@ class DbConnectionWrapper
if (is_null($oMysqli)) {
// Reset to standard connection
static::$oDbCnxMockableForQuery = static::$oDbCnxStandard;
}
else {
} else {
static::$oDbCnxMockableForQuery = $oMysqli;
}
}
}
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Various dev/debug helpers
* TODO: cleanup or at least re-organize
@@ -25,7 +25,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* MyHelpers
*
@@ -35,28 +34,23 @@ class MyHelpers
{
public static function CheckValueInArray($sDescription, $value, $aData)
{
if (!in_array($value, $aData))
{
if (!in_array($value, $aData)) {
self::HandleWrongValue($sDescription, $value, $aData);
}
}
public static function CheckKeyInArray($sDescription, $key, $aData)
{
if (!array_key_exists($key, $aData))
{
if (!array_key_exists($key, $aData)) {
self::HandleWrongValue($sDescription, $key, array_keys($aData));
}
}
public static function HandleWrongValue($sDescription, $value, $aData)
{
if (count($aData) == 0)
{
if (count($aData) == 0) {
$sArrayDesc = "{}";
}
else
{
} else {
$sArrayDesc = "{".implode(", ", $aData)."}";
}
// exit!
@@ -64,7 +58,7 @@ class MyHelpers
}
// getmicrotime()
// format sss.mmmuuupppnnn
// format sss.mmmuuupppnnn
public static function getmicrotime()
{
return microtime(true);
@@ -74,14 +68,15 @@ class MyHelpers
* MakeSQLComment
* converts hash into text comment which we can use in a (mySQL) query
*/
public static function MakeSQLComment ($aHash)
public static function MakeSQLComment($aHash)
{
if (empty($aHash)) return "";
if (empty($aHash)) {
return "";
}
$sComment = "";
{
foreach($aHash as $sKey=>$sValue)
{
$sComment .= "\n-- ". $sKey ."=>" . $sValue;
foreach ($aHash as $sKey => $sValue) {
$sComment .= "\n-- ".$sKey."=>".$sValue;
}
}
return $sComment;
@@ -90,12 +85,9 @@ class MyHelpers
public static function var_dump_html($aWords, $bFullDisplay = false)
{
echo "<pre>\n";
if ($bFullDisplay)
{
if ($bFullDisplay) {
print_r($aWords); // full dump!
}
else
{
} else {
var_dump($aWords); // truncate things when they are too big
}
echo "\n</pre>\n";
@@ -123,9 +115,10 @@ class MyHelpers
{
$aLines1 = explode("\n", $s1);
$aLines2 = explode("\n", $s2);
for ($i = 0 ; $i < min(count($aLines1), count($aLines2)) ; $i++)
{
if ($aLines1[$i] != $aLines2[$i]) return $i;
for ($i = 0 ; $i < min(count($aLines1), count($aLines2)) ; $i++) {
if ($aLines1[$i] != $aLines2[$i]) {
return $i;
}
}
return false;
}
@@ -142,9 +135,10 @@ class MyHelpers
// do not work fine with multiline strings
$iLen1 = strlen($s1);
$iLen2 = strlen($s2);
for ($i = 0 ; $i < min($iLen1, $iLen2) ; $i++)
{
if ($s1[$i] !== $s2[$i]) return $i;
for ($i = 0 ; $i < min($iLen1, $iLen2) ; $i++) {
if ($s1[$i] !== $s2[$i]) {
return $i;
}
}
return false;
}
@@ -154,9 +148,10 @@ class MyHelpers
// do not work fine with multiline strings
$iLen1 = strlen($s1);
$iLen2 = strlen($s2);
for ($i = 0 ; $i < min(strlen($s1), strlen($s2)) ; $i++)
{
if ($s1[$iLen1 - $i - 1] !== $s2[$iLen2 - $i - 1]) return array($iLen1 - $i, $iLen2 - $i);
for ($i = 0 ; $i < min(strlen($s1), strlen($s2)) ; $i++) {
if ($s1[$iLen1 - $i - 1] !== $s2[$iLen2 - $i - 1]) {
return [$iLen1 - $i, $iLen2 - $i];
}
}
return false;
}
@@ -177,8 +172,7 @@ class MyHelpers
protected static function string_cmp_html($s1, $s2, $sHighlight)
{
$iDiffPos = self::first_diff($s1, $s2);
if ($iDiffPos === false)
{
if ($iDiffPos === false) {
echo "strings are identical";
return;
}
@@ -189,7 +183,7 @@ class MyHelpers
$sMiddle1 = substr($s1, $iDiffPos, $aLastDiff[0] - $iDiffPos);
$sMiddle2 = substr($s2, $iDiffPos, $aLastDiff[1] - $iDiffPos);
echo "<p>$sStart<span style=\"$sHighlight\">$sMiddle1</span>$sEnd</p>\n";
echo "<p>$sStart<span style=\"$sHighlight\">$sMiddle2</span>$sEnd</p>\n";
}
@@ -203,40 +197,34 @@ class MyHelpers
public static function var_cmp_html($var1, $var2, $sHighlight = 'color:red; font-weight:bold;')
{
if (is_object($var1))
{
if (is_object($var1)) {
return self::object_cmp_html($var1, $var2, $sHighlight);
}
else if (count(explode("\n", $var1)) > 1)
{
} elseif (count(explode("\n", $var1)) > 1) {
// multiline string
return self::text_cmp_html($var1, $var2, $sHighlight);
}
else
{
} else {
return self::string_cmp_html($var1, $var2, $sHighlight);
}
}
public static function get_callstack($iLevelsToIgnore = 0, $aCallStack = null)
{
if ($aCallStack == null) $aCallStack = debug_backtrace();
if ($aCallStack == null) {
$aCallStack = debug_backtrace();
}
$aCallStack = array_slice($aCallStack, $iLevelsToIgnore);
$aDigestCallStack = array();
$bFirstLine = true;
foreach ($aCallStack as $aCallInfo)
{
$aDigestCallStack = [];
$bFirstLine = true;
foreach ($aCallStack as $aCallInfo) {
$sLine = empty($aCallInfo['line']) ? "" : $aCallInfo['line'];
$sFile = empty($aCallInfo['file']) ? "" : $aCallInfo['file'];
if ($sFile != '')
{
if ($sFile != '') {
$sFile = str_replace('\\', '/', $sFile);
$sAppRoot = str_replace('\\', '/', APPROOT);
$iPos = strpos($sFile, $sAppRoot);
if ($iPos !== false)
{
if ($iPos !== false) {
$sFile = substr($sFile, strlen($sAppRoot));
}
}
@@ -244,55 +232,51 @@ class MyHelpers
$sType = empty($aCallInfo['type']) ? "" : $aCallInfo['type'];
$sFunction = empty($aCallInfo['function']) ? "" : $aCallInfo['function'];
if ($bFirstLine)
{
if ($bFirstLine) {
$bFirstLine = false;
// For this line do not display the "function name" because
// that will be the name of our error handler for sure !
$sFunctionInfo = "N/A";
}
else
{
} else {
$args = '';
if (empty($aCallInfo['args'])) $aCallInfo['args'] = array();
foreach ($aCallInfo['args'] as $a)
{
if (!empty($args))
{
if (empty($aCallInfo['args'])) {
$aCallInfo['args'] = [];
}
foreach ($aCallInfo['args'] as $a) {
if (!empty($args)) {
$args .= ', ';
}
switch (gettype($a))
{
switch (gettype($a)) {
case 'integer':
case 'double':
$args .= $a;
break;
$args .= $a;
break;
case 'string':
$a = Str::pure2html(self::beautifulstr($a, 64, true, false));
$args .= "\"$a\"";
break;
case 'array':
$args .= 'array('.count($a).')';
break;
case 'object':
$args .= 'Object('.get_class($a).')';
break;
case 'resource':
$args .= 'Resource('.strstr($a, '#').')';
break;
case 'boolean':
$args .= $a ? 'true' : 'false';
break;
case 'NULL':
$args .= 'null';
break;
default:
$args .= 'Unknown';
$a = Str::pure2html(self::beautifulstr($a, 64, true, false));
$args .= "\"$a\"";
break;
case 'array':
$args .= 'array('.count($a).')';
break;
case 'object':
$args .= 'Object('.get_class($a).')';
break;
case 'resource':
$args .= 'Resource('.strstr($a, '#').')';
break;
case 'boolean':
$args .= $a ? 'true' : 'false';
break;
case 'NULL':
$args .= 'null';
break;
default:
$args .= 'Unknown';
}
}
$sFunctionInfo = "$sClass$sType$sFunction($args)";
}
$aDigestCallStack[] = array('File'=>$sFile, 'Line'=>$sLine, 'Function'=>$sFunctionInfo);
$aDigestCallStack[] = ['File' => $sFile, 'Line' => $sLine, 'Function' => $sFunctionInfo];
}
return $aDigestCallStack;
}
@@ -311,9 +295,8 @@ class MyHelpers
public static function get_callstack_text($iLevelsToIgnore = 0, $aCallStack = null)
{
$aDigestCallStack = self::get_callstack($iLevelsToIgnore, $aCallStack);
$aRes = array();
foreach ($aDigestCallStack as $aCall)
{
$aRes = [];
foreach ($aDigestCallStack as $aCall) {
$aRes[] = $aCall['File'].' at '.$aCall['Line'].', '.$aCall['Function'];
}
return implode("\n", $aRes);
@@ -325,21 +308,27 @@ class MyHelpers
///////////////////////////////////////////////////////////////////////////////
public static function make_table_from_assoc_array(&$aData)
{
if (!is_array($aData)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not an array");
if (!is_array($aData)) {
throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not an array");
}
$aFirstRow = reset($aData);
if (count($aData) == 0) return '';
if (!is_array($aFirstRow)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array");
if (count($aData) == 0) {
return '';
}
if (!is_array($aFirstRow)) {
throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array");
}
$sOutput = "";
$sOutput .= "<TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"1\" CELLPADDING=\"1\">\n";
// Table header
//
$sOutput .= " <TR CLASS=celltitle>\n";
foreach ($aFirstRow as $fieldname=>$trash) {
foreach ($aFirstRow as $fieldname => $trash) {
$sOutput .= " <TD><B>".$fieldname."</B></TD>\n";
}
$sOutput .= " </TR>\n";
// Table contents
//
$iCount = 0;
@@ -354,7 +343,7 @@ class MyHelpers
}
$sOutput .= " </TR>\n";
}
$sOutput .= "</TABLE>\n";
return $sOutput;
}
@@ -368,7 +357,9 @@ class MyHelpers
}
public static function debug_breakpoint_notempty($arg)
{
if (empty($arg)) return;
if (empty($arg)) {
return;
}
echo "<H1> Debug breakpoint (triggered on non-empty value) </H1>\n";
MyHelpers::var_dump_html($arg);
MyHelpers::dump_callstack();
@@ -381,7 +372,7 @@ class MyHelpers
*/
public static function xmlentities($string)
{
return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;' ), $string );
return str_replace([ '&', '"', "'", '<', '>' ], [ '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;' ], $string);
}
/**
@@ -390,20 +381,24 @@ class MyHelpers
*/
public static function xmlencode($string)
{
return xmlentities(iconv("UTF-8", "UTF-8//IGNORE",$string));
return xmlentities(iconv("UTF-8", "UTF-8//IGNORE", $string));
}
///////////////////////////////////////////////////////////////////////////////
// Source: New - format strings for output
// Last modif: 2005/01/18 RQU
///////////////////////////////////////////////////////////////////////////////
public static function beautifulstr($sLongString, $iMaxLen, $bShowLen=false, $bShowTooltip=true)
public static function beautifulstr($sLongString, $iMaxLen, $bShowLen = false, $bShowTooltip = true)
{
if (!is_string($sLongString)) throw new CoreException("beautifulstr: expect a string as 1st argument");
if (!is_string($sLongString)) {
throw new CoreException("beautifulstr: expect a string as 1st argument");
}
// Nothing to do if the string is short
if (strlen($sLongString) <= $iMaxLen) return $sLongString;
if (strlen($sLongString) <= $iMaxLen) {
return $sLongString;
}
// Truncate the string
$sSuffix = "...";
if ($bShowLen) {
@@ -411,7 +406,7 @@ class MyHelpers
}
$sOutput = substr($sLongString, 0, $iMaxLen - strlen($sSuffix)).$sSuffix;
$sOutput = htmlspecialchars($sOutput);
// Add tooltip if required
//if ($bShowTooltip) {
// $oTooltip = new gui_tooltip($sLongString);
@@ -427,11 +422,11 @@ Utility class: static methods for cleaning & escaping untrusted (i.e.
user-supplied) strings.
Any string can (usually) be thought of as being in one of these 'modes':
pure = what the user actually typed / what you want to see on the page /
what is actually stored in the DB
what is actually stored in the DB
gpc = incoming GET, POST or COOKIE data
sql = escaped for passing safely to RDBMS via SQL (also, data from DB
queries and file reads if you have magic_quotes_runtime on--which
is rare)
queries and file reads if you have magic_quotes_runtime on--which
is rare)
html = safe for html display (htmlentities applied)
Always knowing what mode your string is in--using these methods to
convert between modes--will prevent SQL injection and cross-site scripting.
@@ -456,8 +451,11 @@ class Str
}
public static function gpc2pure($gpc)
{
if (ini_get('magic_quotes_sybase')) $pure = str_replace("''", "'", $gpc);
else $pure = $gpc;
if (ini_get('magic_quotes_sybase')) {
$pure = str_replace("''", "'", $gpc);
} else {
$pure = $gpc;
}
return $pure;
}
public static function html2pure($html)
@@ -477,7 +475,9 @@ class Str
}
public static function pure2sql($pure, $maxLength = false)
{
if ($maxLength) $pure = substr($pure, 0, $maxLength);
if ($maxLength) {
$pure = substr($pure, 0, $maxLength);
}
return (STR_SYBASE)
? str_replace("'", "''", $pure)
: addslashes($pure);
@@ -485,7 +485,9 @@ class Str
public static function sql2html($sql, $maxLength = false)
{
$pure = self::sql2pure($sql);
if ($maxLength) $pure = substr($pure, 0, $maxLength);
if ($maxLength) {
$pure = substr($pure, 0, $maxLength);
}
return self::pure2html($pure);
}
public static function sql2pure($sql)
@@ -507,7 +509,7 @@ class Str
protected static function xmlentities($string)
{
return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;' ), $string );
return str_replace([ '&', '"', "'", '<', '>' ], [ '&amp;' , '&quot;', '&apos;' , '&lt;' , '&gt;' ], $string);
}
/**
@@ -516,7 +518,7 @@ class Str
*/
protected static function xmlencode($string)
{
return self::xmlentities(iconv("UTF-8", "UTF-8//IGNORE",$string));
return self::xmlentities(iconv("UTF-8", "UTF-8//IGNORE", $string));
}
public static function islowcase($sString)

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -17,64 +18,64 @@ class TemporaryObjectDescriptor extends DBObject
{
public static function Init()
{
$aParams = array(
$aParams = [
'category' => 'core',
'key_type' => 'autoincrement',
'name_attcode' => array('item_class', 'temp_id'),
'name_attcode' => ['item_class', 'temp_id'],
'image_attcode' => '',
'state_attcode' => '',
'reconc_keys' => array(''),
'reconc_keys' => [''],
'db_table' => 'priv_temporary_object_descriptor',
'db_key_field' => 'id',
'db_finalclass_field' => '',
'style' => new ormStyle(null, null, null, null, null, null),
'indexes' => array(
'indexes' => [
1 =>
array(
[
0 => 'temp_id',
),
],
2 =>
array(
[
0 => 'item_class',
1 => 'item_id',
),
),
);
],
],
];
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime('expiration_date', array('sql' => 'expiration_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('temp_id', array('sql' => 'temp_id', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('item_class', array('sql' => 'item_class', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeObjectKey('item_id', array('class_attcode' => 'item_class', 'sql' => 'item_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeDateTime('creation_date', array('sql' => 'creation_date', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('host_class', array('sql' => 'host_class', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeObjectKey('host_id', array('class_attcode' => 'host_class', 'sql' => 'host_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('host_att_code', array('sql' => 'host_att_code', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeEnum("operation", array("allowed_values" => new ValueSetEnum('create,delete'), "sql" => "operation", "default_value" => "create", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeDateTime('expiration_date', ['sql' => 'expiration_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]));
MetaModel::Init_AddAttribute(new AttributeString('temp_id', ['sql' => 'temp_id', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]));
MetaModel::Init_AddAttribute(new AttributeString('item_class', ['sql' => 'item_class', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]));
MetaModel::Init_AddAttribute(new AttributeObjectKey('item_id', ['class_attcode' => 'item_class', 'sql' => 'item_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]));
MetaModel::Init_AddAttribute(new AttributeDateTime('creation_date', ['sql' => 'creation_date', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]));
MetaModel::Init_AddAttribute(new AttributeString('host_class', ['sql' => 'host_class', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]));
MetaModel::Init_AddAttribute(new AttributeObjectKey('host_id', ['class_attcode' => 'host_class', 'sql' => 'host_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]));
MetaModel::Init_AddAttribute(new AttributeString('host_att_code', ['sql' => 'host_att_code', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => [], 'always_load_in_tables' => false]));
MetaModel::Init_AddAttribute(new AttributeEnum("operation", ["allowed_values" => new ValueSetEnum('create,delete'), "sql" => "operation", "default_value" => "create", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_SetZListItems('details', array(
MetaModel::Init_SetZListItems('details', [
0 => 'temp_id',
1 => 'item_class',
2 => 'item_id',
3 => 'creation_date',
4 => 'expiration_date',
5 => 'meta',
));
MetaModel::Init_SetZListItems('standard_search', array(
]);
MetaModel::Init_SetZListItems('standard_search', [
0 => 'temp_id',
1 => 'item_class',
2 => 'item_id',
));
MetaModel::Init_SetZListItems('list', array(
]);
MetaModel::Init_SetZListItems('list', [
0 => 'temp_id',
1 => 'item_class',
2 => 'item_id',
3 => 'creation_date',
4 => 'expiration_date',
));;
]);
;
}
public function DBInsertNoReload()
{
$this->SetCurrentDateIfNull('creation_date');
@@ -82,7 +83,6 @@ class TemporaryObjectDescriptor extends DBObject
return parent::DBInsertNoReload();
}
/**
* Set/Update all of the '_item' fields
*

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -30,12 +31,11 @@ use Combodo\iTop\Service\Router\Router;
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/core/asynctask.class.inc.php');
require_once(APPROOT.'/core/email.class.inc.php');
/**
* A user defined action, to customize the application
* A user defined action, to customize the application
*
* @package iTopORM
*/
@@ -47,8 +47,8 @@ abstract class Action extends cmdbAbstractObject
*/
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
@@ -59,14 +59,14 @@ abstract class Action extends cmdbAbstractObject
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"style" => new ormStyle("ibo-dm-class--Action", "ibo-dm-class-alt--Action", "var(--ibo-dm-class--Action--main-color)", "var(--ibo-dm-class--Action--complementary-color)", null, '../images/icons/icons8-in-transit.svg'),
);
];
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values" => null, "sql" => "name", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("name", ["allowed_values" => null, "sql" => "name", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("description", ["allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array(
"allowed_values" => new ValueSetEnum(array('test' => 'Being tested', 'enabled' => 'In production', 'disabled' => 'Inactive')),
MetaModel::Init_AddAttribute(new AttributeEnum("status", [
"allowed_values" => new ValueSetEnum(['test' => 'Being tested', 'enabled' => 'In production', 'disabled' => 'Inactive']),
"styled_values" => [
'test' => new ormStyle('ibo-dm-enum--Action-status-test', 'ibo-dm-enum-alt--Action-status-test', 'var(--ibo-dm-enum--Action-status-test--main-color)', 'var(--ibo-dm-enum--Action-status-test--complementary-color)', null, null),
'enabled' => new ormStyle('ibo-dm-enum--Action-status-enabled', 'ibo-dm-enum-alt--Action-status-enabled', 'var(--ibo-dm-enum--Action-status-enabled--main-color)', 'var(--ibo-dm-enum--Action-status-enabled--complementary-color)', 'fas fa-check', null),
@@ -76,21 +76,23 @@ abstract class Action extends cmdbAbstractObject
"sql" => "status",
"default_value" => "test",
"is_null_allowed" => false,
"depends_on" => array(),
)));
"depends_on" => [],
]));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list",
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "action_id", "ext_key_to_remote" => "trigger_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
MetaModel::Init_AddAttribute(new AttributeEnum("asynchronous", array("allowed_values" => new ValueSetEnum(['use_global_setting' => 'Use global settings','yes' => 'Yes' ,'no' => 'No']), "sql" => "asynchronous", "default_value" => 'use_global_setting', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect(
"trigger_list",
["linked_class" => "lnkTriggerAction", "ext_key_to_me" => "action_id", "ext_key_to_remote" => "trigger_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => [], "display_style" => 'property']
));
MetaModel::Init_AddAttribute(new AttributeEnum("asynchronous", ["allowed_values" => new ValueSetEnum(['use_global_setting' => 'Use global settings','yes' => 'Yes' ,'no' => 'No']), "sql" => "asynchronous", "default_value" => 'use_global_setting', "is_null_allowed" => false, "depends_on" => []]));
// Display lists
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list'));
MetaModel::Init_SetZListItems('details', ['name', 'description', 'status', 'trigger_list']);
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status'));
MetaModel::Init_SetZListItems('list', ['finalclass', 'name', 'description', 'status']);
// Search criteria
// - Default criteria of the search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status'));
MetaModel::Init_SetZListItems('default_search', ['name', 'description', 'status']);
}
@@ -111,8 +113,7 @@ abstract class Action extends cmdbAbstractObject
*/
public function IsActive()
{
switch($this->Get('status'))
{
switch ($this->Get('status')) {
case 'enabled':
case 'test':
return true;
@@ -131,8 +132,7 @@ abstract class Action extends cmdbAbstractObject
*/
public function IsBeingTested()
{
switch($this->Get('status'))
{
switch ($this->Get('status')) {
case 'test':
return true;
@@ -220,7 +220,7 @@ abstract class Action extends cmdbAbstractObject
$iLastExecutionDays = $oConfig->Get($sLastExecutionDaysConfigParamName);
if ($iLastExecutionDays < 0) {
throw new InvalidConfigParamException("Invalid value for {$sLastExecutionDaysConfigParamName} config parameter. Param desc: " . $oConfig->GetDescription($sLastExecutionDaysConfigParamName));
throw new InvalidConfigParamException("Invalid value for {$sLastExecutionDaysConfigParamName} config parameter. Param desc: ".$oConfig->GetDescription($sLastExecutionDaysConfigParamName));
}
$sActionQueryOql = 'SELECT EventNotification WHERE action_id = :action_id';
@@ -250,7 +250,7 @@ abstract class Action extends cmdbAbstractObject
*/
public static function GetAsynchronousGlobalSetting(): bool
{
return false;
return false;
}
/**
@@ -270,7 +270,7 @@ abstract class Action extends cmdbAbstractObject
}
/**
* A notification
* A notification
*
* @package iTopORM
*/
@@ -282,8 +282,8 @@ abstract class ActionNotification extends Action
*/
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
@@ -293,22 +293,22 @@ abstract class ActionNotification extends Action
"db_table" => "priv_action_notification",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list'));
MetaModel::Init_SetZListItems('details', ['name', 'description', 'status', 'trigger_list']);
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('finalclass', 'description', 'status'));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_SetZListItems('list', ['finalclass', 'description', 'status']);
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", ["sql" => "language", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
// Search criteria
// - Criteria of the std search form
// MetaModel::Init_SetZListItems('standard_search', array('name'));
// MetaModel::Init_SetZListItems('standard_search', array('name'));
// - Default criteria of the search form
// MetaModel::Init_SetZListItems('default_search', array('name'));
// MetaModel::Init_SetZListItems('default_search', array('name'));
}
/**
@@ -321,7 +321,8 @@ abstract class ActionNotification extends Action
* @throws \DictExceptionUnknownLanguage
* @since 3.2.0
*/
public function SetNotificationLanguage($sLanguage = null, $sLanguageCode = null){
public function SetNotificationLanguage($sLanguage = null, $sLanguageCode = null)
{
$sPreviousLanguage = Dict::GetUserLanguage();
$aPreviousPluginProperties = ApplicationContext::GetPluginProperties('QueryLocalizerPlugin');
$sLanguage = $sLanguage ?? $this->Get('language');
@@ -338,7 +339,7 @@ abstract class ActionNotification extends Action
}
/**
* An email notification
* An email notification
*
* @package iTopORM
*/
@@ -348,69 +349,68 @@ class ActionEmail extends ActionNotification
* @var string
* @since 3.0.1
*/
const ENUM_HEADER_NAME_MESSAGE_ID = 'Message-ID';
public const ENUM_HEADER_NAME_MESSAGE_ID = 'Message-ID';
/**
* @var string
* @since 3.0.1
*/
const ENUM_HEADER_NAME_REFERENCES = 'References';
public const ENUM_HEADER_NAME_REFERENCES = 'References';
/**
* @var string
* @since 3.1.0
*/
const TEMPLATE_BODY_CONTENT = '$content$';
public const TEMPLATE_BODY_CONTENT = '$content$';
/**
* Wraps the 'body' of the message for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
* @since 3.1.0
*/
const CONTENT_HIGHLIGHT = '<div style="border:2px dashed #6800ff;position:relative;padding:2px;margin-top:14px;"><div style="background-color:#6800ff;color:#fff;font-family:Courier New, sans-serif;font-size:14px;line-height:16px;padding:3px;display:block;position:absolute;top:-22px;right:0;">$content$</div>%s</div>';
public const CONTENT_HIGHLIGHT = '<div style="border:2px dashed #6800ff;position:relative;padding:2px;margin-top:14px;"><div style="background-color:#6800ff;color:#fff;font-family:Courier New, sans-serif;font-size:14px;line-height:16px;padding:3px;display:block;position:absolute;top:-22px;right:0;">$content$</div>%s</div>';
/**
* Wraps a placeholder of the email's body for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
*/
const FIELD_HIGHLIGHT = '<span style="background-color:#6800ff;color:#fff;font-size:smaller;font-family:Courier New, sans-serif;padding:2px;">\\$$1\\$</span>';
public const FIELD_HIGHLIGHT = '<span style="background-color:#6800ff;color:#fff;font-size:smaller;font-family:Courier New, sans-serif;padding:2px;">\\$$1\\$</span>';
/**
* @inheritDoc
*/
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "grant_by_profile,core/cmdb,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"reconc_keys" => ['name'],
"db_table" => "priv_action_email",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-mailing.svg'),
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeEmailAddress("test_recipient", array("allowed_values" => null, "sql" => "test_recipient", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEmailAddress("test_recipient", ["allowed_values" => null, "sql" => "test_recipient", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("from", array("allowed_values" => null, "sql" => "from", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("from_label", array("allowed_values" => null, "sql" => "from_label", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("reply_to", array("allowed_values" => null, "sql" => "reply_to", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("reply_to_label", array("allowed_values" => null, "sql" => "reply_to_label", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("to", array("allowed_values" => null, "sql" => "to", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("cc", array("allowed_values" => null, "sql" => "cc", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("bcc", array("allowed_values" => null, "sql" => "bcc", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", array("allowed_values" => null, "sql" => "subject", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTemplateHTML("body", array("allowed_values" => null, "sql" => "body", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("allowed_values" => new ValueSetEnum('low,normal,high'), "sql" => "importance", "default_value" => 'normal', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeBlob("html_template", array("is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeEnum("ignore_notify", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "ignore_notify", "default_value" => 'yes', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("from", ["allowed_values" => null, "sql" => "from", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("from_label", ["allowed_values" => null, "sql" => "from_label", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("reply_to", ["allowed_values" => null, "sql" => "reply_to", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("reply_to_label", ["allowed_values" => null, "sql" => "reply_to_label", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeOQL("to", ["allowed_values" => null, "sql" => "to", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeOQL("cc", ["allowed_values" => null, "sql" => "cc", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeOQL("bcc", ["allowed_values" => null, "sql" => "bcc", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", ["allowed_values" => null, "sql" => "subject", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeTemplateHTML("body", ["allowed_values" => null, "sql" => "body", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("importance", ["allowed_values" => new ValueSetEnum('low,normal,high'), "sql" => "importance", "default_value" => 'normal', "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeBlob("html_template", ["is_null_allowed" => true, "depends_on" => [], "always_load_in_tables" => false]));
MetaModel::Init_AddAttribute(new AttributeEnum("ignore_notify", ["allowed_values" => new ValueSetEnum('yes,no'), "sql" => "ignore_notify", "default_value" => 'yes', "is_null_allowed" => false, "depends_on" => []]));
// Display lists
// - Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array(
'col:col1' => array(
'fieldset:ActionEmail:main' => array(
MetaModel::Init_SetZListItems('details', [
'col:col1' => [
'fieldset:ActionEmail:main' => [
0 => 'name',
1 => 'description',
2 => 'status',
@@ -419,14 +419,14 @@ class ActionEmail extends ActionNotification
5 => 'subject',
6 => 'body',
// 5 => 'importance', not handled when sending the mail, better hide it then
),
'fieldset:ActionEmail:trigger' => array(
],
'fieldset:ActionEmail:trigger' => [
0 => 'trigger_list',
1 => 'asynchronous'
),
),
'col:col2' => array(
'fieldset:ActionEmail:recipients' => array(
1 => 'asynchronous',
],
],
'col:col2' => [
'fieldset:ActionEmail:recipients' => [
0 => 'from',
1 => 'from_label',
2 => 'reply_to',
@@ -436,17 +436,17 @@ class ActionEmail extends ActionNotification
6 => 'to',
7 => 'cc',
8 => 'bcc',
),
),
));
],
],
]);
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('status', 'to', 'subject', 'language'));
MetaModel::Init_SetZListItems('list', ['status', 'to', 'subject', 'language']);
// Search criteria
// - Standard criteria of the search
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'status', 'subject', 'language'));
MetaModel::Init_SetZListItems('standard_search', ['name', 'description', 'status', 'subject', 'language']);
// - Default criteria for the search
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status', 'subject', 'language'));
MetaModel::Init_SetZListItems('default_search', ['name', 'description', 'status', 'subject', 'language']);
}
// count the recipients found
@@ -477,10 +477,11 @@ class ActionEmail extends ActionNotification
{
$oTrigger = $aArgs['trigger->object()'] ?? null;
$sOQL = $this->Get($sRecipAttCode);
if (utils::IsNullOrEmptyString($sOQL)) return '';
if (utils::IsNullOrEmptyString($sOQL)) {
return '';
}
try
{
try {
$oSearch = DBObjectSearch::FromOQL($sOQL);
if ($this->Get('ignore_notify') === 'no') {
// In theory, it is possible to notify *any* kind of object,
@@ -492,31 +493,26 @@ class ActionEmail extends ActionNotification
}
}
$oSearch->AllowAllData();
}
catch (OQLException $e)
{
} catch (OQLException $e) {
$this->m_aMailErrors[] = "query syntax error for recipient '$sRecipAttCode'";
return $e->getMessage();
}
$sClass = $oSearch->GetClass();
// Determine the email attribute (the first one will be our choice)
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
if ($oAttDef instanceof AttributeEmailAddress)
{
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
if ($oAttDef instanceof AttributeEmailAddress) {
$sEmailAttCode = $sAttCode;
// we've got one, exit the loop
break;
}
}
if (!isset($sEmailAttCode))
{
if (!isset($sEmailAttCode)) {
$this->m_aMailErrors[] = "wrong target for recipient '$sRecipAttCode'";
return "The objects of the class '$sClass' do not have any email attribute";
}
if($oTrigger !== null && in_array('Contact', MetaModel::EnumParentClasses($sClass, ENUM_CHILD_CLASSES_ALL), true)) {
if ($oTrigger !== null && in_array('Contact', MetaModel::EnumParentClasses($sClass, ENUM_CHILD_CLASSES_ALL), true)) {
$aArgs['trigger_id'] = $oTrigger->GetKey();
$aArgs['action_id'] = $this->GetKey();
@@ -526,13 +522,11 @@ class ActionEmail extends ActionNotification
$oSearch->AddConditionExpression(Expression::FromOQL("`$sAlias`.id NOT IN ($sSubscribedContactsOQL)"));
}
$oSet = new DBObjectSet($oSearch, array() /* order */, $aArgs);
$aRecipients = array();
while ($oObj = $oSet->Fetch())
{
$oSet = new DBObjectSet($oSearch, [] /* order */, $aArgs);
$aRecipients = [];
while ($oObj = $oSet->Fetch()) {
$sAddress = trim($oObj->Get($sEmailAttCode));
if (utils::IsNotNullOrEmptyString($sAddress))
{
if (utils::IsNotNullOrEmptyString($sAddress)) {
$aRecipients[] = $sAddress;
$this->m_iRecipients++;
}
@@ -551,15 +545,11 @@ class ActionEmail extends ActionNotification
*/
public function DoExecute($oTrigger, $aContextArgs)
{
if (MetaModel::IsLogEnabledNotification())
{
if (MetaModel::IsLogEnabledNotification()) {
$oLog = new EventNotificationEmail();
if ($this->IsBeingTested())
{
if ($this->IsBeingTested()) {
$oLog->Set('message', 'TEST - Notification sent ('.$this->Get('test_recipient').')');
}
else
{
} else {
$oLog->Set('message', 'Notification pending');
}
$oLog->Set('userinfo', UserRights::GetUser());
@@ -570,49 +560,36 @@ class ActionEmail extends ActionNotification
// Must be inserted now so that it gets a valid id that will make the link
// between an eventual asynchronous task (queued) and the log
$oLog->DBInsertNoReload();
}
else
{
} else {
$oLog = null;
}
try
{
try {
$sRes = $this->_DoExecute($oTrigger, $aContextArgs, $oLog);
if ($this->IsBeingTested())
{
if ($this->IsBeingTested()) {
$sPrefix = 'TEST ('.$this->Get('test_recipient').') - ';
}
else
{
} else {
$sPrefix = '';
}
if ($oLog)
{
$oLog->Set('message', $sPrefix . $sRes);
$oLog->DBUpdate();
}
if ($oLog) {
$oLog->Set('message', $sPrefix.$sRes);
$oLog->DBUpdate();
}
}
catch (Exception $e)
{
if ($oLog)
{
} catch (Exception $e) {
if ($oLog) {
$oLog->Set('message', 'Error: '.$e->getMessage());
try
{
$oLog->DBUpdate();
}
catch (Exception $eSecondTryUpdate)
{
IssueLog::Error("Failed to process email ".$oLog->GetKey()." - reason: ".$e->getMessage()."\nTrace:\n".$e->getTraceAsString());
try {
$oLog->DBUpdate();
} catch (Exception $eSecondTryUpdate) {
IssueLog::Error("Failed to process email ".$oLog->GetKey()." - reason: ".$e->getMessage()."\nTrace:\n".$e->getTraceAsString());
$oLog->Set('message', 'Error: more details in the log for email "'.$oLog->GetKey().'"');
$oLog->DBUpdate();
}
$oLog->Set('message', 'Error: more details in the log for email "'.$oLog->GetKey().'"');
$oLog->DBUpdate();
}
}
}
@@ -629,11 +606,11 @@ class ActionEmail extends ActionNotification
*/
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
$sStyles = file_get_contents(APPROOT . utils::GetCSSFromSASS("css/email.scss"));
$sStyles = file_get_contents(APPROOT.utils::GetCSSFromSASS("css/email.scss"));
$sStyles .= MetaModel::GetConfig()->Get('email_css');
$oEmail = new EMail();
$aEmailContent = $this->PrepareMessageContent($aContextArgs, $oLog);
$oEmail->SetSubject($aEmailContent['subject']);
$oEmail->SetBody($aEmailContent['body'], 'text/html', $sStyles);
@@ -645,23 +622,18 @@ class ActionEmail extends ActionNotification
$oEmail->SetReferences($aEmailContent['references']);
$oEmail->SetMessageId($aEmailContent['message_id']);
$oEmail->SetInReplyTo($aEmailContent['in_reply_to']);
foreach($aEmailContent['attachments'] as $aAttachment) {
foreach ($aEmailContent['attachments'] as $aAttachment) {
$oEmail->AddAttachment($aAttachment['data'], $aAttachment['filename'], $aAttachment['mime_type']);
}
if (empty($this->m_aMailErrors))
{
if ($this->m_iRecipients == 0)
{
if (empty($this->m_aMailErrors)) {
if ($this->m_iRecipients == 0) {
return 'No recipient';
}
else
{
} else {
$aErrors = [];
$iRes = $oEmail->Send($aErrors, $this->IsAsynchronous() ? Email::ENUM_SEND_FORCE_ASYNCHRONOUS : Email::ENUM_SEND_FORCE_SYNCHRONOUS, $oLog);
switch ($iRes)
{
switch ($iRes) {
case EMAIL_SEND_OK:
return "Sent";
@@ -721,39 +693,36 @@ class ActionEmail extends ActionNotification
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
[$sPreviousLanguage, $aPreviousPluginProperties] = $this->SetNotificationLanguage();
try
{
try {
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
$this->m_aMailErrors = [];
// Determine recipients
//
$aMessageContent['to'] = $this->FindRecipients('to', $aContextArgs);
$aMessageContent['cc'] = $this->FindRecipients('cc', $aContextArgs);
$aMessageContent['bcc'] = $this->FindRecipients('bcc', $aContextArgs);
$aMessageContent['from'] = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$aMessageContent['from_label'] = MetaModel::ApplyParams($this->Get('from_label'), $aContextArgs);
$aMessageContent['reply_to'] = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$aMessageContent['reply_to_label'] = MetaModel::ApplyParams($this->Get('reply_to_label'), $aContextArgs);
$aMessageContent['subject'] = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = $this->BuildMessageBody(false);
$aMessageContent['body'] = MetaModel::ApplyParams($sBody, $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$aMessageContent['message_id'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
$aMessageContent['references'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
}
catch (Exception $e) {
} catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
}
finally {
} finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
$this->SetNotificationLanguage($sPreviousLanguage, $aPreviousPluginProperties['language_code'] ?? null);
}
if (!is_null($oLog)) {
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
@@ -802,19 +771,17 @@ class ActionEmail extends ActionNotification
}
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
$aMessageContent['in_reply_to'] = $aMessageContent['references'];
if (isset($aContextArgs['attachments']))
{
$aAttachmentReport = array();
if (isset($aContextArgs['attachments'])) {
$aAttachmentReport = [];
/** @var \ormDocument $oDocument */
foreach($aContextArgs['attachments'] as $oDocument)
{
foreach ($aContextArgs['attachments'] as $oDocument) {
$aMessageContent['attachments'][] = ['data' => $oDocument->GetData(), 'filename' => $oDocument->GetFileName(), 'mime_type' => $oDocument->GetMimeType()];
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData() ?? ''));
$aAttachmentReport[] = [$oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData() ?? '')];
}
$oLog->Set('attachments', $aAttachmentReport);
}
return $aMessageContent;
}
@@ -859,11 +826,11 @@ class ActionEmail extends ActionNotification
]);
throw new Exception($sErrorMessage);
}
/**
* Compose the body of the message from the 'body' attribute and the HTML template (if any)
* @since 3.1.0 N°4849
* @param bool $bHighlightPlaceholders If true add some extra HTML around placeholders to highlight them
* @param bool $bHighlightPlaceholders If true add some extra HTML around placeholders to highlight them
* @return string
*/
protected function BuildMessageBody(bool $bHighlightPlaceholders = false): string
@@ -889,13 +856,13 @@ HTML;
$sBody = $oHtmlTemplate->GetData();
}
}
if($bHighlightPlaceholders) {
if ($bHighlightPlaceholders) {
// Highlight all placeholders
$sBody = preg_replace('/\\$([^$]+)\\$/', static::FIELD_HIGHLIGHT, $sBody);
}
return $sBody;
}
/**
* @since 3.1.0 N°4849
* @inheritDoc

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2016-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -20,8 +21,7 @@
// Note: for PHP < 7, this compatibility used to be provided by APCU itself (if compiled with some options)
// for PHP 7+, it can be provided by the mean of apcu_bc, which is not so simple to install
// The current emulation aims at skipping this complexity
if (!function_exists('apc_store') && function_exists('apcu_store'))
{
if (!function_exists('apc_store') && function_exists('apcu_store')) {
function apc_add($key, $var, $ttl = 0)
{
return apcu_add($key, $var, $ttl);
@@ -74,25 +74,23 @@ if (!function_exists('apc_store') && function_exists('apcu_store'))
*/
function apc_cache_info_compat()
{
if (!function_exists('apc_cache_info')) return array();
if (!function_exists('apc_cache_info')) {
return [];
}
$oFunction = new ReflectionFunction('apc_cache_info');
if ($oFunction->getNumberOfParameters() != 2)
{
if ($oFunction->getNumberOfParameters() != 2) {
// Beware: APCu behaves slightly differently from APC !!
// Worse: the compatibility layer integrated into APC differs from apcu-bc (testing the number of parameters is a must)
// In CLI mode (PHP > 7) apc_cache_info returns null and outputs an error message.
$aCacheUserData = @apc_cache_info();
}
else
{
} else {
$aCacheUserData = @apc_cache_info('user');
}
return $aCacheUserData;
}
// Cache emulation
if (!function_exists('apc_store'))
{
if (!function_exists('apc_store')) {
require_once(APPROOT.'core/apc-emulation.php');
}
}

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (c) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -28,7 +29,7 @@
*/
function apc_cache_info($cache_type = '', $limited = false)
{
$aInfo = array();
$aInfo = [];
$sRootCacheDir = apcFile::GetCacheFileName();
$aInfo['cache_list'] = apcFile::GetCacheEntries($sRootCacheDir);
return $aInfo;
@@ -40,13 +41,11 @@ function apc_cache_info($cache_type = '', $limited = false)
* @param int $ttl
* @return array|bool
*/
function apc_store($key, $var = NULL, $ttl = 0)
function apc_store($key, $var = null, $ttl = 0)
{
if (is_array($key))
{
$aResult = array();
foreach($key as $sKey => $value)
{
if (is_array($key)) {
$aResult = [];
foreach ($key as $sKey => $value) {
$aResult[] = apcFile::StoreOneFile($sKey, $value, $ttl);
}
return $aResult;
@@ -89,8 +88,7 @@ function apc_clear_cache($cache_type = '')
*/
function apc_delete($key)
{
if (empty($key))
{
if (empty($key)) {
return false;
}
$bRet1 = apcFile::DeleteEntry(apcFile::GetCacheFileName($key));
@@ -126,17 +124,17 @@ function apc_exists($keys)
class apcFile
{
// Check only once per request
static public $aFilesByTime = null;
static public $iFileCount = 0;
public static $aFilesByTime = null;
public static $iFileCount = 0;
/** Get the file name corresponding to the cache entry.
* If an empty key is provided, the root of the cache is returned.
* @param $sKey
* @return string
*/
static public function GetCacheFileName($sKey = '')
public static function GetCacheFileName($sKey = '')
{
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey ?? '');
$sPath = str_replace([' ', '/', '\\', '.'], '-', $sKey ?? '');
return utils::GetCachePath().'apc-emul/'.$sPath;
}
@@ -144,26 +142,21 @@ class apcFile
* @param $sEntry string starting folder.
* @return array list of entries stored into array of key 'info'
*/
static public function GetCacheEntries($sEntry)
public static function GetCacheEntries($sEntry)
{
$aResult = array();
if (is_dir($sEntry))
{
$aFiles = array_diff(scandir($sEntry), array('.', '..'));
foreach($aFiles as $sFile)
{
$aResult = [];
if (is_dir($sEntry)) {
$aFiles = array_diff(scandir($sEntry), ['.', '..']);
foreach ($aFiles as $sFile) {
$sSubFile = $sEntry.'/'.$sFile;
$aResult = array_merge($aResult, self::GetCacheEntries($sSubFile));
}
}
else
{
} else {
$sKey = basename($sEntry);
if (strpos($sKey, '-') === 0)
{
if (strpos($sKey, '-') === 0) {
$sKey = substr($sKey, 1);
}
$aResult[] = array('info' => $sKey);
$aResult[] = ['info' => $sKey];
}
return $aResult;
}
@@ -172,35 +165,25 @@ class apcFile
* @param $sCache
* @return bool true if the entry was deleted false if error occurs (like entry did not exist).
*/
static public function DeleteEntry($sCache)
public static function DeleteEntry($sCache)
{
if (is_dir($sCache))
{
$aFiles = array_diff(scandir($sCache), array('.', '..'));
foreach($aFiles as $sFile)
{
if (is_dir($sCache)) {
$aFiles = array_diff(scandir($sCache), ['.', '..']);
foreach ($aFiles as $sFile) {
$sSubFile = $sCache.'/'.$sFile;
if (!self::DeleteEntry($sSubFile))
{
if (!self::DeleteEntry($sSubFile)) {
return false;
}
}
if (!@rmdir($sCache))
{
if (!@rmdir($sCache)) {
return false;
}
}
else
{
if (is_file($sCache))
{
if (!@unlink($sCache))
{
} else {
if (is_file($sCache)) {
if (!@unlink($sCache)) {
return false;
}
}
else
{
} else {
return false;
}
}
@@ -215,23 +198,22 @@ class apcFile
* @return bool
* @since 3.2.0 N°7068
*/
static public function ExistsOneFile($sKey) {
return is_file(self::GetCacheFileName('-' . $sKey)) || is_file(self::GetCacheFileName($sKey));
public static function ExistsOneFile($sKey)
{
return is_file(self::GetCacheFileName('-'.$sKey)) || is_file(self::GetCacheFileName($sKey));
}
/** Get one cache entry content.
* @param $sKey
* @return bool|mixed
*/
static public function FetchOneFile($sKey)
public static function FetchOneFile($sKey)
{
// Try the 'TTLed' version
$sValue = self::ReadCacheLocked(self::GetCacheFileName('-'.$sKey));
if ($sValue === false)
{
if ($sValue === false) {
$sValue = self::ReadCacheLocked(self::GetCacheFileName($sKey));
if ($sValue === false)
{
if ($sValue === false) {
return false;
}
}
@@ -245,7 +227,7 @@ class apcFile
* @param int $iTTL time to live
* @return bool
*/
static public function StoreOneFile($sKey, $value, $iTTL)
public static function StoreOneFile($sKey, $value, $iTTL)
{
if (empty($sKey)) {
return false;
@@ -279,40 +261,31 @@ class apcFile
* remove older files if the mamximum is reached.
* @param $sNewFilename
*/
static protected function AddFile($sNewFilename)
protected static function AddFile($sNewFilename)
{
if (strpos(basename($sNewFilename), '-') !== 0)
{
if (strpos(basename($sNewFilename), '-') !== 0) {
return;
}
$iMaxFiles = MetaModel::GetConfig()->Get('apc_cache_emulation.max_entries');
if ($iMaxFiles == 0)
{
if ($iMaxFiles == 0) {
return;
}
if (!self::$aFilesByTime)
{
if (!self::$aFilesByTime) {
self::ListFilesByTime();
self::$iFileCount = count(self::$aFilesByTime);
if ($iMaxFiles !== 0)
{
if ($iMaxFiles !== 0) {
asort(self::$aFilesByTime);
}
}
else
{
} else {
self::$aFilesByTime[$sNewFilename] = time();
self::$iFileCount++;
}
if (self::$iFileCount > $iMaxFiles)
{
if (self::$iFileCount > $iMaxFiles) {
$iFileNbToRemove = self::$iFileCount - $iMaxFiles;
foreach(self::$aFilesByTime as $sFileToRemove => $iTime)
{
foreach (self::$aFilesByTime as $sFileToRemove => $iTime) {
@unlink($sFileToRemove);
if (--$iFileNbToRemove === 0)
{
if (--$iFileNbToRemove === 0) {
break;
}
}
@@ -324,25 +297,19 @@ class apcFile
/** Get the list of files with their associated access time
* @param string $sCheck Directory to scan
*/
static protected function ListFilesByTime($sCheck = null)
protected static function ListFilesByTime($sCheck = null)
{
if (empty($sCheck))
{
if (empty($sCheck)) {
$sCheck = self::GetCacheFileName();
}
// Garbage collection
$aFiles = array_diff(@scandir($sCheck), array('.', '..'));
foreach($aFiles as $sFile)
{
$aFiles = array_diff(@scandir($sCheck), ['.', '..']);
foreach ($aFiles as $sFile) {
$sSubFile = $sCheck.'/'.$sFile;
if (is_dir($sSubFile))
{
if (is_dir($sSubFile)) {
self::ListFilesByTime($sSubFile);
}
else
{
if (strpos(basename($sSubFile), '-') === 0)
{
} else {
if (strpos(basename($sSubFile), '-') === 0) {
self::$aFilesByTime[$sSubFile] = @fileatime($sSubFile);
}
}
@@ -353,7 +320,7 @@ class apcFile
* @param $sFilename
* @return bool|string the content of the cache entry or false if error
*/
static protected function ReadCacheLocked($sFilename)
protected static function ReadCacheLocked($sFilename)
{
$sContent = false;
$file = @fopen($sFilename, 'r');
@@ -367,7 +334,7 @@ class apcFile
return $sContent;
}
static protected function ResetFileCount()
protected static function ResetFileCount()
{
self::$aFilesByTime = null;
self::$iFileCount = 0;

View File

@@ -4,8 +4,10 @@
* Class ApcService
* @since 2.7.6 N°4125
*/
class ApcService {
public function __construct() {
class ApcService
{
public function __construct()
{
}
/**
@@ -13,7 +15,8 @@ class ApcService {
* @return bool
* @see function_exists()
*/
public function function_exists($function_name) {
public function function_exists($function_name)
{
return function_exists($function_name);
}
@@ -22,7 +25,7 @@ class ApcService {
* @return mixed
* @see apc_fetch()
*/
function apc_fetch($key)
public function apc_fetch($key)
{
return apc_fetch($key);
}
@@ -34,9 +37,8 @@ class ApcService {
* @return array|bool
* @see apc_store()
*/
function apc_store($key, $var = NULL, $ttl = 0)
public function apc_store($key, $var = null, $ttl = 0)
{
return apc_store($key, $var, $ttl);
}
}
?>

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -17,7 +18,6 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Service\Notification\Event\EventNotificationNewsroomService;
/**
* Persistent classes (internal): user defined actions
*
@@ -25,11 +25,10 @@ use Combodo\iTop\Service\Notification\Event\EventNotificationNewsroomService;
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ExecAsyncTask implements iBackgroundProcess
{
public function GetPeriodicity()
{
{
return 2; // seconds
}
@@ -39,19 +38,16 @@ class ExecAsyncTask implements iBackgroundProcess
// Criteria: planned, and expected to occur... ASAP or in the past
$sOQL = "SELECT AsyncTask WHERE (status = 'planned') AND (ISNULL(planned) OR (planned < '$sNow'))";
$iProcessed = 0;
while (time() < $iTimeLimit)
{
while (time() < $iTimeLimit) {
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), ['created' => true] /* order by*/, [], null, 1 /* limit count */);
$oTask = $oSet->Fetch();
if (is_null($oTask))
{
if (is_null($oTask)) {
// Nothing to be done
break;
}
$iProcessed++;
if ($oTask->Process())
{
if ($oTask->Process()) {
$oTask->DBDelete();
}
}
@@ -60,7 +56,7 @@ class ExecAsyncTask implements iBackgroundProcess
}
/**
* A
* A
*
* @package iTopORM
*/
@@ -72,79 +68,72 @@ abstract class AsyncTask extends DBObject
*/
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => array('created'),
"name_attcode" => ['created'],
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_async_task",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
);
];
MetaModel::Init_Params($aParams);
// Null is allowed to ease the migration from iTop 2.0.2 and earlier, when the status did not exist, and because the default value is not taken into account in the SQL definition
// The value is set from null to planned in the setup program
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('planned,running,idle,error'), "sql"=>"status", "default_value"=>"planned", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", ["allowed_values" => new ValueSetEnum('planned,running,idle,error'), "sql" => "status", "default_value" => "planned", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("started", array("allowed_values"=>null, "sql"=>"started", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("planned", array("allowed_values"=>null, "sql"=>"planned", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", array("targetclass"=>"Event", "jointype"=> "", "allowed_values"=>null, "sql"=>"event_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", ["allowed_values" => null, "sql" => "created", "default_value" => "NOW()", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDateTime("started", ["allowed_values" => null, "sql" => "started", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDateTime("planned", ["allowed_values" => null, "sql" => "planned", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", ["targetclass" => "Event", "jointype" => "", "allowed_values" => null, "sql" => "event_id", "is_null_allowed" => true, "on_target_delete" => DEL_SILENT, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeInteger("remaining_retries", array("allowed_values"=>null, "sql"=>"remaining_retries", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("last_error_code", array("allowed_values"=>null, "sql"=>"last_error_code", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("last_error", array("allowed_values"=>null, "sql"=>"last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("last_attempt", array("allowed_values"=>null, "sql"=>"last_attempt", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("remaining_retries", ["allowed_values" => null, "sql" => "remaining_retries", "default_value" => 0, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeInteger("last_error_code", ["allowed_values" => null, "sql" => "last_error_code", "default_value" => 0, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("last_error", ["allowed_values" => null, "sql" => "last_error", "default_value" => '', "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDateTime("last_attempt", ["allowed_values" => null, "sql" => "last_attempt", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
}
/**
* Every is fine
*/
const OK = 0;
public const OK = 0;
/**
* The task no longer exists
*/
const DELETED = 1;
public const DELETED = 1;
/**
* The task is already being executed
*/
const ALREADY_RUNNING = 2;
public const ALREADY_RUNNING = 2;
/**
* The current process requests the ownership on the task.
* In case the task can be accessed concurrently, this function can be overloaded to add a critical section.
* The function must not block the caller if another process is already owning the task
*
* @return integer A code among OK/DELETED/ALREADY_RUNNING.
*/
* The function must not block the caller if another process is already owning the task
*
* @return integer A code among OK/DELETED/ALREADY_RUNNING.
*/
public function MarkAsRunning()
{
try
{
if ($this->Get('status') == 'running')
{
try {
if ($this->Get('status') == 'running') {
return self::ALREADY_RUNNING;
}
else
{
} else {
$this->Set('status', 'running');
$this->Set('started', time());
$this->DBUpdate();
return self::OK;
}
}
catch(Exception $e)
{
} catch (Exception $e) {
// Corrupted task !! (for example: "Failed to reload object")
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$e->getMessage().' - fatal error, deleting the task.');
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage());
$oEventLog->DBUpdate();
if ($this->Get('event_id') != 0) {
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', 'Failed, corrupted data: '.$e->getMessage());
$oEventLog->DBUpdate();
}
$this->DBDelete();
return self::DELETED;
@@ -155,8 +144,7 @@ abstract class AsyncTask extends DBObject
{
$iRetryDelay = 600;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries)) {
$aConfig = $aRetries[get_class($this)];
$iRetryDelay = $aConfig['retry_delay'] ?? $iRetryDelay;
}
@@ -167,8 +155,7 @@ abstract class AsyncTask extends DBObject
{
$iMaxRetries = 0;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries)) {
$aConfig = $aRetries[get_class($this)];
$iMaxRetries = $aConfig['max_retries'] ?? $iMaxRetries;
}
@@ -177,40 +164,33 @@ abstract class AsyncTask extends DBObject
public function IsRetryDelayExponential()
{
$bExponential = false;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$bExponential = (bool) ($aConfig['exponential_delay'] ?? $bExponential);
}
return $bExponential;
$bExponential = false;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries)) {
$aConfig = $aRetries[get_class($this)];
$bExponential = (bool) ($aConfig['exponential_delay'] ?? $bExponential);
}
return $bExponential;
}
public static function CheckRetryConfig(Config $oConfig, $sAsyncTaskClass)
{
$aMessages = [];
$aRetries = $oConfig->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists($sAsyncTaskClass, $aRetries))
{
$aValidKeys = array("retry_delay", "max_retries", "exponential_delay");
$aConfig = $aRetries[$sAsyncTaskClass];
if (!is_array($aConfig))
{
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_Keys', $sAsyncTaskClass, implode(', ', $aValidKeys));
}
else
{
foreach($aConfig as $key => $value)
{
if (!in_array($key, $aValidKeys))
{
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_InvalidKey_Keys', $sAsyncTaskClass, $key, implode(', ', $aValidKeys));
}
}
}
}
return $aMessages;
$aMessages = [];
$aRetries = $oConfig->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists($sAsyncTaskClass, $aRetries)) {
$aValidKeys = ["retry_delay", "max_retries", "exponential_delay"];
$aConfig = $aRetries[$sAsyncTaskClass];
if (!is_array($aConfig)) {
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_Keys', $sAsyncTaskClass, implode(', ', $aValidKeys));
} else {
foreach ($aConfig as $key => $value) {
if (!in_array($key, $aValidKeys)) {
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_InvalidKey_Keys', $sAsyncTaskClass, $key, implode(', ', $aValidKeys));
}
}
}
}
return $aMessages;
}
/**
@@ -223,16 +203,15 @@ abstract class AsyncTask extends DBObject
*/
public static function GetNextRetryDelay($bIsExponential, $iRetryDelay, $iMaxRetries, $iRemainingRetries)
{
if ($bIsExponential)
{
$iExponent = $iMaxRetries - $iRemainingRetries;
if ($iExponent < 0) $iExponent = 0; // Safety net in case on configuration change in the middle of retries
return $iRetryDelay * (2 ** $iExponent);
}
else
{
return $iRetryDelay;
}
if ($bIsExponential) {
$iExponent = $iMaxRetries - $iRemainingRetries;
if ($iExponent < 0) {
$iExponent = 0;
} // Safety net in case on configuration change in the middle of retries
return $iRetryDelay * (2 ** $iExponent);
} else {
return $iRetryDelay;
}
}
/**
@@ -242,14 +221,14 @@ abstract class AsyncTask extends DBObject
{
}
protected function OnInsert()
protected function OnInsert()
{
$this->Set('created', time());
}
/**
* @return boolean True if the task record can be deleted
*/
/**
* @return boolean True if the task record can be deleted
*/
public function Process()
{
// By default: consider that the task is not completed
@@ -257,25 +236,19 @@ abstract class AsyncTask extends DBObject
// Attempt to take the ownership
$iStatus = $this->MarkAsRunning();
if ($iStatus == self::OK)
{
try
{
if ($iStatus == self::OK) {
try {
$sStatus = $this->DoProcess();
if ($this->Get('event_id') != 0)
{
if ($this->Get('event_id') != 0) {
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', $sStatus);
$oEventLog->DBUpdate();
}
$bRet = true;
} catch (Exception $e)
{
} catch (Exception $e) {
$this->HandleError($e->getMessage(), $e->getCode());
}
}
else
{
} else {
// Already done or being handled by another process... skip...
$bRet = false;
}
@@ -288,8 +261,7 @@ abstract class AsyncTask extends DBObject
*/
protected function HandleError($sErrorMessage, $iErrorCode)
{
if ($this->Get('last_attempt') == '')
{
if ($this->Get('last_attempt') == '') {
// First attempt
$this->Set('remaining_retries', $this->GetMaxRetries($iErrorCode));
}
@@ -299,45 +271,34 @@ abstract class AsyncTask extends DBObject
$this->Set('last_attempt', time());
$iRemaining = $this->Get('remaining_retries');
if ($iRemaining > 0)
{
if ($iRemaining > 0) {
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
$iNextRetryDelay = static::GetNextRetryDelay($this->IsRetryDelayExponential(), $iRetryDelay, $this->GetMaxRetries($iErrorCode), $iRemaining);
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iNextRetryDelay.'s');
if ($this->Get('event_id') != 0)
{
if ($this->Get('event_id') != 0) {
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task. Remaining retries: $iRemaining. Next retry in {$iNextRetryDelay}s");
try
{
$oEventLog->DBUpdate();
}
catch (Exception $e)
{
$oEventLog->Set('message', "Failed to process async task. Remaining retries: $iRemaining. Next retry in {$iNextRetryDelay}s, more details in the log");
$oEventLog->DBUpdate();
}
try {
$oEventLog->DBUpdate();
} catch (Exception $e) {
$oEventLog->Set('message', "Failed to process async task. Remaining retries: $iRemaining. Next retry in {$iNextRetryDelay}s, more details in the log");
$oEventLog->DBUpdate();
}
}
$this->Set('remaining_retries', $iRemaining - 1);
$this->Set('status', 'planned');
$this->Set('started', null);
$this->Set('planned', time() + $iNextRetryDelay);
}
else
{
} else {
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage);
if ($this->Get('event_id') != 0)
{
if ($this->Get('event_id') != 0) {
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task.");
try
{
$oEventLog->DBUpdate();
}
catch (Exception $e)
{
$oEventLog->Set('message', 'Failed to process async task, more details in the log');
$oEventLog->DBUpdate();
try {
$oEventLog->DBUpdate();
} catch (Exception $e) {
$oEventLog->Set('message', 'Failed to process async task, more details in the log');
$oEventLog->DBUpdate();
}
}
$this->Set('status', 'error');
@@ -352,20 +313,20 @@ abstract class AsyncTask extends DBObject
* Throws an exception (message and code)
*
* @return string
*/
*/
abstract public function DoProcess();
/**
* Describes the error codes that DoProcess can return by the mean of exceptions
*/
static public function EnumErrorCodes()
*/
public static function EnumErrorCodes()
{
return array();
return [];
}
}
/**
* An email notification
* An email notification
*
* @package iTopORM
*/
@@ -373,38 +334,37 @@ class AsyncSendEmail extends AsyncTask
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "created",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_async_send_email",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeInteger("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>Email::ORIGINAL_FORMAT, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("version", ["allowed_values" => null, "sql" => "version", "default_value" => Email::ORIGINAL_FORMAT, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeText("to", ["allowed_values" => null, "sql" => "to", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeText("subject", ["allowed_values" => null, "sql" => "subject", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeLongText("message", ["allowed_values" => null, "sql" => "message", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
// Display lists
// MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); // Attributes to be displayed for the complete details
// MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Attributes to be displayed for a list
// MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); // Attributes to be displayed for the complete details
// MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
static public function AddToQueue(EMail $oEMail, $oLog)
public static function AddToQueue(EMail $oEMail, $oLog)
{
$oNew = MetaModel::NewObject(__class__);
if ($oLog)
{
if ($oLog) {
$oNew->Set('event_id', $oLog->GetKey());
}
$oNew->Set('to', $oEMail->GetRecipientTO(true /* string */));
@@ -425,35 +385,33 @@ class AsyncSendEmail extends AsyncTask
{
$sMessage = $this->Get('message');
$iVersion = (int) $this->Get('version');
switch($iVersion)
{
switch ($iVersion) {
case Email::FORMAT_V2:
$oEMail = Email::UnSerializeV2($sMessage);
break;
$oEMail = Email::UnSerializeV2($sMessage);
break;
case Email::ORIGINAL_FORMAT:
$oEMail = unserialize($sMessage);
break;
$oEMail = unserialize($sMessage);
break;
default:
return 'Unknown version of the serialization format: '.$iVersion;
return 'Unknown version of the serialization format: '.$iVersion;
}
$iRes = $oEMail->Send($aIssues, true /* force synchro !!!!! */);
switch ($iRes)
{
case EMAIL_SEND_OK:
return "Sent";
switch ($iRes) {
case EMAIL_SEND_OK:
return "Sent";
case EMAIL_SEND_PENDING:
return "Bug - the email should be sent in synchronous mode";
case EMAIL_SEND_PENDING:
return "Bug - the email should be sent in synchronous mode";
case EMAIL_SEND_ERROR:
if (is_array($aIssues)) {
$sMessage = "Sending eMail failed: ".implode(', ', $aIssues);
} else {
$sMessage = "Sending eMail failed.";
}
throw new Exception($sMessage);
case EMAIL_SEND_ERROR:
if (is_array($aIssues)) {
$sMessage = "Sending eMail failed: ".implode(', ', $aIssues);
} else {
$sMessage = "Sending eMail failed.";
}
throw new Exception($sMessage);
}
return '';
}
@@ -463,33 +421,33 @@ class AsyncSendEmail extends AsyncTask
* An async notification to be sent to iTop users through the newsroom
* @since 3.2.0
*/
class AsyncSendNewsroom extends AsyncTask {
class AsyncSendNewsroom extends AsyncTask
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "created",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_async_send_newsroom",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("recipients", array("allowed_values"=>null, "sql"=>"recipients", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "allowed_values"=>null, "sql"=>"action_id", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "allowed_values"=>null, "sql"=>"trigger_id", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("title", array("allowed_values"=>null, "sql"=>"title", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values"=>null, "sql"=>"object_id", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("object_class", array("allowed_values"=>null, "sql"=>"object_class", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("url", array("allowed_values"=>null, "sql"=>"url", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>'NOW()', "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("recipients", ["allowed_values" => null, "sql" => "recipients", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", ["targetclass" => "Action", "allowed_values" => null, "sql" => "action_id", "default_value" => null, "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", ["targetclass" => "Trigger", "allowed_values" => null, "sql" => "trigger_id", "default_value" => null, "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeText("title", ["allowed_values" => null, "sql" => "title", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeText("message", ["allowed_values" => null, "sql" => "message", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", ["allowed_values" => null, "sql" => "object_id", "default_value" => null, "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("object_class", ["allowed_values" => null, "sql" => "object_class", "default_value" => null, "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeText("url", ["allowed_values" => null, "sql" => "url", "default_value" => null, "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", ["allowed_values" => null, "sql" => "date", "default_value" => 'NOW()', "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
}
@@ -514,7 +472,7 @@ class AsyncSendNewsroom extends AsyncTask {
$oNew->Set('object_id', $iObjectId);
$oNew->Set('object_class', $sObjectClass);
$oNew->SetCurrentDate('date');
$oNew->DBInsert();
}
@@ -532,13 +490,12 @@ class AsyncSendNewsroom extends AsyncTask {
$iObjectId = $this->Get('object_id');
$sObjectClass = $this->Get('object_class');
$sDate = $this->Get('date');
foreach ($aRecipients as $iRecipientId)
{
foreach ($aRecipients as $iRecipientId) {
$oEvent = EventNotificationNewsroomService::MakeEventFromAction($oAction, $iRecipientId, $iTriggerId, $sMessage, $sTitle, $sUrl, $iObjectId, $sObjectClass, $sDate);
$oEvent->DBInsertNoReload();
}
return "Sent";
}
}
}

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -25,7 +26,6 @@ MetaModel::IncludeModule('application/audit.domain.class.inc.php');
MetaModel::IncludeModule('application/query.class.inc.php');
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
MetaModel::IncludeModule('core/event.class.inc.php');
MetaModel::IncludeModule('core/action.class.inc.php');
MetaModel::IncludeModule('core/trigger.class.inc.php');

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Tasks performed in the background
*
@@ -24,7 +24,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ObsolescenceDateUpdater implements iBackgroundProcess
{
public function GetPeriodicity()
@@ -37,18 +36,17 @@ class ObsolescenceDateUpdater implements iBackgroundProcess
$iCountSet = 0;
$iCountReset = 0;
$iClasses = 0;
foreach (MetaModel::EnumObsoletableClasses() as $sClass)
{
foreach (MetaModel::EnumObsoletableClasses() as $sClass) {
$oObsoletedToday = new DBObjectSearch($sClass);
$oObsoletedToday->AddCondition('obsolescence_flag', 1, '=');
$oObsoletedToday->AddCondition('obsolescence_date', null, '=');
$sToday = date(AttributeDate::GetSQLFormat());
$iCountSet += MetaModel::BulkUpdate($oObsoletedToday, array('obsolescence_date' => $sToday));
$iCountSet += MetaModel::BulkUpdate($oObsoletedToday, ['obsolescence_date' => $sToday]);
$oObsoletedToday = new DBObjectSearch($sClass);
$oObsoletedToday->AddCondition('obsolescence_flag', 1, '!=');
$oObsoletedToday->AddCondition('obsolescence_date', null, '!=');
$iCountReset += MetaModel::BulkUpdate($oObsoletedToday, array('obsolescence_date' => null));
$iCountReset += MetaModel::BulkUpdate($oObsoletedToday, ['obsolescence_date' => null]);
}
return "Obsolescence date updated (classes: $iClasses ; set: $iCountSet ; reset: $iCountReset)\n";
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2010-2024 Combodo SAS
*
@@ -17,7 +18,6 @@
* You should have received a copy of the GNU Affero General Public License
*/
interface iProcess
{
/**
@@ -33,7 +33,7 @@ interface iProcess
/**
* interface iBackgroundProcess
* Any extension that must be called regularly to be executed in the background
* Any extension that must be called regularly to be executed in the background
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -62,7 +62,6 @@ interface iScheduledProcess extends iProcess
public function GetNextOccurrence();
}
/**
* Implementation of {@link iScheduledProcess}, using config parameters for module
*
@@ -82,11 +81,11 @@ interface iScheduledProcess extends iProcess
abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
{
// param have default names/values but can be overridden
const MODULE_SETTING_ENABLED = 'enabled';
const DEFAULT_MODULE_SETTING_ENABLED = true;
const MODULE_SETTING_WEEKDAYS = 'week_days';
const DEFAULT_MODULE_SETTING_WEEKDAYS = 'monday, tuesday, wednesday, thursday, friday, saturday, sunday';
const MODULE_SETTING_TIME = 'time';
public const MODULE_SETTING_ENABLED = 'enabled';
public const DEFAULT_MODULE_SETTING_ENABLED = true;
public const MODULE_SETTING_WEEKDAYS = 'week_days';
public const DEFAULT_MODULE_SETTING_WEEKDAYS = 'monday, tuesday, wednesday, thursday, friday, saturday, sunday';
public const MODULE_SETTING_TIME = 'time';
/**
* @var Config can be used to mock config for tests
@@ -111,15 +110,13 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
*/
public function getOConfig()
{
if (!isset($this->oConfig))
{
if (!isset($this->oConfig)) {
$this->oConfig = MetaModel::GetConfig();
}
return $this->oConfig;
}
/**
* Interpret current setting for the week days
*
@@ -128,7 +125,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
*/
public function InterpretWeekDays()
{
static $aWEEKDAYTON = array(
static $aWEEKDAYTON = [
'monday' => 1,
'tuesday' => 2,
'wednesday' => 3,
@@ -136,32 +133,26 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
'friday' => 5,
'saturday' => 6,
'sunday' => 7,
);
$aDays = array();
];
$aDays = [];
$sWeekDays = $this->getOConfig()->GetModuleSetting(
$this->GetModuleName(),
static::MODULE_SETTING_WEEKDAYS,
static::DEFAULT_MODULE_SETTING_WEEKDAYS
);
if ($sWeekDays !== '')
{
if ($sWeekDays !== '') {
$aWeekDaysRaw = explode(',', $sWeekDays);
foreach ($aWeekDaysRaw as $sWeekDay)
{
foreach ($aWeekDaysRaw as $sWeekDay) {
$sWeekDay = strtolower(trim($sWeekDay));
if (array_key_exists($sWeekDay, $aWEEKDAYTON))
{
if (array_key_exists($sWeekDay, $aWEEKDAYTON)) {
$aDays[] = $aWEEKDAYTON[$sWeekDay];
}
else
{
} else {
throw new ProcessInvalidConfigException($this->GetModuleName().": wrong format for setting '".static::MODULE_SETTING_WEEKDAYS."' (found '$sWeekDay')");
}
}
}
if (count($aDays) === 0)
{
if (count($aDays) === 0) {
throw new ProcessInvalidConfigException($this->GetModuleName().': missing setting \''.static::MODULE_SETTING_WEEKDAYS.'\'');
}
$aDays = array_unique($aDays);
@@ -185,8 +176,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
static::DEFAULT_MODULE_SETTING_ENABLED
);
if (!$bEnabled)
{
if (!$bEnabled) {
return new DateTime('3000-01-01');
}
@@ -202,21 +192,17 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
static::GetDefaultModuleSettingTime()
);
$sProcessTime = trim($sProcessTime);
if (!preg_match('/[0-2]\d:[0-5]\d/', $sProcessTime))
{
if (!preg_match('/[0-2]\d:[0-5]\d/', $sProcessTime)) {
throw new ProcessInvalidConfigException($this->GetModuleName().": wrong format for setting '".static::MODULE_SETTING_TIME."' (found '$sProcessTime')");
}
$oNow = new DateTime($sCurrentTime);
$iNextPos = false;
$sDay = $oNow->format('N');
for ($iDay = (int) $sDay; $iDay <= 7; $iDay++)
{
for ($iDay = (int) $sDay; $iDay <= 7; $iDay++) {
$iNextPos = array_search($iDay, $aDays, true);
if ($iNextPos !== false)
{
if (($iDay > $oNow->format('N')) || ($oNow->format('H:i') < $sProcessTime))
{
if ($iNextPos !== false) {
if (($iDay > $oNow->format('N')) || ($oNow->format('H:i') < $sProcessTime)) {
break;
}
$iNextPos = false; // necessary on sundays
@@ -225,17 +211,14 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
// 3rd - Compute the result
//
if ($iNextPos === false)
{
if ($iNextPos === false) {
// Jump to the first day within the next week
$iFirstDayOfWeek = $aDays[0];
$iDayMove = $oNow->format('N') - $iFirstDayOfWeek;
$oRet = clone $oNow;
$oRet->modify(-$iDayMove.' days');
$oRet->modify('+1 weeks');
}
else
{
} else {
$iNextDayOfWeek = $aDays[$iNextPos];
$iMove = $iNextDayOfWeek - $oNow->format('N');
$oRet = clone $oNow;

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class BackgroundTask
* A class to record information about the execution of background processes ({@link iProcess} impl)
@@ -46,49 +46,47 @@ class BackgroundTask extends DBObject
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "class_name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_backgroundtask",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("class_name", array("allowed_values"=>null, "sql"=>"class_name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("first_run_date", array("allowed_values"=>null, "sql"=>"first_run_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("latest_run_date", array("allowed_values"=>null, "sql"=>"latest_run_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("next_run_date", array("allowed_values"=>null, "sql"=>"next_run_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("total_exec_count", array("allowed_values"=>null, "sql"=>"total_exec_count", "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDecimal("latest_run_duration", array("allowed_values"=>null, "sql"=>"latest_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDecimal("min_run_duration", array("allowed_values"=>null, "sql"=>"min_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDecimal("max_run_duration", array("allowed_values"=>null, "sql"=>"max_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDecimal("average_run_duration", array("allowed_values"=>null, "sql"=>"average_run_duration", "digits"=> 8, "decimals"=> 3, "default_value"=>"0", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("class_name", ["allowed_values" => null, "sql" => "class_name", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDateTime("first_run_date", ["allowed_values" => null, "sql" => "first_run_date", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDateTime("latest_run_date", ["allowed_values" => null, "sql" => "latest_run_date", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDateTime("next_run_date", ["allowed_values" => null, "sql" => "next_run_date", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeBoolean("running", array("allowed_values"=>null, "sql"=>"running", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('active,paused,removed'), "sql"=>"status", "default_value"=>'active', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("system_user", array("allowed_values"=>null, "sql"=>"system_user", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("total_exec_count", ["allowed_values" => null, "sql" => "total_exec_count", "default_value" => "0", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDecimal("latest_run_duration", ["allowed_values" => null, "sql" => "latest_run_duration", "digits" => 8, "decimals" => 3, "default_value" => "0", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDecimal("min_run_duration", ["allowed_values" => null, "sql" => "min_run_duration", "digits" => 8, "decimals" => 3, "default_value" => "0", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDecimal("max_run_duration", ["allowed_values" => null, "sql" => "max_run_duration", "digits" => 8, "decimals" => 3, "default_value" => "0", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDecimal("average_run_duration", ["allowed_values" => null, "sql" => "average_run_duration", "digits" => 8, "decimals" => 3, "default_value" => "0", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeBoolean("running", ["allowed_values" => null, "sql" => "running", "default_value" => false, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("status", ["allowed_values" => new ValueSetEnum('active,paused,removed'), "sql" => "status", "default_value" => 'active', "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("system_user", ["allowed_values" => null, "sql" => "system_user", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
}
public function ComputeDurations($fLatestDuration)
{
$iTotalRun = $this->Get('total_exec_count');
$fAverageDuration = ($this->Get('average_run_duration') * $iTotalRun + $fLatestDuration) / (1+$iTotalRun);
$this->Set('average_run_duration', sprintf('%.3f',$fAverageDuration));
$this->Set('total_exec_count', 1+$iTotalRun);
if ($fLatestDuration < $this->Get('min_run_duration'))
{
$this->Set('min_run_duration', sprintf('%.3f',$fLatestDuration));
$fAverageDuration = ($this->Get('average_run_duration') * $iTotalRun + $fLatestDuration) / (1 + $iTotalRun);
$this->Set('average_run_duration', sprintf('%.3f', $fAverageDuration));
$this->Set('total_exec_count', 1 + $iTotalRun);
if ($fLatestDuration < $this->Get('min_run_duration')) {
$this->Set('min_run_duration', sprintf('%.3f', $fLatestDuration));
}
if ($fLatestDuration > $this->Get('max_run_duration'))
{
$this->Set('max_run_duration', sprintf('%.3f',$fLatestDuration));
if ($fLatestDuration > $this->Get('max_run_duration')) {
$this->Set('max_run_duration', sprintf('%.3f', $fLatestDuration));
}
$this->Set('latest_run_duration', sprintf('%.3f',$fLatestDuration));
$this->Set('latest_run_duration', sprintf('%.3f', $fLatestDuration));
}
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Persistent class (internal) cmdbChange
*
@@ -9,7 +10,7 @@
use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin;
/**
* A change as requested/validated at once by user, may groups many atomic changes
* A change as requested/validated at once by user, may groups many atomic changes
*
* @package iTopORM
*/
@@ -17,26 +18,26 @@ class CMDBChange extends DBObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "core/cmdb, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "date",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_change",
"db_key_field" => "id",
"db_finalclass_field" => "",
'indexes' => array(
array('origin'),
),
);
'indexes' => [
['origin'],
],
];
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("allowed_values"=>null, "sql"=>"user_id", "targetclass"=>"User", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum(implode(',', [CMDBChangeOrigin::INTERACTIVE, CMDBChangeOrigin::CSV_INTERACTIVE, CMDBChangeOrigin::CSV_IMPORT, CMDBChangeOrigin::WEBSERVICE_SOAP, CMDBChangeOrigin::WEBSERVICE_REST, CMDBChangeOrigin::SYNCHRO_DATA_SOURCE, CMDBChangeOrigin::EMAIL_PROCESSING, CMDBChangeOrigin::CUSTOM_EXTENSION])), "sql"=>"origin", "default_value"=>CMDBChangeOrigin::INTERACTIVE, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", ["allowed_values" => null, "sql" => "date", "default_value" => "NOW()", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", ["allowed_values" => null, "sql" => "userinfo", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", ["allowed_values" => null, "sql" => "user_id", "targetclass" => "User", "is_null_allowed" => true, "on_target_delete" => DEL_MANUAL, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("origin", ["allowed_values" => new ValueSetEnum(implode(',', [CMDBChangeOrigin::INTERACTIVE, CMDBChangeOrigin::CSV_INTERACTIVE, CMDBChangeOrigin::CSV_IMPORT, CMDBChangeOrigin::WEBSERVICE_SOAP, CMDBChangeOrigin::WEBSERVICE_REST, CMDBChangeOrigin::SYNCHRO_DATA_SOURCE, CMDBChangeOrigin::EMAIL_PROCESSING, CMDBChangeOrigin::CUSTOM_EXTENSION])), "sql" => "origin", "default_value" => CMDBChangeOrigin::INTERACTIVE, "is_null_allowed" => true, "depends_on" => []]));
}
/**
@@ -48,12 +49,9 @@ class CMDBChange extends DBObject
*/
public static function GetCurrentUserName()
{
if (UserRights::IsImpersonated())
{
if (UserRights::IsImpersonated()) {
$sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUserFriendlyName(), UserRights::GetUserFriendlyName());
}
else
{
} else {
$sUserString = UserRights::GetUserFriendlyName();
}
return $sUserString;
@@ -74,12 +72,9 @@ class CMDBChange extends DBObject
public function GetUserName()
{
if (preg_match('/^(.*)\\(CSV\\)$/i', $this->Get('userinfo'), $aMatches))
{
if (preg_match('/^(.*)\\(CSV\\)$/i', $this->Get('userinfo'), $aMatches)) {
$sUser = $aMatches[1];
}
else
{
} else {
$sUser = $this->Get('userinfo');
}
return $sUser;

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2010-2024 Combodo SAS
*
@@ -18,7 +19,6 @@
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Any extension to compute things like a stop watch deadline or working hours
*
@@ -29,7 +29,7 @@
/**
* Metric computing for stop watches.
* Can be used for AttributeStopWatch goal (iTop XML node xpath: /itop_design/classes/class/fields/field/goal)
*/
*/
interface iMetricComputer
{
public static function GetDescription();
@@ -44,7 +44,7 @@ interface iMetricComputer
/**
* Working time computing for stop watches
*/
*/
interface iWorkingTimeComputer
{
public static function GetDescription();
@@ -58,7 +58,7 @@ interface iWorkingTimeComputer
* considering only the valid (open) hours for a specified object
*/
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate);
/**
* @param DBObject $oObject The object for which to compute the duration
* @param DateTime $oStartDate The starting point for the computation (default = now)
@@ -87,7 +87,7 @@ class DefaultMetricComputer implements iMetricComputer
/**
* Default implementation of working time computing
*/
*/
class DefaultWorkingTimeComputer implements iWorkingTimeComputer
{
public static function GetDescription()
@@ -100,8 +100,7 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
*/
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate)
{
if (class_exists('WorkingTimeRecorder'))
{
if (class_exists('WorkingTimeRecorder')) {
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
}
//echo "GetDeadline - default: ".$oStartDate->format('Y-m-d H:i:s')." + $iDuration<br/>\n";
@@ -109,26 +108,23 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
// the specified duration to the given date/time
$oResult = clone $oStartDate;
$oResult->modify($iDuration.' seconds');
if (class_exists('WorkingTimeRecorder'))
{
if (class_exists('WorkingTimeRecorder')) {
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oResult->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_END);
}
return $oResult;
}
/**
* @inheritDoc
*/
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate)
{
if (class_exists('WorkingTimeRecorder'))
{
if (class_exists('WorkingTimeRecorder')) {
WorkingTimeRecorder::Trace(WorkingTimeRecorder::TRACE_DEBUG, __class__.'::'.__function__);
}
//echo "GetOpenDuration - default: ".$oStartDate->format('Y-m-d H:i:s')." to ".$oEndDate->format('Y-m-d H:i:s')."<br/>\n";
$iDuration = abs($oEndDate->format('U') - $oStartDate->format('U'));
if (class_exists('WorkingTimeRecorder'))
{
if (class_exists('WorkingTimeRecorder')) {
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oEndDate->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_DURATION);
}
return $iDuration;

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2016-2024 Combodo SAS
//
// This file is part of iTop.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Simple helper class for keeping track of the context inside the call stack
*
@@ -63,7 +63,7 @@ class ContextTag
* @since 3.1.0 N°6047
*/
public const TAG_IMPORT = 'Import';
/**
/**
* @since 3.1.0 N°6047
*/
public const TAG_EXPORT = 'Export';
@@ -74,7 +74,7 @@ class ContextTag
*/
public const TAG_OBJECT_SEARCH = 'ObjectSearch';
protected static $aStack = array();
protected static $aStack = [];
/**
* Store a context tag on the stack
@@ -124,33 +124,29 @@ class ContextTag
*/
public static function GetTags()
{
$aRawTags = array(
$aRawTags = [
ContextTag::TAG_REST,
ContextTag::TAG_SYNCHRO,
ContextTag::TAG_SETUP,
ContextTag::TAG_CONSOLE,
ContextTag::TAG_CRON,
ContextTag::TAG_PORTAL);
ContextTag::TAG_PORTAL];
$aTags = array();
$aTags = [];
foreach ($aRawTags as $sRawTag)
{
foreach ($aRawTags as $sRawTag) {
$aTags[$sRawTag] = Dict::S("Core:Context={$sRawTag}");
}
$aPortalsConf = PortalDispatcherData::GetData();
$aDispatchers = array();
foreach ($aPortalsConf as $sPortalId => $aConf)
{
$aDispatchers = [];
foreach ($aPortalsConf as $sPortalId => $aConf) {
$sHandlerClass = $aConf['handler'];
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
}
foreach ($aDispatchers as $sPortalId => $oDispatcher)
{
if ($sPortalId != 'backoffice')
{
foreach ($aDispatchers as $sPortalId => $oDispatcher) {
if ($sPortalId != 'backoffice') {
$aTags['Portal:'.$sPortalId] = $oDispatcher->GetLabel();
}
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -17,14 +18,12 @@
* You should have received a copy of the GNU Affero General Public License
*/
/**
* Class ItopCounter
*
*/
final class ItopCounter
{
/**
* Key based counter.
* The counter is protected against concurrency script.
@@ -48,8 +47,7 @@ final class ItopCounter
$oiTopMutex->Lock();
$bIsInsideTransaction = CMDBSource::IsInsideTransaction();
if ($bIsInsideTransaction)
{
if ($bIsInsideTransaction) {
// # Transaction isolation hack:
// When inside a transaction, we need to open a new connection for the counter.
// So it is visible immediately to the connections outside of the transaction.
@@ -59,75 +57,61 @@ final class ItopCounter
// we did not wanted this! As opening a short connection is less prone to starving than a long running one.
// Plus it would trigger way more deadlocks!
$hDBLink = self::InitMySQLSession();
}
else
{
} else {
$hDBLink = CMDBSource::GetMysqli();
}
try
{
$oFilter = DBObjectSearch::FromOQL('SELECT KeyValueStore WHERE key_name=:key_name AND namespace=:namespace', array(
try {
$oFilter = DBObjectSearch::FromOQL('SELECT KeyValueStore WHERE key_name=:key_name AND namespace=:namespace', [
'key_name' => $sCounterName,
'namespace' => $sSelfClassName,
));
]);
$oAttDef = MetaModel::GetAttributeDef(KeyValueStore::class, 'value');
$aAttToLoad = array(KeyValueStore::class => array('value' => $oAttDef));
$sSql = $oFilter->MakeSelectQuery(array(), array(), $aAttToLoad);
$aAttToLoad = [KeyValueStore::class => ['value' => $oAttDef]];
$sSql = $oFilter->MakeSelectQuery([], [], $aAttToLoad);
$hResult = mysqli_query($hDBLink, $sSql);
$aCounter = mysqli_fetch_array($hResult, MYSQLI_NUM);
mysqli_free_result($hResult);
//Rebuild the filter, as the MakeSelectQuery polluted the orignal and it cannot be reused
$oFilter = DBObjectSearch::FromOQL('SELECT KeyValueStore WHERE key_name=:key_name AND namespace=:namespace', array(
$oFilter = DBObjectSearch::FromOQL('SELECT KeyValueStore WHERE key_name=:key_name AND namespace=:namespace', [
'key_name' => $sCounterName,
'namespace' => $sSelfClassName,
));
]);
if (is_null($aCounter))
{
if (null != $oNewObjectValueProvider)
{
if (is_null($aCounter)) {
if (null != $oNewObjectValueProvider) {
$iComputedValue = $oNewObjectValueProvider();
}
else
{
} else {
$iComputedValue = 0;
}
$iCurrentValue = $iComputedValue + 1;
$aQueryParams = array(
$aQueryParams = [
'key_name' => $sCounterName,
'value' => "$iCurrentValue",
'namespace' => $sSelfClassName,
);
];
$sSql = $oFilter->MakeInsertQuery($aQueryParams);
}
else
{
} else {
$iCurrentValue = (int) $aCounter[1];
$iCurrentValue++;
$aQueryParams = array(
$aQueryParams = [
'value' => "$iCurrentValue",
);
];
$sSql = $oFilter->MakeUpdateQuery($aQueryParams);
}
$hResult = mysqli_query($hDBLink, $sSql);
}
catch(Exception $e)
{
} catch (Exception $e) {
IssueLog::Error($e->getMessage());
throw $e;
}
finally
{
if ($bIsInsideTransaction)
{
} finally {
if ($bIsInsideTransaction) {
mysqli_close($hDBLink);
}
$oiTopMutex->Unlock();
@@ -157,8 +141,7 @@ final class ItopCounter
{
$sRootClass = MetaModel::GetRootClass($sLeafClass);
$oNewObjectCallback = function() use ($sRootClass)
{
$oNewObjectCallback = function () use ($sRootClass) {
$sRootTable = MetaModel::DBGetTable($sRootClass);
$sIdField = MetaModel::DBGetKey($sRootClass);
@@ -186,17 +169,14 @@ final class ItopCounter
$hDBLink = CMDBSource::GetMysqliInstance($sDBHost, $sDBUser, $sDBPwd, $sDBName, $bDBTlsEnabled, $sDBTlsCA, false);
if (!$hDBLink)
{
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
}
if (!$hDBLink) {
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), ['host' => $sDBHost, 'user' => $sDBUser]);
}
return $hDBLink;
}
}
/**
* Persistent classes for a CMDB
*
@@ -208,44 +188,43 @@ class KeyValueStore extends DBObject
{
public static function Init()
{
$aParams = array(
$aParams = [
'category' => '',
'key_type' => 'autoincrement',
'name_attcode' => array('key_name'),
'name_attcode' => ['key_name'],
'state_attcode' => '',
'reconc_keys' => array(''),
'reconc_keys' => [''],
'db_table' => 'key_value_store',
'db_key_field' => 'id',
'db_finalclass_field' => '',
'indexes' => array (
array (
'indexes' => [
[
0 => 'key_name',
1 => 'namespace',
),
),);
],
],];
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("namespace", array("allowed_values"=>null, "sql"=>'namespace', "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeString("key_name", array("allowed_values"=>null, "sql"=>'key_name', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeString("value", array("allowed_values"=>null, "sql"=>'value', "default_value"=>'0', "is_null_allowed"=>false, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeString("namespace", ["allowed_values" => null, "sql" => 'namespace', "default_value" => null, "is_null_allowed" => true, "depends_on" => [], "always_load_in_tables" => false]));
MetaModel::Init_AddAttribute(new AttributeString("key_name", ["allowed_values" => null, "sql" => 'key_name', "default_value" => '', "is_null_allowed" => false, "depends_on" => [], "always_load_in_tables" => false]));
MetaModel::Init_AddAttribute(new AttributeString("value", ["allowed_values" => null, "sql" => 'value', "default_value" => '0', "is_null_allowed" => false, "depends_on" => [], "always_load_in_tables" => false]));
MetaModel::Init_SetZListItems('details', array (
MetaModel::Init_SetZListItems('details', [
0 => 'key_name',
1 => 'value',
2 => 'namespace',
));
MetaModel::Init_SetZListItems('standard_search', array (
]);
MetaModel::Init_SetZListItems('standard_search', [
0 => 'key_name',
1 => 'value',
2 => 'namespace',
));
MetaModel::Init_SetZListItems('list', array (
]);
MetaModel::Init_SetZListItems('list', [
0 => 'key_name',
1 => 'value',
2 => 'namespace',
));
]);
;
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -40,56 +41,48 @@ class CSVBulkExport extends TabularBulkExport
{
parent::ReadParameters();
$this->aStatusInfo['separator'] = utils::ReadParam('separator', ',', true, 'raw_data');
if (strtolower($this->aStatusInfo['separator']) == 'tab')
{
if (strtolower($this->aStatusInfo['separator']) == 'tab') {
$this->aStatusInfo['separator'] = "\t";
}
else if (strtolower($this->aStatusInfo['separator']) == 'other')
{
} elseif (strtolower($this->aStatusInfo['separator']) == 'other') {
$this->aStatusInfo['separator'] = utils::ReadParam('other-separator', ',', true, 'raw_data');
}
$this->aStatusInfo['text_qualifier'] = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
if (strtolower($this->aStatusInfo['text_qualifier']) == 'other')
{
if (strtolower($this->aStatusInfo['text_qualifier']) == 'other') {
$this->aStatusInfo['text_qualifier'] = utils::ReadParam('other-text-qualifier', '"', true, 'raw_data');
}
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 0, true);
$sDateFormatRadio = utils::ReadParam('csv_date_format_radio', '');
switch($sDateFormatRadio)
{
switch ($sDateFormatRadio) {
case 'default':
// Export from the UI => format = same as is the UI
$this->aStatusInfo['date_format'] = (string)AttributeDateTime::GetFormat();
break;
// Export from the UI => format = same as is the UI
$this->aStatusInfo['date_format'] = (string)AttributeDateTime::GetFormat();
break;
case 'custom':
// Custom format specified from the UI
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
break;
// Custom format specified from the UI
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
break;
default:
// Export from the command line (or scripted) => default format is SQL, as in previous versions of iTop, unless specified otherwise
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetSQLFormat(), true, 'raw_data');
// Export from the command line (or scripted) => default format is SQL, as in previous versions of iTop, unless specified otherwise
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetSQLFormat(), true, 'raw_data');
}
}
protected function SuggestField($sClass, $sAttCode)
{
switch($sAttCode)
{
switch ($sAttCode) {
case 'id': // replace 'id' by 'friendlyname'
$sAttCode = 'friendlyname';
break;
default:
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef instanceof AttributeExternalKey)
{
if ($oAttDef instanceof AttributeExternalKey) {
$sAttCode .= '_friendlyname';
}
}
@@ -99,7 +92,7 @@ class CSVBulkExport extends TabularBulkExport
public function EnumFormParts()
{
return array_merge(parent::EnumFormParts(), array('csv_options' => array('separator', 'charset', 'text-qualifier', 'no_localize', 'formatted_text'), 'interactive_fields_csv' => array('interactive_fields_csv')));
return array_merge(parent::EnumFormParts(), ['csv_options' => ['separator', 'charset', 'text-qualifier', 'no_localize', 'formatted_text'], 'interactive_fields_csv' => ['interactive_fields_csv']]);
}
/**
@@ -128,11 +121,11 @@ class CSVBulkExport extends TabularBulkExport
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');
$sCustomDateTimeFormat = utils::ReadParam('', ',', true, 'raw_data');
$aSep = array(
$aSep = [
';' => Dict::S('UI:CSVImport:SeparatorSemicolon+'),
',' => Dict::S('UI:CSVImport:SeparatorComma+'),
'tab' => Dict::S('UI:CSVImport:SeparatorTab+'),
);
];
$sOtherSeparator = '';
if (!array_key_exists($sRawSeparator, $aSep)) {
$sOtherSeparator = $sRawSeparator;
@@ -155,10 +148,10 @@ class CSVBulkExport extends TabularBulkExport
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oFieldSetTextQualifier));
$sRawQualifier = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
$aQualifiers = array(
$aQualifiers = [
'"' => Dict::S('UI:CSVImport:QualifierDoubleQuote+'),
'\'' => Dict::S('UI:CSVImport:QualifierSimpleQuote+'),
);
];
$sOtherQualifier = '';
if (!array_key_exists($sRawQualifier, $aQualifiers)) {
$sOtherQualifier = $sRawQualifier;
@@ -230,7 +223,6 @@ class CSVBulkExport extends TabularBulkExport
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
$oFieldSetDate->AddSubBlock($oRadioCustom);
$oP->add_ready_script(
<<<EOF
$('#form_part_csv_options').on('preview_updated', function() { FormatDatesInPreview('csv', 'csv'); });
@@ -243,9 +235,8 @@ EOF
return $oPanel;
break;
default:
return parent:: GetFormPart($oP, $sPartId);
return parent::GetFormPart($oP, $sPartId);
}
}
@@ -253,8 +244,7 @@ EOF
{
if ($sAttCode != 'id') {
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
if ($oAttDef instanceof AttributeDateTime) { // AttributeDate is derived from AttributeDateTime
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
@@ -266,14 +256,13 @@ EOF
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
switch ($sAttCode) {
case 'id':
$sRet = $oObj->GetKey();
break;
default:
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
}
return $sRet;
}
@@ -285,20 +274,17 @@ EOF
$this->aStatusInfo['position'] = 0;
$this->aStatusInfo['total'] = $oSet->Count();
$aData = array();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$aData = [];
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
$aData[] = $aFieldSpec['sColLabel'];
}
$sFrom = array("\r\n", $this->aStatusInfo['text_qualifier']);
$sTo = array("\n", $this->aStatusInfo['text_qualifier'].$this->aStatusInfo['text_qualifier']);
foreach($aData as $idx => $sData)
{
$sFrom = ["\r\n", $this->aStatusInfo['text_qualifier']];
$sTo = ["\n", $this->aStatusInfo['text_qualifier'].$this->aStatusInfo['text_qualifier']];
foreach ($aData as $idx => $sData) {
// Escape and encode (if needed) the headers
$sEscaped = str_replace($sFrom, $sTo, (string)$sData);
$aData[$idx] = $this->aStatusInfo['text_qualifier'].$sEscaped.$this->aStatusInfo['text_qualifier'];
if ($this->aStatusInfo['charset'] != 'UTF-8')
{
if ($this->aStatusInfo['charset'] != 'UTF-8') {
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
// and thus to convert field by field and not the whole row or file at once (see ticket N°991)
$aData[$idx] = @iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $aData[$idx]);
@@ -325,45 +311,37 @@ EOF
$sExportDateTimeFormat = $this->aStatusInfo['date_format'];
$oPrevDateTimeFormat = AttributeDateTime::GetFormat();
$oPrevDateFormat = AttributeDate::GetFormat();
if ($sExportDateTimeFormat !== (string)$oPrevDateTimeFormat)
{
if ($sExportDateTimeFormat !== (string)$oPrevDateTimeFormat) {
// Change date & time formats
$oDateTimeFormat = new DateTimeFormat($sExportDateTimeFormat);
$oDateFormat = new DateTimeFormat($oDateTimeFormat->ToDateFormat());
AttributeDateTime::SetFormat($oDateTimeFormat);
AttributeDate::SetFormat($oDateFormat);
}
while($aRow = $oSet->FetchAssoc())
{
while ($aRow = $oSet->FetchAssoc()) {
set_time_limit(intval($iLoopTimeLimit));
$aData = array();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$aData = [];
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
$sAlias = $aFieldSpec['sAlias'];
$sAttCode = $aFieldSpec['sAttCode'];
$sField = '';
$oObj = $aRow[$sAlias];
if ($oObj != null)
{
switch($sAttCode)
{
if ($oObj != null) {
switch ($sAttCode) {
case 'id':
$sField = $oObj->GetKey();
break;
default:
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput, !$this->aStatusInfo['formatted_text']);
}
}
if ($this->aStatusInfo['charset'] != 'UTF-8')
{
if ($this->aStatusInfo['charset'] != 'UTF-8') {
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
// and thus to convert field by field and not the whole row or file at once (see ticket N°991)
$aData[] = @iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $sField);
}
else
{
} else {
$aData[] = $sField;
}
}
@@ -375,27 +353,23 @@ EOF
AttributeDate::SetFormat($oPrevDateFormat);
set_time_limit(intval($iPreviousTimeLimit));
$this->aStatusInfo['position'] += $this->iChunkSize;
if ($this->aStatusInfo['total'] == 0)
{
if ($this->aStatusInfo['total'] == 0) {
$iPercentage = 100;
}
else
{
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
} else {
$iPercentage = floor(min(100.0, 100.0 * $this->aStatusInfo['position'] / $this->aStatusInfo['total']));
}
if ($iCount < $this->iChunkSize)
{
if ($iCount < $this->iChunkSize) {
$sRetCode = 'done';
}
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
$aStatus = ['code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage];
return $sData;
}
public function GetSupportedFormats()
{
return array('csv' => Dict::S('Core:BulkExport:CSVFormat'));
return ['csv' => Dict::S('Core:BulkExport:CSVFormat')];
}
public function GetMimeType()

View File

@@ -1,10 +1,10 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
define('stSTARTING', 1); //grey zone: the type is undetermined
define('stRAW', 2); //building a non-qualified string
define('stQUALIFIED', 3); //building qualified string
@@ -19,7 +19,6 @@ define('evEND', 5);
define('NULL_VALUE', '<NULL>');
/**
* CSVParser
*
@@ -41,9 +40,9 @@ class CSVParser
}
protected $m_sCurrCell = '';
protected $m_aCurrRow = array();
protected $m_aCurrRow = [];
protected $m_iToSkip = 0;
protected $m_aDataSet = array();
protected $m_aDataSet = [];
protected function __AddChar($c)
{
@@ -55,27 +54,20 @@ class CSVParser
}
protected function __AddCell($c = null, $aFieldMap = null, $bTrimSpaces = false)
{
if ($bTrimSpaces)
{
if ($bTrimSpaces) {
$sCell = trim($this->m_sCurrCell);
}
else
{
} else {
$sCell = $this->m_sCurrCell;
}
if ($sCell == NULL_VALUE)
{
if ($sCell == NULL_VALUE) {
$sCell = null;
}
if (!is_null($aFieldMap))
{
if (!is_null($aFieldMap)) {
$iNextCol = count($this->m_aCurrRow);
$iNextName = $aFieldMap[$iNextCol];
$this->m_aCurrRow[$iNextName] = $sCell;
}
else
{
} else {
$this->m_aCurrRow[] = $sCell;
}
$this->m_sCurrCell = '';
@@ -84,33 +76,24 @@ class CSVParser
{
$this->__AddCell($c, $aFieldMap, $bTrimSpaces);
if ($this->m_iToSkip > 0)
{
if ($this->m_iToSkip > 0) {
$this->m_iToSkip--;
}
elseif (count($this->m_aCurrRow) > 1)
{
} elseif (count($this->m_aCurrRow) > 1) {
$this->m_aDataSet[] = $this->m_aCurrRow;
}
elseif (count($this->m_aCurrRow) == 1)
{
} elseif (count($this->m_aCurrRow) == 1) {
// Get the unique value
$aValues = array_values($this->m_aCurrRow);
$sValue = $aValues[0];
if (strlen($sValue) > 0)
{
$sValue = $aValues[0];
if (strlen($sValue) > 0) {
$this->m_aDataSet[] = $this->m_aCurrRow;
}
}
else
{
} else {
// blank line, skip silently
}
$this->m_aCurrRow = array();
$this->m_aCurrRow = [];
// More time for the next row
if ($this->m_iTimeLimitPerRow !== null)
{
if ($this->m_iTimeLimitPerRow !== null) {
set_time_limit(intval($this->m_iTimeLimitPerRow));
}
}
@@ -124,87 +107,71 @@ class CSVParser
$this->__AddRow($c, $aFieldMap, true);
}
function ToArray($iToSkip = 1, $aFieldMap = null, $iMax = 0)
public function ToArray($iToSkip = 1, $aFieldMap = null, $iMax = 0)
{
$aTransitions = array();
$aTransitions = [];
$aTransitions[stSTARTING][evBLANK] = array('', stSTARTING);
$aTransitions[stSTARTING][evSEPARATOR] = array('__AddCell', stSTARTING);
$aTransitions[stSTARTING][evNEWLINE] = array('__AddRow', stSTARTING);
$aTransitions[stSTARTING][evTEXTQUAL] = array('', stQUALIFIED);
$aTransitions[stSTARTING][evOTHERCHAR] = array('__AddChar', stRAW);
$aTransitions[stSTARTING][evEND] = array('__AddRow', stSTARTING);
$aTransitions[stSTARTING][evBLANK] = ['', stSTARTING];
$aTransitions[stSTARTING][evSEPARATOR] = ['__AddCell', stSTARTING];
$aTransitions[stSTARTING][evNEWLINE] = ['__AddRow', stSTARTING];
$aTransitions[stSTARTING][evTEXTQUAL] = ['', stQUALIFIED];
$aTransitions[stSTARTING][evOTHERCHAR] = ['__AddChar', stRAW];
$aTransitions[stSTARTING][evEND] = ['__AddRow', stSTARTING];
$aTransitions[stRAW][evBLANK] = array('__AddChar', stRAW);
$aTransitions[stRAW][evSEPARATOR] = array('__AddCellTrimmed', stSTARTING);
$aTransitions[stRAW][evNEWLINE] = array('__AddRowTrimmed', stSTARTING);
$aTransitions[stRAW][evTEXTQUAL] = array('__AddChar', stRAW);
$aTransitions[stRAW][evOTHERCHAR] = array('__AddChar', stRAW);
$aTransitions[stRAW][evEND] = array('__AddRowTrimmed', stSTARTING);
$aTransitions[stRAW][evBLANK] = ['__AddChar', stRAW];
$aTransitions[stRAW][evSEPARATOR] = ['__AddCellTrimmed', stSTARTING];
$aTransitions[stRAW][evNEWLINE] = ['__AddRowTrimmed', stSTARTING];
$aTransitions[stRAW][evTEXTQUAL] = ['__AddChar', stRAW];
$aTransitions[stRAW][evOTHERCHAR] = ['__AddChar', stRAW];
$aTransitions[stRAW][evEND] = ['__AddRowTrimmed', stSTARTING];
$aTransitions[stQUALIFIED][evBLANK] = array('__AddChar', stQUALIFIED);
$aTransitions[stQUALIFIED][evSEPARATOR] = array('__AddChar', stQUALIFIED);
$aTransitions[stQUALIFIED][evNEWLINE] = array('__AddChar', stQUALIFIED);
$aTransitions[stQUALIFIED][evTEXTQUAL] = array('', stESCAPED);
$aTransitions[stQUALIFIED][evOTHERCHAR] = array('__AddChar', stQUALIFIED);
$aTransitions[stQUALIFIED][evEND] = array('__AddRow', stSTARTING);
$aTransitions[stQUALIFIED][evBLANK] = ['__AddChar', stQUALIFIED];
$aTransitions[stQUALIFIED][evSEPARATOR] = ['__AddChar', stQUALIFIED];
$aTransitions[stQUALIFIED][evNEWLINE] = ['__AddChar', stQUALIFIED];
$aTransitions[stQUALIFIED][evTEXTQUAL] = ['', stESCAPED];
$aTransitions[stQUALIFIED][evOTHERCHAR] = ['__AddChar', stQUALIFIED];
$aTransitions[stQUALIFIED][evEND] = ['__AddRow', stSTARTING];
$aTransitions[stESCAPED][evBLANK] = array('', stESCAPED);
$aTransitions[stESCAPED][evSEPARATOR] = array('__AddCell', stSTARTING);
$aTransitions[stESCAPED][evNEWLINE] = array('__AddRow', stSTARTING);
$aTransitions[stESCAPED][evTEXTQUAL] = array('__AddChar', stQUALIFIED);
$aTransitions[stESCAPED][evOTHERCHAR] = array('__AddChar', stSTARTING);
$aTransitions[stESCAPED][evEND] = array('__AddRow', stSTARTING);
$aTransitions[stESCAPED][evBLANK] = ['', stESCAPED];
$aTransitions[stESCAPED][evSEPARATOR] = ['__AddCell', stSTARTING];
$aTransitions[stESCAPED][evNEWLINE] = ['__AddRow', stSTARTING];
$aTransitions[stESCAPED][evTEXTQUAL] = ['__AddChar', stQUALIFIED];
$aTransitions[stESCAPED][evOTHERCHAR] = ['__AddChar', stSTARTING];
$aTransitions[stESCAPED][evEND] = ['__AddRow', stSTARTING];
// Reset parser variables
$this->m_sCurrCell = '';
$this->m_aCurrRow = array();
$this->m_aCurrRow = [];
$this->m_iToSkip = $iToSkip;
$this->m_aDataSet = array();
$this->m_aDataSet = [];
$iDataLength = strlen($this->m_sCSVData);
$iState = stSTARTING;
$iTimeLimit = null;
if ($this->m_iTimeLimitPerRow !== null)
{
if ($this->m_iTimeLimitPerRow !== null) {
// Give some time for the first row
$iTimeLimit = ini_get('max_execution_time');
set_time_limit(intval($this->m_iTimeLimitPerRow));
}
for($i = 0; $i <= $iDataLength ; $i++)
{
if ($i == $iDataLength)
{
for ($i = 0; $i <= $iDataLength ; $i++) {
if ($i == $iDataLength) {
$c = null;
$iEvent = evEND;
}
else
{
} else {
$c = $this->m_sCSVData[$i];
if ($c == $this->m_sSep)
{
if ($c == $this->m_sSep) {
$iEvent = evSEPARATOR;
}
elseif ($c == ' ')
{
} elseif ($c == ' ') {
$iEvent = evBLANK;
}
elseif ($c == "\t")
{
} elseif ($c == "\t") {
$iEvent = evBLANK;
}
elseif ($c == "\n")
{
} elseif ($c == "\n") {
$iEvent = evNEWLINE;
}
elseif ($c == $this->m_sTextQualifier)
{
} elseif ($c == $this->m_sTextQualifier) {
$iEvent = evTEXTQUAL;
}
else
{
} else {
$iEvent = evOTHERCHAR;
}
}
@@ -212,24 +179,21 @@ class CSVParser
$sAction = $aTransitions[$iState][$iEvent][0];
$iState = $aTransitions[$iState][$iEvent][1];
if (!empty($sAction))
{
$aCallSpec = array($this, $sAction);
if (is_callable($aCallSpec))
{
if (!empty($sAction)) {
$aCallSpec = [$this, $sAction];
if (is_callable($aCallSpec)) {
call_user_func($aCallSpec, $c, $aFieldMap);
}
else
{
} else {
throw new CSVParserException("CSVParser: unknown verb '$sAction'");
}
}
$iLineCount = count($this->m_aDataSet);
if (($iMax > 0) && ($iLineCount >= $iMax)) break;
if (($iMax > 0) && ($iLineCount >= $iMax)) {
break;
}
}
if ($iTimeLimit !== null)
{
if ($iTimeLimit !== null) {
// Restore the previous time limit
set_time_limit(intval($iTimeLimit));
}
@@ -242,6 +206,3 @@ class CSVParser
return $aHeader[0];
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2024 Combodo SAS
//
// This file is part of iTop.
@@ -23,7 +24,8 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class CustomFieldsHandler {
abstract class CustomFieldsHandler
{
/** @var string $sAttCode */
protected $sAttCode;
/** @var array{
@@ -47,7 +49,8 @@ abstract class CustomFieldsHandler {
*
* @param $sAttCode
*/
final public function __construct($sAttCode) {
final public function __construct($sAttCode)
{
$this->sAttCode = $sAttCode;
$this->aValues = null;
}
@@ -59,7 +62,8 @@ abstract class CustomFieldsHandler {
* @throws \ApplicationException if {@link static::$oForm} attribute not initialized yet
* @since 3.1.0 N°6322 N°1150 Add template_id checks
*/
public function Validate(DBObject $oHostObject) {
public function Validate(DBObject $oHostObject)
{
if (false === isset($this->oForm)) {
throw new ApplicationException('oForm attribute not init yet. You must call BuildForm before this method !');
}
@@ -68,9 +72,8 @@ abstract class CustomFieldsHandler {
$this->oForm->Validate();
if ($this->oForm->GetValid()) {
$ret = true;
}
else {
$aMessages = array();
} else {
$aMessages = [];
foreach ($this->oForm->GetErrorMessages() as $sFieldId => $aFieldMessages) {
$aMessages[] = $sFieldId.': '.implode(', ', $aFieldMessages);
}
@@ -87,7 +90,8 @@ abstract class CustomFieldsHandler {
*
* @return \Combodo\iTop\Form\Form
*/
public function GetForm() {
public function GetForm()
{
return $this->oForm;
}
@@ -96,15 +100,17 @@ abstract class CustomFieldsHandler {
$this->aValues = $aValues;
}
public static function GetPrerequisiteAttributes($sClass = null) {
return array();
public static function GetPrerequisiteAttributes($sClass = null)
{
return [];
}
/**
* List the available verbs for 'GetForTemplate'
*/
public static function EnumTemplateVerbs() {
return array();
public static function EnumTemplateVerbs()
{
return [];
}
/**
@@ -169,7 +175,6 @@ abstract class CustomFieldsHandler {
return null;
}
/**
* @param DBObject $oHostObject
*

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,10 +17,9 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* data generator
* helps the consultants in creating dummy data sets, for various test purposes (validation, usability, scalability)
* helps the consultants in creating dummy data sets, for various test purposes (validation, usability, scalability)
*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -37,60 +37,50 @@ class cmdbDataGenerator
protected $m_sOrganizationCode;
protected $m_sOrganizationName;
protected $m_OrganizationDomains;
/**
* Constructor
*/
public function __construct($sOrganizationId = "")
{
global $aCompanies, $aCompaniesCode;
if ($sOrganizationId == '')
{
if ($sOrganizationId == '') {
// No organization provided, pick a random and unused one from our predefined list
$retries = 5*count($aCompanies);
while ( ($retries > 0) && !isset($this->m_sOrganizationCode)) // Stupid algorithm, but I'm too lazy to do something bulletproof tonight
{
$retries = 5 * count($aCompanies);
while (($retries > 0) && !isset($this->m_sOrganizationCode)) { // Stupid algorithm, but I'm too lazy to do something bulletproof tonight
$index = rand(0, count($aCompanies) - 1);
if (!$this->OrganizationExists($aCompanies[$index]['code']))
{
if (!$this->OrganizationExists($aCompanies[$index]['code'])) {
$this->m_sOrganizationCode = $aCompanies[$index]['code'];
$this->m_sOrganizationName = $aCompanies[$index]['name'];
$this->m_OrganizationDomains = $aCompanies[$index]['domain'];
}
$retries--;
}
}
else
{
} else {
// A code has been provided, let's take the information we need from the organization itself
$this->m_sOrganizationId = $sOrganizationId;
$oOrg = $this->GetOrganization($sOrganizationId);
if ($oOrg == null)
{
if ($oOrg == null) {
echo "Unable to find the organization '$sOrganisationCode' in the database... can not add objects into this organization.<br/>\n";
exit();
}
$this->m_sOrganizationCode = $oOrg->Get('code');
$this->m_sOrganizationName = $oOrg->Get('name');
if (!isset($aCompaniesCode[$this->m_sOrganizationCode]['domain']))
{
if (!isset($aCompaniesCode[$this->m_sOrganizationCode]['domain'])) {
// Generate some probable domain names for this organization
$this->m_OrganizationDomains = array(strtolower($this->m_sOrganizationCode).".com", strtolower($this->m_sOrganizationCode).".org", strtolower($this->m_sOrganizationCode)."corp.net",);
}
else
{
$this->m_OrganizationDomains = [strtolower($this->m_sOrganizationCode).".com", strtolower($this->m_sOrganizationCode).".org", strtolower($this->m_sOrganizationCode)."corp.net",];
} else {
// Pick the domain names for this organization from the predefined list
$this->m_OrganizationDomains = $aCompaniesCode[$this->m_sOrganizationCode]['domain'];
}
}
if (!isset($this->m_sOrganizationCode))
{
if (!isset($this->m_sOrganizationCode)) {
echo "Unable to find an organization code which is not already used... can not create a new organization. Enhance the list of fake organizations (\$aCompanies in data_sample.inc.php).<br/>\n";
exit();
}
}
/**
* Get the current organization id used by the generator
*
@@ -100,7 +90,7 @@ class cmdbDataGenerator
{
return $this->m_sOrganizationId;
}
/**
* Get the current organization id used by the generator
*
@@ -111,7 +101,7 @@ class cmdbDataGenerator
{
$this->m_sOrganizationId = $sId;
}
/**
* Get the current organization code used by the generator
*
@@ -127,69 +117,66 @@ class cmdbDataGenerator
*
* @return string The organization name
*/
function GetOrganizationName()
public function GetOrganizationName()
{
return $this->m_sOrganizationName;
}
/**
* Get a pseudo random first name taken from a (big) prefedined list
*
* @return string A random first name
*/
function GenerateFirstName()
public function GenerateFirstName()
{
global $aFirstNames;
return $aFirstNames[rand(0, count($aFirstNames) - 1)];
}
/**
* Get a pseudo random last name taken from a (big) prefedined list
*
* @return string A random last name
*/
function GenerateLastName()
public function GenerateLastName()
{
global $aNames;
return $aNames[rand(0, count($aNames) - 1)];
}
/**
* Get a pseudo random country name taken from a prefedined list
*
* @return string A random city name
*/
function GenerateCountryName()
public function GenerateCountryName()
{
global $aCountries;
return $aCountries[rand(0, count($aCountries) - 1)];
}
/**
* Get a pseudo random city name taken from a (big) prefedined list
*
* @return string A random city name
*/
function GenerateCityName()
public function GenerateCityName()
{
global $aCities;
return $aCities[rand(0, count($aCities) - 1)];
}
/**
* Get a pseudo random email address made of the first name, last name and organization's domain
*
* @return string A random email address
*/
function GenerateEmail($sFirstName, $sLastName)
public function GenerateEmail($sFirstName, $sLastName)
{
if (rand(1, 20) > 18)
{
if (rand(1, 20) > 18) {
// some people (let's say 5~10%) have an irregular email address
$sEmail = strtolower($this->CleanForEmail($sLastName))."@".strtolower($this->GenerateDomain());
}
else
{
} else {
$sEmail = strtolower($this->CleanForEmail($sFirstName)).".".strtolower($this->CleanForEmail($sLastName))."@".strtolower($this->GenerateDomain());
}
return $sEmail;
@@ -203,7 +190,7 @@ class cmdbDataGenerator
* - domain() => returns a domain name for the current organization
* - enum(aaa,bb,c,dddd) => returns randomly one of aaa,bb,c or dddd with the same
* probability of occurence. If you want to change the probability you can repeat some values
* i.e enum(most probable,most probable,most probable,most probable,most probable,rare)
* i.e enum(most probable,most probable,most probable,most probable,most probable,rare)
* - number(xxx-yyy) => a random number between xxx and yyy (bounds included)
* note that if the first number (xxx) begins with a zero, then the result will zero padded
* to the same number of digits as xxx.
@@ -215,39 +202,28 @@ class cmdbDataGenerator
* @param string $sTemplate The template used for generating the string
* @return string The generated pseudo random the string
*/
function GenerateString($sTemplate)
public function GenerateString($sTemplate)
{
$sResult = "";
$aParts = explode("\|", $sTemplate);
foreach($aParts as $sPart)
{
if (preg_match("/domain\(\)/", $sPart, $aMatches))
{
foreach ($aParts as $sPart) {
if (preg_match("/domain\(\)/", $sPart, $aMatches)) {
$sResult .= strtolower($this->GenerateDomain());
}
elseif (preg_match("/enum\((.+)\)/", $sPart, $aMatches))
{
} elseif (preg_match("/enum\((.+)\)/", $sPart, $aMatches)) {
$sEnumValues = $aMatches[1];
$aEnumValues = explode(",", $sEnumValues);
$sResult .= $aEnumValues[rand(0, count($aEnumValues) - 1)];
}
elseif (preg_match("/number\((\d+)-(\d+)\)/", $sPart, $aMatches))
{
} elseif (preg_match("/number\((\d+)-(\d+)\)/", $sPart, $aMatches)) {
$sStartNumber = $aMatches[1];
if ($sStartNumber[0] == '0')
{
if ($sStartNumber[0] == '0') {
// number must be zero padded
$sFormat = "%0".strlen($sStartNumber)."d";
}
else
{
} else {
$sFormat = "%d";
}
$sEndNumber = $aMatches[2];
$sResult .= sprintf($sFormat, rand($sStartNumber, $sEndNumber));
}
else
{
} else {
$sResult .= $sPart;
}
}
@@ -264,27 +240,22 @@ class cmdbDataGenerator
* @param string $aFilterCriteria A hash array of filterCOde => FilterValue (the strict operator '=' is used )
* @return mixed The key to an object of the given class, or null if none are found
*/
function GenerateKey($sClass, $aFilterCriteria)
public function GenerateKey($sClass, $aFilterCriteria)
{
$retKey = null;
$oFilter = new DBObjectSearch($sClass);
foreach($aFilterCriteria as $sFilterCode => $filterValue)
{
foreach ($aFilterCriteria as $sFilterCode => $filterValue) {
$oFilter->AddCondition($sFilterCode, $filterValue, '=');
}
$oSet = new CMDBObjectSet($oFilter);
if ($oSet->Count() > 0)
{
if ($oSet->Count() > 0) {
$max_count = $index = rand(1, $oSet->Count());
do
{
do {
$oObj = $oSet->Fetch();
$index--;
}
while($index > 0);
if (!is_object($oObj))
{
} while ($index > 0);
if (!is_object($oObj)) {
echo "<pre>";
echo "ERROR: non empty set, but invalid object picked! class='$sClass'\n";
echo "Index chosen: $max_count\n";
@@ -292,9 +263,7 @@ class cmdbDataGenerator
echo "Filter criteria:\n";
print_r($aFilterCriteria);
echo "</pre>";
}
else
{
} else {
$retKey = $oObj->GetKey();
}
}
@@ -305,7 +274,7 @@ class cmdbDataGenerator
// Protected methods
//
///////////////////////////////////////////////////////////////////////////////
/**
* Generate a (random) domain name consistent with the organization name & code
*
@@ -316,17 +285,14 @@ class cmdbDataGenerator
*/
protected function GenerateDomain()
{
if (is_array($this->m_OrganizationDomains))
{
$sDomain = $this->m_OrganizationDomains[rand(0, count($this->m_OrganizationDomains)-1)];
}
else
{
if (is_array($this->m_OrganizationDomains)) {
$sDomain = $this->m_OrganizationDomains[rand(0, count($this->m_OrganizationDomains) - 1)];
} else {
$sDomain = $this->m_OrganizationDomains;
}
return $sDomain;
}
/**
* Strips accented characters from a string in order to produce a suitable email address
*
@@ -335,7 +301,7 @@ class cmdbDataGenerator
*/
protected function CleanForEmail($sText)
{
return str_replace(array("'", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>"), array("", "e", "e", "e", "c", "a", "a", "n", "oe", "ae"), $sText);
return str_replace(["'", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>", "<EFBFBD>"], ["", "e", "e", "e", "c", "a", "a", "n", "oe", "ae"], $sText);
}
/**
@@ -364,11 +330,9 @@ class cmdbDataGenerator
$oFilter = new DBObjectSearch('Organization');
$oFilter->AddCondition('id', $sId, '=');
$oSet = new CMDBObjectSet($oFilter);
if ($oSet->Count() > 0)
{
if ($oSet->Count() > 0) {
$oOrg = $oSet->Fetch(); // Let's take the first one found
}
return $oOrg;
}
}
?>

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,24 +17,23 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Helper class to generate Date & Time formatting strings in the various conventions
* from the PHP DateTime::createFromFormat convention.
*
*
* Example:
*
*
* $oFormat = new DateTimeFormat('m/d/Y H:i');
* $oFormat->ToExcel();
* >> 'MM/dd/YYYY HH:mm'
*
*
* @author Denis Flaven <denis.flaven@combodo.com>
*
*/
class DateTimeFormat
{
protected $sPHPFormat;
/**
* Constructs the DateTimeFormat object
* @param string $sPHPFormat A format string using the PHP 'DateTime::createFromFormat' convention
@@ -42,7 +42,7 @@ class DateTimeFormat
{
$this->sPHPFormat = (string)$sPHPFormat;
}
/**
* @return string
*/
@@ -50,34 +50,34 @@ class DateTimeFormat
{
return $this->sPHPFormat;
}
/**
* Return the mapping table for converting between various conventions for date/time formats
*/
protected static function GetFormatMapping()
{
return array(
return [
// Days
'd' => array('regexpr' => '(0[1-9]|[1-2][0-9]|3[0-1])', 'datepicker' => 'dd', 'excel' => 'dd', 'moment' => 'DD'), // Day of the month: 2 digits (with leading zero)
'j' => array('regexpr' => '([1-9]|[1-2][0-9]|3[0-1])', 'datepicker' => 'd', 'excel' => 'd', 'moment' => 'D'), // Day of the month: 1 or 2 digits (without leading zero)
'd' => ['regexpr' => '(0[1-9]|[1-2][0-9]|3[0-1])', 'datepicker' => 'dd', 'excel' => 'dd', 'moment' => 'DD'], // Day of the month: 2 digits (with leading zero)
'j' => ['regexpr' => '([1-9]|[1-2][0-9]|3[0-1])', 'datepicker' => 'd', 'excel' => 'd', 'moment' => 'D'], // Day of the month: 1 or 2 digits (without leading zero)
// Months
'm' => array('regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'mm', 'excel' => 'MM', 'moment' => 'MM' ), // Month on 2 digits i.e. 01-12
'n' => array('regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'm', 'excel' => 'm', 'moment' => 'M'), // Month on 1 or 2 digits 1-12
'm' => ['regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'mm', 'excel' => 'MM', 'moment' => 'MM' ], // Month on 2 digits i.e. 01-12
'n' => ['regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'm', 'excel' => 'm', 'moment' => 'M'], // Month on 1 or 2 digits 1-12
// Years
'Y' => array('regexpr' => '([0-9]{4})', 'datepicker' => 'yy', 'excel' => 'YYYY', 'moment' => 'YYYY'), // Year on 4 digits
'y' => array('regexpr' => '([0-9]{2})', 'datepicker' => 'y', 'excel' => 'YY', 'moment' => 'YY'), // Year on 2 digits
'Y' => ['regexpr' => '([0-9]{4})', 'datepicker' => 'yy', 'excel' => 'YYYY', 'moment' => 'YYYY'], // Year on 4 digits
'y' => ['regexpr' => '([0-9]{2})', 'datepicker' => 'y', 'excel' => 'YY', 'moment' => 'YY'], // Year on 2 digits
// Hours
'H' => array('regexpr' => '([0-1][0-9]|2[0-3])', 'datepicker' => 'HH', 'excel' => 'HH', 'moment' => 'HH'), // Hour 00..23
'h' => array('regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'hh', 'excel' => 'hh', 'moment' => 'hh'), // Hour 01..12
'G' => array('regexpr' => '([0-9]|1[0-9]|2[0-3])', 'datepicker' => 'H', 'excel' => 'H', 'moment' => 'H'), // Hour 0..23
'g' => array('regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'h', 'excel' => 'h', 'moment' => 'h'), // Hour 1..12
'a' => array('regexpr' => '(am|pm)', 'datepicker' => 'tt', 'excel' => 'am/pm', 'moment' => 'a'),
'A' => array('regexpr' => '(AM|PM)', 'datepicker' => 'TT', 'excel' => 'AM/PM', 'moment' => 'A'),
'H' => ['regexpr' => '([0-1][0-9]|2[0-3])', 'datepicker' => 'HH', 'excel' => 'HH', 'moment' => 'HH'], // Hour 00..23
'h' => ['regexpr' => '(0[1-9]|1[0-2])', 'datepicker' => 'hh', 'excel' => 'hh', 'moment' => 'hh'], // Hour 01..12
'G' => ['regexpr' => '([0-9]|1[0-9]|2[0-3])', 'datepicker' => 'H', 'excel' => 'H', 'moment' => 'H'], // Hour 0..23
'g' => ['regexpr' => '([1-9]|1[0-2])', 'datepicker' => 'h', 'excel' => 'h', 'moment' => 'h'], // Hour 1..12
'a' => ['regexpr' => '(am|pm)', 'datepicker' => 'tt', 'excel' => 'am/pm', 'moment' => 'a'],
'A' => ['regexpr' => '(AM|PM)', 'datepicker' => 'TT', 'excel' => 'AM/PM', 'moment' => 'A'],
// Minutes
'i' => array('regexpr' => '([0-5][0-9])', 'datepicker' => 'mm', 'excel' => 'mm', 'moment' => 'mm'),
'i' => ['regexpr' => '([0-5][0-9])', 'datepicker' => 'mm', 'excel' => 'mm', 'moment' => 'mm'],
// Seconds
's' => array('regexpr' => '([0-5][0-9])', 'datepicker' => 'ss', 'excel' => 'ss', 'moment' => 'ss'),
);
's' => ['regexpr' => '([0-5][0-9])', 'datepicker' => 'ss', 'excel' => 'ss', 'moment' => 'ss'],
];
}
/**
@@ -87,57 +87,44 @@ class DateTimeFormat
* @param string $sEscapePattern The replacement string for escaping characters in the output string. %s is the source char.
* @param string $bEscapeAll True to systematically escape all litteral characters
* @param array $sSpecialChars A string containing the only characters to escape in the output
* @return string The string in the requested format
* @return string The string in the requested format
*/
protected function Transform($sOutputFormatCode, $sEscapePattern, $bEscapeAll = false, $sSpecialChars = '')
{
$aMappings = static::GetFormatMapping();
$sResult = '';
$bEscaping = false;
for($i=0; $i < strlen($this->sPHPFormat); $i++)
{
if (($this->sPHPFormat[$i] == '\\'))
{
for ($i = 0; $i < strlen($this->sPHPFormat); $i++) {
if (($this->sPHPFormat[$i] == '\\')) {
$bEscaping = true;
continue;
}
if ($bEscaping)
{
if (($sSpecialChars === '') || (strpos($sSpecialChars, $this->sPHPFormat[$i]) !== false))
{
if ($bEscaping) {
if (($sSpecialChars === '') || (strpos($sSpecialChars, $this->sPHPFormat[$i]) !== false)) {
$sResult .= sprintf($sEscapePattern, $this->sPHPFormat[$i]);
}
else
{
} else {
$sResult .= $this->sPHPFormat[$i];
}
$bEscaping = false;
}
else if(array_key_exists($this->sPHPFormat[$i], $aMappings))
{
} elseif (array_key_exists($this->sPHPFormat[$i], $aMappings)) {
// Not a litteral value, must be replaced by its regular expression pattern
$sResult .= $aMappings[$this->sPHPFormat[$i]][$sOutputFormatCode];
}
else
{
if ($bEscapeAll || (strpos($sSpecialChars, $this->sPHPFormat[$i]) !== false))
{
} else {
if ($bEscapeAll || (strpos($sSpecialChars, $this->sPHPFormat[$i]) !== false)) {
$sResult .= sprintf($sEscapePattern, $this->sPHPFormat[$i]);
}
else
{
} else {
// Normal char with no special meaning, no need to escape it
$sResult .= $this->sPHPFormat[$i];
}
}
}
return $sResult;
}
return $sResult;
}
/**
* Format a date into the supplied format string
* @param mixed $date An int, string, DateTime object or null !!
@@ -146,38 +133,27 @@ class DateTimeFormat
*/
public function Format($date)
{
if ($date == null)
{
if ($date == null) {
$sDate = '';
}
else if (($date === '0000-00-00') || ($date === '0000-00-00 00:00:00'))
{
} elseif (($date === '0000-00-00') || ($date === '0000-00-00 00:00:00')) {
$sDate = '';
}
else if ($date instanceof DateTime)
{
} elseif ($date instanceof DateTime) {
// Parameter is a DateTime
$sDate = $date->format($this->sPHPFormat);
}
else if (is_int($date))
{
} elseif (is_int($date)) {
// Parameter is a Unix timestamp
$oDate = new DateTime();
$oDate->setTimestamp($date);
$sDate = $oDate->format($this->sPHPFormat);
}
else if (is_string($date))
{
} elseif (is_string($date)) {
$oDate = new DateTime($date);
$sDate = $oDate->format($this->sPHPFormat);
}
else
{
} else {
throw new Exception(__CLASS__."::Format: Unexpected date value: ".print_r($date, true));
}
return $sDate;
}
/**
* Parse a date in the supplied format and return the date as a string in the internal format
* @param string $sDate The string to parse
@@ -187,22 +163,18 @@ class DateTimeFormat
*/
public function Parse($sDate)
{
if (($sDate == null) || ($sDate == '0000-00-00 00:00:00') || ($sDate == '0000-00-00'))
{
return null;
}
else
{
if (($sDate == null) || ($sDate == '0000-00-00 00:00:00') || ($sDate == '0000-00-00')) {
return null;
} else {
$sFormat = preg_replace('/\\?/', '', $this->sPHPFormat); // replace escaped characters by a wildcard for parsing
$oDate = DateTime::createFromFormat($this->sPHPFormat, $sDate);
if ($oDate === false)
{
if ($oDate === false) {
throw new Exception(__CLASS__."::Parse: Unable to parse the date: '$sDate' using the format: '{$this->sPHPFormat}'");
}
return $oDate;
}
}
/**
* Get the date or datetime format string in the jQuery UI date picker format
* @return string The format string using the date picker convention
@@ -211,7 +183,7 @@ class DateTimeFormat
{
return $this->Transform('datepicker', "'%s'");
}
/**
* Get a date or datetime format string in the Excel format
* @return string The format string using the Excel convention
@@ -220,7 +192,7 @@ class DateTimeFormat
{
return $this->Transform('excel', "%s");
}
/**
* Get a date or datetime format string in the moment.js format
* @return string The format string using the moment.js convention
@@ -229,16 +201,15 @@ class DateTimeFormat
{
return $this->Transform('moment', "[%s]", true /* escape all */);
}
public static function GetJSSQLToCustomFormat()
{
$aPHPToMoment = array();
foreach(self::GetFormatMapping() as $sPHPCode => $aMapping)
{
$aPHPToMoment = [];
foreach (self::GetFormatMapping() as $sPHPCode => $aMapping) {
$aPHPToMoment[$sPHPCode] = $aMapping['moment'];
}
$sJSMapping = json_encode($aPHPToMoment);
$sFunction =
<<<EOF
function PHPDateTimeFormatToSubFormat(sPHPFormat, sPlaceholders)
@@ -325,7 +296,7 @@ EOF
;
return $sFunction;
}
/**
* Get a placeholder text for a date or datetime format string
* @return string The placeholder text (localized)
@@ -334,34 +305,27 @@ EOF
{
$aMappings = static::GetFormatMapping();
$sResult = '';
$bEscaping = false;
for($i=0; $i < strlen($this->sPHPFormat); $i++)
{
if (($this->sPHPFormat[$i] == '\\'))
{
for ($i = 0; $i < strlen($this->sPHPFormat); $i++) {
if (($this->sPHPFormat[$i] == '\\')) {
$bEscaping = true;
continue;
}
if ($bEscaping)
{
if ($bEscaping) {
$sResult .= $this->sPHPFormat[$i]; // No need to escape characters in the placeholder
$bEscaping = false;
}
else if(array_key_exists($this->sPHPFormat[$i], $aMappings))
{
} elseif (array_key_exists($this->sPHPFormat[$i], $aMappings)) {
// Not a litteral value, must be replaced by Dict equivalent
$sResult .= Dict::S('Core:DateTime:Placeholder_'.$this->sPHPFormat[$i]);
}
else
{
} else {
// Normal char with no special meaning
$sResult .= $this->sPHPFormat[$i];
}
}
return $sResult;
}
@@ -373,14 +337,11 @@ EOF
{
$iStart = 999;
$iEnd = 0;
foreach($aPlaceholders as $sChar)
{
foreach ($aPlaceholders as $sChar) {
$iPos = strpos($this->sPHPFormat, $sChar);
if ($iPos !== false)
{
if (($iPos > 0) && ($this->sPHPFormat[$iPos-1] == '\\'))
{
if ($iPos !== false) {
if (($iPos > 0) && ($this->sPHPFormat[$iPos - 1] == '\\')) {
// The placeholder is actually escaped, it's a litteral character, ignore it
continue;
}
@@ -391,25 +352,25 @@ EOF
$sFormat = substr($this->sPHPFormat, $iStart, $iEnd - $iStart + 1);
return $sFormat;
}
/**
* Produces the Date format string by extracting only the date part of the date and time format string
* @return string
*/
public function ToDateFormat()
{
return $this->ToSubFormat(array('Y', 'y', 'd', 'j', 'm', 'n'));
return $this->ToSubFormat(['Y', 'y', 'd', 'j', 'm', 'n']);
}
/**
* Produces the Time format string by extracting only the time part of the date and time format string
* @return string
*/
public function ToTimeFormat()
{
return $this->ToSubFormat(array('H', 'h', 'G', 'g', 'i', 's', 'a', 'A'));
return $this->ToSubFormat(['H', 'h', 'G', 'g', 'i', 's', 'a', 'A']);
}
/**
* Get the regular expression to (approximately) validate a date/time for the current format
* The validation does not take into account the number of days in a month (i.e. June 31st will pass, as well as Feb 30th!)
@@ -419,8 +380,7 @@ EOF
public function ToRegExpr($sDelimiter = null)
{
$sRet = '^'.$this->Transform('regexpr', "\\%s", false /* escape all */, '.?*$^()[]:').'$';
if ($sDelimiter !== null)
{
if ($sDelimiter !== null) {
$sRet = $sDelimiter.str_replace($sDelimiter, '\\'.$sDelimiter, $sRet).$sDelimiter;
}
return $sRet;

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,9 +17,8 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* A set of persistent objects, could be heterogeneous as long as the objects in the set have a common ancestor class
* A set of persistent objects, could be heterogeneous as long as the objects in the set have a common ancestor class
*
* @package iTopORM
* @copyright Copyright (C) 2010-2024 Combodo SAS

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Database properties - manage database instances in a complex installation
*
@@ -24,9 +24,8 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* A database property
* A database property
*
* @package iTopORM
*/
@@ -34,40 +33,37 @@ class DBProperty extends DBObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "cloud",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_db_properties",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("value", array("allowed_values"=>null, "sql"=>"value", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("name", ["allowed_values" => null, "sql" => "name", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("description", ["allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("value", ["allowed_values" => null, "sql" => "value", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", array("allowed_values"=>null, "sql"=>"change_date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("change_comment", array("allowed_values"=>null, "sql"=>"change_comment", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", ["allowed_values" => null, "sql" => "change_date", "default_value" => "NOW()", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("change_comment", ["allowed_values" => null, "sql" => "change_comment", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
}
/**
* Helper to check wether the table has been created into the DB
* Helper to check wether the table has been created into the DB
* (this table did not exist in 1.0.1 and older versions)
*/
public static function IsInstalled()
{
$sTable = MetaModel::DBGetTable(__CLASS__);
if (CMDBSource::IsTable($sTable))
{
if (CMDBSource::IsTable($sTable)) {
return true;
}
else
{
} else {
return false;
}
return false;
@@ -75,12 +71,10 @@ class DBProperty extends DBObject
public static function SetProperty($sName, $sValue, $sComment = '', $sDescription = null)
{
try
{
try {
$oSearch = DBObjectSearch::FromOQL('SELECT DBProperty WHERE name = :name');
$oSet = new DBObjectSet($oSearch, array(), array('name' => $sName));
if ($oSet->Count() == 0)
{
$oSet = new DBObjectSet($oSearch, [], ['name' => $sName]);
if ($oSet->Count() == 0) {
$oProp = new DBProperty();
$oProp->Set('name', $sName);
$oProp->Set('description', $sDescription);
@@ -88,31 +82,23 @@ class DBProperty extends DBObject
$oProp->Set('change_date', time());
$oProp->Set('change_comment', $sComment);
$oProp->DBInsert();
}
elseif ($oSet->Count() == 1)
{
} elseif ($oSet->Count() == 1) {
$oProp = $oSet->fetch();
if (!is_null($sDescription))
{
if (!is_null($sDescription)) {
$oProp->Set('description', $sDescription);
}
$oProp->Set('value', $sValue);
$oProp->Set('change_date', time());
$oProp->Set('change_comment', $sComment);
$oProp->DBUpdate();
}
else
{
} else {
// Houston...
throw new CoreException('duplicate db property');
}
}
catch (MySQLException $e)
{
} catch (MySQLException $e) {
// This might be because the table could not be found,
// let's check it and discard silently if this is really the case
if (self::IsInstalled())
{
if (self::IsInstalled()) {
throw $e;
}
IssueLog::Error('Attempting to write a DBProperty while the module has not been installed');
@@ -121,34 +107,25 @@ class DBProperty extends DBObject
public static function GetProperty($sName, $default = null)
{
try
{
try {
$oSearch = DBObjectSearch::FromOQL('SELECT DBProperty WHERE name = :name');
$oSet = new DBObjectSet($oSearch, array(), array('name' => $sName));
$oSet = new DBObjectSet($oSearch, [], ['name' => $sName]);
$iCount = $oSet->Count();
if ($iCount == 0)
{
if ($iCount == 0) {
//throw new CoreException('unknown db property', array('name' => $sName));
$sValue = $default;
}
elseif ($iCount == 1)
{
} elseif ($iCount == 1) {
$oProp = $oSet->fetch();
$sValue = $oProp->Get('value');
}
else
{
} else {
// $iCount > 1
// Houston...
throw new CoreException('duplicate db property', array('name' => $sName, 'count' => $iCount));
throw new CoreException('duplicate db property', ['name' => $sName, 'count' => $iCount]);
}
}
catch (MySQLException $e)
{
} catch (MySQLException $e) {
// This might be because the table could not be found,
// let's check it and discard silently if this is really the case
if (self::IsInstalled())
{
if (self::IsInstalled()) {
throw $e;
}
$sValue = $default;
@@ -156,5 +133,3 @@ class DBProperty extends DBObject
return $sValue;
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -9,7 +10,7 @@
*/
/**
* Deletion plan (other objects to be deleted/modified, eventual issues, etc.)
* Deletion plan (other objects to be deleted/modified, eventual issues, etc.)
*
* @package iTopORM
*/
@@ -25,34 +26,34 @@ class DeletionPlan
protected $m_iToDelete;
protected $m_iToUpdate;
protected $m_aToDelete;
protected $m_aToUpdate;
protected $m_aToDelete;
protected $m_aToUpdate;
protected static $m_aModeUpdate = array(
DEL_SILENT => array(
protected static $m_aModeUpdate = [
DEL_SILENT => [
DEL_SILENT => DEL_SILENT,
DEL_AUTO => DEL_AUTO,
DEL_MANUAL => DEL_MANUAL
),
DEL_MANUAL => array(
DEL_MANUAL => DEL_MANUAL,
],
DEL_MANUAL => [
DEL_SILENT => DEL_MANUAL,
DEL_AUTO => DEL_AUTO,
DEL_MANUAL => DEL_MANUAL
),
DEL_AUTO => array(
DEL_MANUAL => DEL_MANUAL,
],
DEL_AUTO => [
DEL_SILENT => DEL_AUTO,
DEL_AUTO => DEL_AUTO,
DEL_MANUAL => DEL_AUTO
)
);
DEL_MANUAL => DEL_AUTO,
],
];
public function __construct()
{
$this->m_iToDelete = 0;
$this->m_iToUpdate = 0;
$this->m_aToDelete = array();
$this->m_aToUpdate = array();
$this->m_aToDelete = [];
$this->m_aToUpdate = [];
$this->m_bFoundStopper = false;
$this->m_bFoundSecurityIssue = false;
@@ -65,22 +66,17 @@ class DeletionPlan
$this->m_iToDelete = 0;
$this->m_iToUpdate = 0;
foreach($this->m_aToDelete as $sClass => $aToDelete)
{
foreach($aToDelete as $iId => $aData)
{
foreach ($this->m_aToDelete as $sClass => $aToDelete) {
foreach ($aToDelete as $iId => $aData) {
$this->m_iToDelete++;
if (isset($aData['issue']))
{
if (isset($aData['issue'])) {
$this->m_bFoundStopper = true;
$this->m_bFoundManualOperation = true;
if (isset($aData['issue_security']))
{
if (isset($aData['issue_security'])) {
$this->m_bFoundSecurityIssue = true;
}
}
if ($aData['mode'] == DEL_MANUAL)
{
if ($aData['mode'] == DEL_MANUAL) {
$this->m_aToDelete[$sClass][$iId]['issue'] = $sClass.'::'.$iId.' '.Dict::S('UI:Delete:MustBeDeletedManually');
$this->m_bFoundStopper = true;
$this->m_bFoundManualDelete = true;
@@ -92,30 +88,25 @@ class DeletionPlan
// www.php.net/manual/fr/function.set-time-limit.php#72305
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
foreach($this->m_aToUpdate as $sClass => $aToUpdate)
{
foreach($aToUpdate as $iId => $aData)
{
foreach ($this->m_aToUpdate as $sClass => $aToUpdate) {
foreach ($aToUpdate as $iId => $aData) {
set_time_limit(intval($iLoopTimeLimit));
$this->m_iToUpdate++;
$oObject = $aData['to_reset'];
$aExtKeyLabels = array();
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef)
{
$aExtKeyLabels = [];
foreach ($aData['attributes'] as $sRemoteExtKey => $aRemoteAttDef) {
$oObject->Set($sRemoteExtKey, $aData['values'][$sRemoteExtKey]);
$aExtKeyLabels[] = $aRemoteAttDef->GetLabel();
}
$this->m_aToUpdate[$sClass][$iId]['attributes_list'] = implode(', ', $aExtKeyLabels);
$this->m_aToUpdate[$sClass][$iId]['attributes_list'] = implode(', ', $aExtKeyLabels);
list($bRes, $aIssues, $bSecurityIssues) = $oObject->CheckToWrite();
if (!$bRes)
{
if (!$bRes) {
$this->m_aToUpdate[$sClass][$iId]['issue'] = implode(', ', $aIssues);
$this->m_bFoundStopper = true;
if ($bSecurityIssues)
{
if ($bSecurityIssues) {
$this->m_aToUpdate[$sClass][$iId]['issue_security'] = true;
$this->m_bFoundSecurityIssue = true;
}
@@ -127,23 +118,17 @@ class DeletionPlan
public function GetIssues()
{
$aIssues = array();
foreach ($this->m_aToDelete as $sClass => $aToDelete)
{
foreach ($aToDelete as $iId => $aData)
{
if (isset($aData['issue']))
{
$aIssues = [];
foreach ($this->m_aToDelete as $sClass => $aToDelete) {
foreach ($aToDelete as $iId => $aData) {
if (isset($aData['issue'])) {
$aIssues[] = $aData['issue'];
}
}
}
foreach ($this->m_aToUpdate as $sClass => $aToUpdate)
{
foreach ($aToUpdate as $iId => $aData)
{
if (isset($aData['issue']))
{
foreach ($this->m_aToUpdate as $sClass => $aToUpdate) {
foreach ($aToUpdate as $iId => $aData) {
if (isset($aData['issue'])) {
$aIssues[] = $aData['issue'];
}
}
@@ -192,63 +177,50 @@ class DeletionPlan
public function AddToDelete($oObject, $iDeletionMode = null)
{
if (is_null($iDeletionMode))
{
if (is_null($iDeletionMode)) {
$bRequestedExplicitely = true;
$iDeletionMode = DEL_AUTO;
}
else
{
} else {
$bRequestedExplicitely = false;
}
$sClass = get_class($oObject);
$iId = $oObject->GetKey();
if (isset($this->m_aToUpdate[$sClass][$iId]))
{
if (isset($this->m_aToUpdate[$sClass][$iId])) {
unset($this->m_aToUpdate[$sClass][$iId]);
}
if (isset($this->m_aToDelete[$sClass][$iId]))
{
if ($this->m_aToDelete[$sClass][$iId]['requested_explicitely'])
{
if (isset($this->m_aToDelete[$sClass][$iId])) {
if ($this->m_aToDelete[$sClass][$iId]['requested_explicitely']) {
// No change: let it in mode DEL_AUTO
}
else
{
} else {
$iPrevDeletionMode = $this->m_aToDelete[$sClass][$iId]['mode'];
$iNewDeletionMode = self::$m_aModeUpdate[$iPrevDeletionMode][$iDeletionMode];
$this->m_aToDelete[$sClass][$iId]['mode'] = $iNewDeletionMode;
if ($bRequestedExplicitely)
{
if ($bRequestedExplicitely) {
// This object was in the root list
$this->m_aToDelete[$sClass][$iId]['requested_explicitely'] = true;
$this->m_aToDelete[$sClass][$iId]['mode'] = DEL_AUTO;
}
}
}
else
{
$this->m_aToDelete[$sClass][$iId] = array(
} else {
$this->m_aToDelete[$sClass][$iId] = [
'to_delete' => $oObject,
'mode' => $iDeletionMode,
'requested_explicitely' => $bRequestedExplicitely,
);
];
}
}
public function SetDeletionIssues($oObject, $aIssues, $bSecurityIssue)
{
if (count($aIssues ?? []) > 0)
{
if (count($aIssues ?? []) > 0) {
$sClass = get_class($oObject);
$iId = $oObject->GetKey();
$this->m_aToDelete[$sClass][$iId]['issue'] = implode(', ', $aIssues);
if ($bSecurityIssue)
{
if ($bSecurityIssue) {
$this->m_aToDelete[$sClass][$iId]['issue_security'] = true;
}
}
@@ -258,21 +230,16 @@ class DeletionPlan
{
$sClass = get_class($oObject);
$iId = $oObject->GetKey();
if (isset($this->m_aToDelete[$sClass][$iId]))
{
if (isset($this->m_aToDelete[$sClass][$iId])) {
// skip... it should be deleted anyhow !
}
else
{
if (!isset($this->m_aToUpdate[$sClass][$iId]))
{
$this->m_aToUpdate[$sClass][$iId] = array(
} else {
if (!isset($this->m_aToUpdate[$sClass][$iId])) {
$this->m_aToUpdate[$sClass][$iId] = [
'to_reset' => $oObject,
);
];
}
$this->m_aToUpdate[$sClass][$iId]['attributes'][$oAttDef->GetCode()] = $oAttDef;
$this->m_aToUpdate[$sClass][$iId]['values'][$oAttDef->GetCode()] = $value;
}
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (c) 2010-2024 Combodo SAS
*
@@ -46,11 +47,9 @@ use utils;
*/
class DesignDocument extends DOMDocument
{
/** To fix DOMNode::getLineNo() ref https://www.php.net/manual/en/domnode.getlineno.php */
public const XML_PARSE_BIG_LINES = 4194304;
/**
* @throws \Exception
*/
@@ -135,13 +134,10 @@ class DesignDocument extends DOMDocument
*/
public static function XPathQuote($sValue)
{
if (strpos($sValue, '"') !== false)
{
if (strpos($sValue, '"') !== false) {
$aParts = explode('"', $sValue);
$sRet = 'concat("'.implode('", \'"\', "', $aParts).'")';
}
else
{
} else {
$sRet = '"'.$sValue.'"';
}
return $sRet;
@@ -156,12 +152,9 @@ class DesignDocument extends DOMDocument
public function GetNodes($sXPath, $oContextNode = null)
{
$oXPath = new \DOMXPath($this);
if (is_null($oContextNode))
{
if (is_null($oContextNode)) {
$oResult = $oXPath->query($sXPath);
}
else
{
} else {
$oResult = $oXPath->query($sXPath, $oContextNode);
}
return $oResult;
@@ -174,8 +167,12 @@ class DesignDocument extends DOMDocument
*/
public static function GetItopNodePath($oNode)
{
if ($oNode instanceof \DOMDocument) return '';
if (is_null($oNode)) return '';
if ($oNode instanceof \DOMDocument) {
return '';
}
if (is_null($oNode)) {
return '';
}
$sId = $oNode->getAttribute('id');
$sNodeDesc = ($sId != '') ? $oNode->nodeName.'['.$sId.']' : $oNode->nodeName;
@@ -303,16 +300,13 @@ class DesignElement extends \DOMElement
public function GetUniqueElement($sTagName, $bMustExist = true)
{
$oNode = null;
foreach($this->childNodes as $oChildNode)
{
if ($oChildNode->nodeName == $sTagName)
{
foreach ($this->childNodes as $oChildNode) {
if ($oChildNode->nodeName == $sTagName) {
$oNode = $oChildNode;
break;
}
}
if ($bMustExist && is_null($oNode))
{
if ($bMustExist && is_null($oNode)) {
throw new DOMFormatException('Missing unique tag: '.$sTagName);
}
return $oNode;
@@ -337,20 +331,17 @@ class DesignElement extends \DOMElement
public function GetText($sDefault = null)
{
$sText = null;
foreach($this->childNodes as $oChildNode)
{
if ($oChildNode instanceof \DOMText)
{
if (is_null($sText)) $sText = '';
foreach ($this->childNodes as $oChildNode) {
if ($oChildNode instanceof \DOMText) {
if (is_null($sText)) {
$sText = '';
}
$sText .= $oChildNode->wholeText;
}
}
if (is_null($sText))
{
if (is_null($sText)) {
return $sDefault;
}
else
{
} else {
return $sText;
}
}
@@ -367,8 +358,7 @@ class DesignElement extends \DOMElement
public function GetChildText($sTagName, $sDefault = null)
{
$sRet = $sDefault;
if ($oChild = $this->GetOptionalElement($sTagName))
{
if ($oChild = $this->GetOptionalElement($sTagName)) {
$sRet = $oChild->GetText($sDefault);
}
return $sRet;
@@ -427,7 +417,6 @@ class DesignElement extends \DOMElement
return self::_FindNode($this, $oRefNode, $sSearchId);
}
/**
* Find the child node matching the given node.
* UNSAFE: may return nodes marked as _alteration="removed"
@@ -482,32 +471,25 @@ class DesignElement extends \DOMElement
*/
public static function _FindNodes(DOMNode $oParent, DesignElement $oRefNode, string $sSearchId = null)
{
if ($oParent instanceof DOMDocument)
{
if ($oParent instanceof DOMDocument) {
$oDoc = $oParent->firstChild->ownerDocument;
$oRoot = $oParent;
}
else
{
} else {
$oDoc = $oParent->ownerDocument;
$oRoot = $oParent;
}
$oXPath = new DOMXPath($oDoc);
if ($oRefNode->hasAttribute('id'))
{
if ($oRefNode->hasAttribute('id')) {
// Find the elements having the same tag name and id
if (!$sSearchId)
{
if (!$sSearchId) {
$sSearchId = $oRefNode->getAttribute('id');
}
$sQuotedId = DesignDocument::XPathQuote($sSearchId);
$sXPath = './'.$oRefNode->tagName."[@id=$sQuotedId]";
$oRes = $oXPath->query($sXPath, $oRoot);
}
else
{
} else {
// Get the elements having the same tag name
$sXPath = './'.$oRefNode->tagName;

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -16,12 +17,10 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
define('DICT_ERR_STRING', 1); // when a string is missing, return the identifier
define('DICT_ERR_EXCEPTION', 2); // when a string is missing, throw an exception
//define('DICT_ERR_LOG', 3); // when a string is missing, log an error
/**
* Class Dict
* Management of localizable strings
@@ -32,8 +31,8 @@ class Dict
protected static $m_sDefaultLanguage = 'EN US';
protected static $m_sCurrentLanguage = null; // No language selected by default
protected static $m_aLanguages = array(); // array( code => array( 'description' => '...', 'localized_description' => '...') ...)
protected static $m_aData = array();
protected static $m_aLanguages = []; // array( code => array( 'description' => '...', 'localized_description' => '...') ...)
protected static $m_aData = [];
protected static $m_sApplicationPrefix = null;
/** @var \ApcService $m_oApcService */
protected static $m_oApcService = null;
@@ -45,8 +44,7 @@ class Dict
*/
public static function SetDefaultLanguage($sLanguageCode)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages)) {
throw new DictExceptionUnknownLanguage($sLanguageCode);
}
self::$m_sDefaultLanguage = $sLanguageCode;
@@ -60,18 +58,15 @@ class Dict
*/
public static function SetUserLanguage($sLanguageCode = null)
{
if (!is_null($sLanguageCode) && !array_key_exists($sLanguageCode, self::$m_aLanguages))
{
if (!is_null($sLanguageCode) && !array_key_exists($sLanguageCode, self::$m_aLanguages)) {
throw new DictExceptionUnknownLanguage($sLanguageCode);
}
self::$m_sCurrentLanguage = $sLanguageCode;
}
public static function GetUserLanguage()
{
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e. login screen, non-authenticated page)
{
if (self::$m_sCurrentLanguage == null) { // May happen when no user is logged in (i.e. login screen, non-authenticated page)
// In which case let's use the default language
return self::$m_sDefaultLanguage;
}
@@ -99,8 +94,7 @@ class Dict
public static function Exists($sStringCode)
{
$sImpossibleString = 'aVlHYKEI3TZuDV5o0pghv7fvhYNYuzYkTk7WL0Zoqw8rggE7aq';
if (static::S($sStringCode, $sImpossibleString) === $sImpossibleString)
{
if (static::S($sStringCode, $sImpossibleString) === $sImpossibleString) {
return false;
}
return true;
@@ -139,26 +133,22 @@ class Dict
$sLangCode = self::GetUserLanguage();
self::InitLangIfNeeded($sLangCode);
if (! array_key_exists($sLangCode, self::$m_aData))
{
if (! array_key_exists($sLangCode, self::$m_aData)) {
IssueLog::Warning("Cannot find $sLangCode in all registered dictionaries.");
// It may happen, when something happens before the dictionaries get loaded
return [ 'label' => $sStringCode, 'lang' => $sLangCode ];
}
$aCurrentDictionary = self::$m_aData[$sLangCode];
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
{
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary)) {
return [ 'label' => $aCurrentDictionary[$sStringCode], 'lang' => $sLangCode ];
}
if (!$bUserLanguageOnly)
{
if (!$bUserLanguageOnly) {
// Attempt to find the string in the default language
//
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
{
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary)) {
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => self::$m_sDefaultLanguage ];
}
// Attempt to find the string in english
@@ -166,22 +156,19 @@ class Dict
self::InitLangIfNeeded('EN US');
$aDefaultDictionary = self::$m_aData['EN US'];
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
{
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary)) {
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => 'EN US' ];
}
}
// Could not find the string...
//
if (is_null($sDefault))
{
if (is_null($sDefault)) {
return [ 'label' => $sStringCode, 'lang' => null ];
}
return [ 'label' => $sDefault, 'lang' => null ];
}
/**
* Formats a localized string with numbered placeholders (%1$s...) for the additional arguments
* See vsprintf for more information about the syntax of the placeholders
@@ -199,15 +186,14 @@ class Dict
$aArguments = func_get_args();
array_shift($aArguments);
if ($sLocalizedFormat == $sFormatCode)
{
if ($sLocalizedFormat == $sFormatCode) {
// Make sure the information will be displayed (ex: an error occurring before the dictionary gets loaded)
return $sFormatCode.' - '.implode(', ', $aArguments);
}
try{
try {
return utils::VSprintf($sLocalizedFormat, $aArguments);
} catch(\Throwable $e){
} catch (\Throwable $e) {
\IssueLog::Error("Cannot format dict key", null, ["sFormatCode" => $sFormatCode, "sLangCode" => $sLangCode, 'exception_msg' => $e->getMessage() ]);
return $sFormatCode.' - '.implode(', ', $aArguments);
}
@@ -236,8 +222,9 @@ class Dict
* @since 2.7.6 N°4125
* @return \ApcService
*/
public static function GetApcService() {
if (self::$m_oApcService === null){
public static function GetApcService()
{
if (self::$m_oApcService === null) {
self::$m_oApcService = new ApcService();
}
return self::$m_oApcService;
@@ -247,7 +234,8 @@ class Dict
* @since 2.7.6 N°4125
* @param \ApcService $m_oApcService
*/
public static function SetApcService($oApcService) {
public static function SetApcService($oApcService)
{
self::$m_oApcService = $oApcService;
}
@@ -258,19 +246,20 @@ class Dict
*/
public static function InitLangIfNeeded($sLangCode)
{
if (array_key_exists($sLangCode, self::$m_aData)) return true;
if (array_key_exists($sLangCode, self::$m_aData)) {
return true;
}
$bResult = false;
if (self::GetApcService()->function_exists('apc_fetch')
&& (self::$m_sApplicationPrefix !== null))
{
&& (self::$m_sApplicationPrefix !== null)) {
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
self::$m_aData[$sLangCode] = self::GetApcService()->apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode);
if (self::$m_aData[$sLangCode] === false) {
unset(self::$m_aData[$sLangCode]);
} else if (! is_array(self::$m_aData[$sLangCode])) {
} elseif (! is_array(self::$m_aData[$sLangCode])) {
// N°4125: we don't fix dictionary corrupted cache (on iTop side).
// but we log an error in a dedicated channel to let itop administrator be aware of a potential APCu issue to fix.
IssueLog::Error("APCu corrupted data (with $sLangCode dictionary). APCu configuration and running version should be troubleshooted...", LogChannels::APC);
@@ -279,14 +268,12 @@ class Dict
$bResult = true;
}
}
if (!$bResult)
{
if (!$bResult) {
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
require_once($sDictFile);
if (self::GetApcService()->function_exists('apc_store')
&& (self::$m_sApplicationPrefix !== null))
{
&& (self::$m_sApplicationPrefix !== null)) {
self::GetApcService()->apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]);
}
$bResult = true;
@@ -309,10 +296,8 @@ class Dict
*/
public static function ResetCache($sApplicationPrefix)
{
if (function_exists('apc_delete'))
{
foreach(self::$m_aLanguages as $sLang => $void)
{
if (function_exists('apc_delete')) {
foreach (self::$m_aLanguages as $sLang => $void) {
apc_delete($sApplicationPrefix.'-dict-'.$sLang);
}
}
@@ -320,7 +305,6 @@ class Dict
/////////////////////////////////////////////////////////////////////////
/**
* Clone a string in every language (if it exists in that language)
*
@@ -330,8 +314,8 @@ class Dict
*/
public static function CloneString($sSourceCode, $sDestCode)
{
foreach(self::$m_aLanguages as $sLanguageCode => $foo) {
if (isset(self::$m_aData[$sLanguageCode][$sSourceCode]) && !isset(self::$m_aData[$sLanguageCode][$sDestCode] )) {
foreach (self::$m_aLanguages as $sLanguageCode => $foo) {
if (isset(self::$m_aData[$sLanguageCode][$sSourceCode]) && !isset(self::$m_aData[$sLanguageCode][$sDestCode])) {
self::$m_aData[$sLanguageCode][$sDestCode] = self::$m_aData[$sLanguageCode][$sSourceCode];
}
}
@@ -339,40 +323,31 @@ class Dict
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
{
$aMissing = array(); // Strings missing for the target language
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
$aNotTranslated = array(); // Strings having the same value in both dictionaries
$aOK = array(); // Strings having different values in both dictionaries
$aMissing = []; // Strings missing for the target language
$aUnexpected = []; // Strings defined for the target language, but not found in the reference dictionary
$aNotTranslated = []; // Strings having the same value in both dictionaries
$aOK = []; // Strings having different values in both dictionaries
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
{
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue) {
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode])) {
$aMissing[$sStringCode] = $sValue;
}
}
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
{
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue) {
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef])) {
$aUnexpected[$sStringCode] = $sValue;
}
else
{
} else {
// The value exists in the reference
$sRefValue = self::$m_aData[$sLanguageRef][$sStringCode];
if ($sValue == $sRefValue)
{
if ($sValue == $sRefValue) {
$aNotTranslated[$sStringCode] = $sValue;
}
else
{
} else {
$aOK[$sStringCode] = $sValue;
}
}
}
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
return [$aMissing, $aUnexpected, $aNotTranslated, $aOK];
}
public static function Dump()
@@ -389,10 +364,9 @@ class Dict
// ~~ or ~* can be used to indicate entries still to be translated.
public static function Add($sLanguageCode, $sEnglishLanguageDesc, $sLocalizedLanguageDesc, $aEntries)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
self::$m_aLanguages[$sLanguageCode] = array('description' => $sEnglishLanguageDesc, 'localized_description' => $sLocalizedLanguageDesc);
self::$m_aData[$sLanguageCode] = array();
if (!array_key_exists($sLanguageCode, self::$m_aLanguages)) {
self::$m_aLanguages[$sLanguageCode] = ['description' => $sEnglishLanguageDesc, 'localized_description' => $sLocalizedLanguageDesc];
self::$m_aData[$sLanguageCode] = [];
}
// No need to actually load the strings since it's only used to know the list of languages
// at setup time !!
@@ -408,27 +382,22 @@ class Dict
{
self::InitLangIfNeeded(self::GetUserLanguage());
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aEntries = array();
$aEntries = [];
$iLength = strlen($sStartingWith);
// First prefill the array with entries from the default language
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
{
if (substr($sCode, 0, $iLength) == $sStartingWith)
{
foreach (self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry) {
if (substr($sCode, 0, $iLength) == $sStartingWith) {
$aEntries[$sCode] = $sEntry;
}
}
// Now put (overwrite) the entries for the user language
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
{
if (substr($sCode, 0, $iLength) == $sStartingWith)
{
foreach (self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry) {
if (substr($sCode, 0, $iLength) == $sStartingWith) {
$aEntries[$sCode] = $sEntry;
}
}
return $aEntries;
}
}
?>

View File

@@ -1,9 +1,10 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -16,7 +17,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Send an email (abstraction for synchronous/asynchronous modes)
*
@@ -27,9 +27,9 @@
use Combodo\iTop\Core\Email\EmailFactory;
use Combodo\iTop\Core\Email\iEMail;
define ('EMAIL_SEND_OK', 0);
define ('EMAIL_SEND_PENDING', 1);
define ('EMAIL_SEND_ERROR', 2);
define('EMAIL_SEND_OK', 0);
define('EMAIL_SEND_PENDING', 1);
define('EMAIL_SEND_ERROR', 2);
class EMail implements iEMail
{
@@ -43,18 +43,18 @@ class EMail implements iEMail
protected $oMailer;
// Serialization formats
const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
public const ORIGINAL_FORMAT = 1; // Original format, consisting in serializing the whole object, inculding the Swift Mailer's object.
// Did not work with attachements since their binary representation cannot be stored as a valid UTF-8 string
const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
public const FORMAT_V2 = 2; // New format, only the raw data are serialized (base64 encoded if needed)
/** @var int ENUM_SEND_DEFAULT This option can be used when sending an e-mail to respect the default configuration parameter. */
const ENUM_SEND_DEFAULT = 0;
public const ENUM_SEND_DEFAULT = 0;
/** @var int ENUM_SEND_FORCE_SYNCHRONOUS This option can be used when sending an e-mail to ignore the default and force synchronous sending instead. Example of a use case: instant e-mail test. */
const ENUM_SEND_FORCE_SYNCHRONOUS = 1;
public const ENUM_SEND_FORCE_SYNCHRONOUS = 1;
/** @var int ENUM_SEND_FORCE_ASYNCHRONOUS This option can be used when sending an e-mail to ignore the default and force synchronous sending instead. Example of a use case: Bulk mails. */
const ENUM_SEND_FORCE_ASYNCHRONOUS = 2;
public const ENUM_SEND_FORCE_ASYNCHRONOUS = 2;
public function __construct()
{
@@ -157,7 +157,7 @@ class EMail implements iEMail
public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null)
{
$this->oMailer->SetBody($sBody, $sMimeType, $sCustomStyles);
}
}
public function AddPart($sText, $sMimeType = 'text/html')
{
@@ -208,4 +208,4 @@ class EMail implements iEMail
{
$this->oMailer->SetRecipientReplyTo($sAddress);
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -45,19 +46,18 @@ class ExcelBulkExport extends TabularBulkExport
{
parent::ReadParameters();
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 0, true);
$sDateFormatRadio = utils::ReadParam('excel_date_format_radio', '');
switch($sDateFormatRadio)
{
switch ($sDateFormatRadio) {
case 'default':
// Export from the UI => format = same as is the UI
$this->aStatusInfo['date_format'] = (string)AttributeDateTime::GetFormat();
break;
// Export from the UI => format = same as is the UI
$this->aStatusInfo['date_format'] = (string)AttributeDateTime::GetFormat();
break;
case 'custom':
// Custom format specified from the UI
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
break;
// Custom format specified from the UI
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
break;
default:
// Export from the command line (or scripted) => default format is SQL, as in previous versions of iTop, unless specified otherwise
@@ -67,7 +67,7 @@ class ExcelBulkExport extends TabularBulkExport
public function EnumFormParts()
{
return array_merge(parent::EnumFormParts(), array('xlsx_options' => array('formatted_text'), 'interactive_fields_xlsx' => array('interactive_fields_xlsx')));
return array_merge(parent::EnumFormParts(), ['xlsx_options' => ['formatted_text'], 'interactive_fields_xlsx' => ['interactive_fields_xlsx']]);
}
/**
@@ -121,7 +121,6 @@ class ExcelBulkExport extends TabularBulkExport
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
$oFieldSetDate->AddSubBlock($oRadioCustom);
$oP->add_ready_script(
<<<EOF
$('#form_part_xlsx_options').on('preview_updated', function() { FormatDatesInPreview('excel', 'xlsx'); });
@@ -141,16 +140,14 @@ EOF
protected function SuggestField($sClass, $sAttCode)
{
switch($sAttCode)
{
switch ($sAttCode) {
case 'id': // replace 'id' by 'friendlyname'
$sAttCode = 'friendlyname';
break;
default:
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef instanceof AttributeExternalKey)
{
if ($oAttDef instanceof AttributeExternalKey) {
$sAttCode .= '_friendlyname';
}
}
@@ -162,8 +159,7 @@ EOF
{
if ($sAttCode != 'id') {
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
if ($oAttDef instanceof AttributeDateTime) { // AttributeDate is derived from AttributeDateTime
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
@@ -175,74 +171,50 @@ EOF
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
switch ($sAttCode) {
case 'id':
$sRet = $oObj->GetKey();
break;
default:
$value = $oObj->Get($sAttCode);
if ($value instanceOf ormCaseLog)
{
if (array_key_exists('formatted_text', $this->aStatusInfo) && $this->aStatusInfo['formatted_text'])
{
$sText = $value->GetText();
}
else
{
$sText = $value->GetAsPlainText();
}
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
$sRet = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $sText));
}
else if ($value instanceOf DBObjectSet)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else if ($value instanceOf ormDocument)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else if ($value instanceOf ormSet)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
}
else
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($oAttDef instanceof AttributeDateTime)
{
// Date and times are formatted using the ISO encoding, not the localized format
if ($oAttDef->IsNull($value))
{
// NOt a valid date
$sRet = '';
$value = $oObj->Get($sAttCode);
if ($value instanceof ormCaseLog) {
if (array_key_exists('formatted_text', $this->aStatusInfo) && $this->aStatusInfo['formatted_text']) {
$sText = $value->GetText();
} else {
$sText = $value->GetAsPlainText();
}
else
{
$sRet = $value;
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
$sRet = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $sText));
} elseif ($value instanceof DBObjectSet) {
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
} elseif ($value instanceof ormDocument) {
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
} elseif ($value instanceof ormSet) {
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
} else {
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($oAttDef instanceof AttributeDateTime) {
// Date and times are formatted using the ISO encoding, not the localized format
if ($oAttDef->IsNull($value)) {
// NOt a valid date
$sRet = '';
} else {
$sRet = $value;
}
} elseif (array_key_exists('formatted_text', $this->aStatusInfo) && $this->aStatusInfo['formatted_text']) {
if ($oAttDef instanceof AttributeText && $oAttDef->GetFormat() == 'html') {
$sRet = str_replace("&gt;", ">", $value);
} else {
$sRet = $oAttDef->GetEditValue($value, $oObj);
}
} else {
$sRet = $oAttDef->GetAsPlainText($value, $oObj);
}
}
else if (array_key_exists('formatted_text', $this->aStatusInfo) && $this->aStatusInfo['formatted_text'])
{
if ($oAttDef instanceof AttributeText && $oAttDef->GetFormat()=='html')
{
$sRet = str_replace("&gt;", ">", $value);
}
else
{
$sRet = $oAttDef->GetEditValue($value, $oObj);
}
}
else
{
$sRet = $oAttDef->GetAsPlainText($value, $oObj);
}
}
}
return $sRet;
}
@@ -255,14 +227,12 @@ EOF
$this->aStatusInfo['position'] = 0;
$this->aStatusInfo['total'] = $oSet->Count();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
$sExtendedAttCode = $aFieldSpec['sFieldSpec'];
$sAttCode = $aFieldSpec['sAttCode'];
$sColLabel = $aFieldSpec['sColLabel'];
switch($sAttCode)
{
switch ($sAttCode) {
case 'id':
$sType = '0';
break;
@@ -270,22 +240,18 @@ EOF
default:
$oAttDef = MetaModel::GetAttributeDef($aFieldSpec['sClass'], $aFieldSpec['sAttCode']);
$sType = 'string';
if($oAttDef instanceof AttributeDate)
{
if ($oAttDef instanceof AttributeDate) {
$sType = 'date';
}
else if($oAttDef instanceof AttributeDateTime)
{
} elseif ($oAttDef instanceof AttributeDateTime) {
$sType = 'datetime';
}
}
$aTableHeaders[] = array('label' => $sColLabel, 'type' => $sType);
$aTableHeaders[] = ['label' => $sColLabel, 'type' => $sType];
}
$sRow = json_encode($aTableHeaders);
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'ab');
if ($hFile === false)
{
if ($hFile === false) {
throw new Exception('ExcelBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for writing.');
}
fwrite($hFile, $sRow."\n");
@@ -307,19 +273,16 @@ EOF
$iCount = 0;
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
while($aRow = $oSet->FetchAssoc())
{
while ($aRow = $oSet->FetchAssoc()) {
set_time_limit(intval($iLoopTimeLimit));
$aData = array();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$aData = [];
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
$sAlias = $aFieldSpec['sAlias'];
$sAttCode = $aFieldSpec['sAttCode'];
$oObj = $aRow[$sAlias];
$sField = '';
if ($oObj)
{
if ($oObj) {
$sField = $this->GetValue($oObj, $sAttCode);
}
$aData[] = $sField;
@@ -329,41 +292,35 @@ EOF
}
set_time_limit(intval($iPreviousTimeLimit));
$this->aStatusInfo['position'] += $this->iChunkSize;
if ($this->aStatusInfo['total'] == 0)
{
if ($this->aStatusInfo['total'] == 0) {
$iPercentage = 100;
$sRetCode = 'done'; // Next phase (GetFooter) will be to build the xlsx file
} else {
$iPercentage = floor(min(100.0, 100.0 * $this->aStatusInfo['position'] / $this->aStatusInfo['total']));
}
else
{
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
}
if ($iCount < $this->iChunkSize)
{
if ($iCount < $this->iChunkSize) {
$sRetCode = 'done';
}
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
$aStatus = ['code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage];
return ''; // The actual XLSX file is built in GetFooter();
}
public function GetFooter()
{
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'rb');
if ($hFile === false)
{
if ($hFile === false) {
throw new Exception('ExcelBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for reading.');
}
$sHeaders = fgets($hFile);
$aHeaders = json_decode($sHeaders, true);
$aData = array();
while($sLine = fgets($hFile))
{
$aData = [];
while ($sLine = fgets($hFile)) {
$aRow = json_decode($sLine);
$aData[] = $aRow;
}
fclose($hFile);
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$sDateFormat = isset($this->aStatusInfo['date_format']) ? $this->aStatusInfo['date_format'] : (string)AttributeDateTime::GetFormat();
@@ -372,14 +329,13 @@ EOF
$oDateFormat = new DateTimeFormat($oDateTimeFormat->ToDateFormat());
$writer->setDateFormat($oDateFormat->ToExcel());
$writer->setAuthor(UserRights::GetUserFriendlyName());
$aHeaderTypes = array();
$aHeaderNames = array();
foreach($aHeaders as $Header)
{
$aHeaderTypes = [];
$aHeaderNames = [];
foreach ($aHeaders as $Header) {
$aHeaderNames[] = $Header['label'];
$aHeaderTypes[] = $Header['type'];
}
$writer->writeSheet($aData,'Sheet1', $aHeaderTypes, $aHeaderNames);
$writer->writeSheet($aData, 'Sheet1', $aHeaderTypes, $aHeaderNames);
$fExcelTime = microtime(true) - $fStartExcel;
//$this->aStatistics['excel_build_duration'] = $fExcelTime;
@@ -405,6 +361,6 @@ EOF
public function GetSupportedFormats()
{
return array('xlsx' => Dict::S('Core:BulkExport:XLSXFormat'));
return ['xlsx' => Dict::S('Core:BulkExport:XLSXFormat')];
}
}

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2024 Combodo SAS
//
// This file is part of iTop.
@@ -35,7 +36,7 @@ class HTMLBulkExport extends TabularBulkExport
public function EnumFormParts()
{
return array_merge(parent::EnumFormParts(), array('interactive_fields_html' => array('interactive_fields_html')));
return array_merge(parent::EnumFormParts(), ['interactive_fields_html' => ['interactive_fields_html']]);
}
/**
@@ -52,17 +53,15 @@ class HTMLBulkExport extends TabularBulkExport
break;
default:
return parent:: GetFormPart($oP, $sPartId);
return parent::GetFormPart($oP, $sPartId);
}
}
protected function GetSampleData($oObj, $sAttCode)
{
if ($sAttCode != 'id')
{
if ($sAttCode != 'id') {
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
if ($oAttDef instanceof AttributeDateTime) { // AttributeDate is derived from AttributeDateTime
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
@@ -73,24 +72,18 @@ class HTMLBulkExport extends TabularBulkExport
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
switch ($sAttCode) {
case 'id':
$sRet = $oObj->GetHyperlink();
break;
default:
$value = $oObj->Get($sAttCode);
if ($value instanceof ormCaseLog)
{
if ($value instanceof ormCaseLog) {
$sRet = $value->GetAsSimpleHtml();
}
elseif ($value instanceof ormStopWatch)
{
} elseif ($value instanceof ormStopWatch) {
$sRet = $value->GetTimeSpent();
}
else
{
} else {
$sRet = $oObj->GetAsHtml($sAttCode);
}
}
@@ -100,7 +93,7 @@ class HTMLBulkExport extends TabularBulkExport
public function GetHeader()
{
$sData = '';
$oSet = new DBObjectSet($this->oSearch);
$this->aStatusInfo['status'] = 'running';
$this->aStatusInfo['position'] = 0;
@@ -109,8 +102,7 @@ class HTMLBulkExport extends TabularBulkExport
$sData .= "<table class=\"listResults\">\n";
$sData .= "<thead>\n";
$sData .= "<tr>\n";
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
$sData .= "<th>".$aFieldSpec['sColLabel']."</th>\n";
}
$sData .= "</tr>\n";
@@ -135,32 +127,25 @@ class HTMLBulkExport extends TabularBulkExport
$sData = '';
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
while($aRow = $oSet->FetchAssoc())
{
while ($aRow = $oSet->FetchAssoc()) {
set_time_limit(intval($iLoopTimeLimit));
$oMainObj = $aRow[$sFirstAlias];
$sHilightClass = '';
if ($oMainObj)
{
if ($oMainObj) {
$sHilightClass = MetaModel::GetHilightClass($sClass, $aRow[$sFirstAlias]);
}
if ($sHilightClass != '')
{
if ($sHilightClass != '') {
$sData .= "<tr class=\"$sHilightClass\">";
}
else
{
} else {
$sData .= "<tr>";
}
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
foreach ($this->aStatusInfo['fields'] as $iCol => $aFieldSpec) {
$sAlias = $aFieldSpec['sAlias'];
$sAttCode = $aFieldSpec['sAttCode'];
$oObj = $aRow[$sAlias];
$sField = '';
if ($oObj)
{
if ($oObj) {
$sField = $this->GetValue($oObj, $sAttCode);
}
$sValue = ($sField === '') ? '&nbsp;' : $sField;
@@ -171,21 +156,17 @@ class HTMLBulkExport extends TabularBulkExport
}
set_time_limit(intval($iPreviousTimeLimit));
$this->aStatusInfo['position'] += $this->iChunkSize;
if ($this->aStatusInfo['total'] == 0)
{
if ($this->aStatusInfo['total'] == 0) {
$iPercentage = 100;
}
else
{
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
} else {
$iPercentage = floor(min(100.0, 100.0 * $this->aStatusInfo['position'] / $this->aStatusInfo['total']));
}
if ($iCount < $this->iChunkSize)
{
if ($iCount < $this->iChunkSize) {
$sRetCode = 'done';
}
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
$aStatus = ['code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage];
return $sData;
}
@@ -198,7 +179,7 @@ class HTMLBulkExport extends TabularBulkExport
public function GetSupportedFormats()
{
return array('html' => Dict::S('Core:BulkExport:HTMLFormat'));
return ['html' => Dict::S('Core:BulkExport:HTMLFormat')];
}
public function GetMimeType()

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2016-2024 Combodo SAS
//
// This file is part of iTop.
@@ -48,7 +49,7 @@ abstract class HTMLSanitizer
if (!class_exists($sSanitizerClass)) {
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
} else if (!is_subclass_of($sSanitizerClass, 'HTMLSanitizer')) {
} elseif (!is_subclass_of($sSanitizerClass, 'HTMLSanitizer')) {
if ($sConfigKey === 'html_sanitizer') {
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.'. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
@@ -62,17 +63,14 @@ abstract class HTMLSanitizer
try {
$oSanitizer = new $sSanitizerClass();
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
}
catch (Exception $e) {
} catch (Exception $e) {
if ($sSanitizerClass != 'HTMLDOMSanitizer') {
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
// try again with the HTMLDOMSanitizer
$oSanitizer = new HTMLDOMSanitizer();
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
}
else
{
} else {
IssueLog::Error('Failed to sanitize an HTML string with "HTMLDOMSanitizer". The following exception occured: '.$e->getMessage());
IssueLog::Error('The HTML will NOT be sanitized.');
$sCleanHTML = $sHTML;
@@ -104,8 +102,6 @@ class HTMLNullSanitizer extends HTMLSanitizer
}
}
/**
* Common implementation for sanitizer using DOM parsing
*/
@@ -166,7 +162,7 @@ abstract class DOMSanitizer extends HTMLSanitizer
protected function CleanNode(DOMNode $oElement)
{
$aAttrToRemove = array();
$aAttrToRemove = [];
// Gather the attributes to remove
if ($oElement->hasAttributes()) {
foreach ($oElement->attributes as $oAttr) {
@@ -174,13 +170,13 @@ abstract class DOMSanitizer extends HTMLSanitizer
if ((false === empty($this->GetAttrsBlackList()))
&& (in_array($sAttr, $this->GetAttrsBlackList(), true))) {
$aAttrToRemove[] = $oAttr->name;
} else if ((false === empty($this->GetTagsWhiteList()))
} elseif ((false === empty($this->GetTagsWhiteList()))
&& (false === in_array($sAttr, $this->GetTagsWhiteList()[strtolower($oElement->tagName)]))) {
$aAttrToRemove[] = $oAttr->name;
} else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value)) {
} elseif (!$this->IsValidAttributeContent($sAttr, $oAttr->value)) {
// Invalid content
$aAttrToRemove[] = $oAttr->name;
} else if ($sAttr == 'style') {
} elseif ($sAttr == 'style') {
// Special processing for style tags
$sCleanStyle = $this->CleanStyle($oAttr->value);
if ($sCleanStyle == '') {
@@ -192,17 +188,15 @@ abstract class DOMSanitizer extends HTMLSanitizer
}
}
// Now remove them
foreach($aAttrToRemove as $sName)
{
foreach ($aAttrToRemove as $sName) {
$oElement->removeAttribute($sName);
}
}
if ($oElement->hasChildNodes())
{
$aChildElementsToRemove = array();
if ($oElement->hasChildNodes()) {
$aChildElementsToRemove = [];
// Gather the child noes to remove
foreach($oElement->childNodes as $oNode) {
foreach ($oElement->childNodes as $oNode) {
if ($oNode instanceof DOMElement) {
$sNodeTagName = strtolower($oNode->tagName);
}
@@ -210,11 +204,11 @@ abstract class DOMSanitizer extends HTMLSanitizer
&& (false === empty($this->GetTagsBlackList()))
&& (in_array($sNodeTagName, $this->GetTagsBlackList(), true))) {
$aChildElementsToRemove[] = $oNode;
} else if (($oNode instanceof DOMElement)
} elseif (($oNode instanceof DOMElement)
&& (false === empty($this->GetTagsWhiteList()))
&& (false === array_key_exists($sNodeTagName, $this->GetTagsWhiteList()))) {
$aChildElementsToRemove[] = $oNode;
} else if ($oNode instanceof DOMComment) {
} elseif ($oNode instanceof DOMComment) {
$aChildElementsToRemove[] = $oNode;
} else {
// Recurse
@@ -225,8 +219,7 @@ abstract class DOMSanitizer extends HTMLSanitizer
}
}
// Now remove them
foreach($aChildElementsToRemove as $oDomElement)
{
foreach ($aChildElementsToRemove as $oDomElement) {
$oElement->removeChild($oDomElement);
}
}
@@ -252,7 +245,7 @@ abstract class DOMSanitizer extends HTMLSanitizer
return $sStyle;
}
$aAllowedStyles = array();
$aAllowedStyles = [];
$aItems = explode(';', $sStyle);
{
foreach ($aItems as $sItem) {
@@ -267,78 +260,76 @@ abstract class DOMSanitizer extends HTMLSanitizer
}
}
class HTMLDOMSanitizer extends DOMSanitizer
{
/**
* @var array
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
*/
protected static $aTagsWhiteList = array(
'html' => array(),
'body' => array(),
'a' => array('href', 'name', 'style', 'class', 'target', 'title', 'data-role', 'data-object-class', 'data-object-id', 'data-object-key'),
'p' => array('style', 'class'),
'blockquote' => array('style', 'class'),
'br' => array(),
'span' => array('style', 'class'),
'div' => array('style', 'class'),
'b' => array('class'),
'i' => array('class'),
'u' => array('class'),
'em' => array('class'),
'strong' => array('class'),
'img' => array('src', 'style', 'class', 'alt', 'title', 'width', 'height'),
'ul' => array('style', 'class'),
'ol' => array('reversed', 'start', 'style', 'class', 'type'),
'li' => array('style', 'class', 'value'),
'h1' => array('style', 'class'),
'h2' => array('style', 'class'),
'h3' => array('style', 'class'),
'h4' => array('style', 'class'),
'nav' => array('style', 'class'),
'section' => array('style', 'class'),
'code' => array('style', 'class'),
'table' => array('style', 'class', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing'),
'colgroup' => array(),
'col' => array('style'),
'thead' => array('style', 'class'),
'tbody' => array('style', 'class'),
'tr' => array('style', 'class', 'colspan', 'rowspan'),
'td' => array('style', 'class', 'colspan', 'rowspan'),
'th' => array('style', 'class', 'colspan', 'rowspan'),
'fieldset' => array('style', 'class'),
'legend' => array('style', 'class'),
'font' => array('face', 'color', 'style', 'class', 'size'),
'big' => array(),
'small' => array(),
'tt' => array(),
'kbd' => array(),
'samp' => array(),
'var' => array(),
'del' => array(),
's' => array(), // strikethrough
'ins' => array(),
'cite' => array(),
'q' => array(),
'hr' => array('style', 'class'),
'pre' => array('class'),
'center' => array(),
'figure' => array('style', 'class'), // Ckeditor 5 puts images in figures
'figcaption' => array('class'),
'mark' => array('class')
);
protected static $aTagsWhiteList = [
'html' => [],
'body' => [],
'a' => ['href', 'name', 'style', 'class', 'target', 'title', 'data-role', 'data-object-class', 'data-object-id', 'data-object-key'],
'p' => ['style', 'class'],
'blockquote' => ['style', 'class'],
'br' => [],
'span' => ['style', 'class'],
'div' => ['style', 'class'],
'b' => ['class'],
'i' => ['class'],
'u' => ['class'],
'em' => ['class'],
'strong' => ['class'],
'img' => ['src', 'style', 'class', 'alt', 'title', 'width', 'height'],
'ul' => ['style', 'class'],
'ol' => ['reversed', 'start', 'style', 'class', 'type'],
'li' => ['style', 'class', 'value'],
'h1' => ['style', 'class'],
'h2' => ['style', 'class'],
'h3' => ['style', 'class'],
'h4' => ['style', 'class'],
'nav' => ['style', 'class'],
'section' => ['style', 'class'],
'code' => ['style', 'class'],
'table' => ['style', 'class', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing'],
'colgroup' => [],
'col' => ['style'],
'thead' => ['style', 'class'],
'tbody' => ['style', 'class'],
'tr' => ['style', 'class', 'colspan', 'rowspan'],
'td' => ['style', 'class', 'colspan', 'rowspan'],
'th' => ['style', 'class', 'colspan', 'rowspan'],
'fieldset' => ['style', 'class'],
'legend' => ['style', 'class'],
'font' => ['face', 'color', 'style', 'class', 'size'],
'big' => [],
'small' => [],
'tt' => [],
'kbd' => [],
'samp' => [],
'var' => [],
'del' => [],
's' => [], // strikethrough
'ins' => [],
'cite' => [],
'q' => [],
'hr' => ['style', 'class'],
'pre' => ['class'],
'center' => [],
'figure' => ['style', 'class'], // Ckeditor 5 puts images in figures
'figcaption' => ['class'],
'mark' => ['class'],
];
protected static $aAttrsWhiteList = array(
protected static $aAttrsWhiteList = [
'src' => '/^(http:|https:|data:)/i',
);
];
/**
* @var array
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
*/
protected static $aStylesWhiteList = array(
protected static $aStylesWhiteList = [
'aspect-ratio',
'background-color',
'border',
@@ -361,7 +352,7 @@ class HTMLDOMSanitizer extends DOMSanitizer
'vertical-align',
'width',
'white-space',
);
];
public function __construct($sInlineImageClassName = InlineImage::class)
{
@@ -431,15 +422,13 @@ class HTMLDOMSanitizer extends DOMSanitizer
// Export only the content of the body tag
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
// remove the body tag itself
$sCleanHtml = str_replace(array('<body>', '</body>'), '', $sCleanHtml);
$sCleanHtml = str_replace(['<body>', '</body>'], '', $sCleanHtml);
}
return $sCleanHtml;
}
}
/**
* @since 2.6.5 2.7.6 3.0.0 N°4360
*/

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