Compare commits

..

1 Commits

Author SHA1 Message Date
Denis Flaven
c615234988 SVN:2.0-beta1[2235] 2012-10-08 12:00:20 +00:00
4664 changed files with 130218 additions and 650597 deletions

View File

@@ -1,26 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* UserRightsMatrix (User management Module)
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/

View File

@@ -1,27 +1,27 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* UserRightsNull
* User management Module - say Yeah! to everything
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class UserRightsNull extends UserRightsAddOnAPI

View File

@@ -1,27 +1,27 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* UserRightsProfile
* User management Module, basing the right on profiles and a matrix (similar to UserRightsMatrix, but profiles and other decorations have been added)
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
define('ADMIN_PROFILE_NAME', 'Administrator');
@@ -54,7 +54,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights,grant_by_profile",
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -75,8 +75,8 @@ class URP_Profiles extends UserRightsBaseClassGUI
MetaModel::Init_SetZListItems('details', array('name', 'description', 'user_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name','description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array ('name','description'));
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
}
protected static $m_aCacheProfiles = null;
@@ -137,8 +137,11 @@ class URP_Profiles extends UserRightsBaseClassGUI
$oUserRights = UserRights::GetModuleInstance();
$aDisplayData = array();
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass)
foreach (MetaModel::GetClasses('bizmodel') as $sClass)
{
// Skip non instantiable classes
if (MetaModel::IsAbstract($sClass)) continue;
$aStimuli = array();
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus)
{
@@ -180,7 +183,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
if (!$bEditMode)
{
$oPage->SetCurrentTab(Dict::S('UI:UserManagement:GrantMatrix'));
$this->DoShowGrantSumary($oPage);
$this->DoShowGrantSumary($oPage);
}
}
@@ -200,12 +203,6 @@ class URP_Profiles extends UserRightsBaseClassGUI
// preserve DB integrity by deleting links to users
protected function OnDelete()
{
// Don't remove admin profile
if ($this->Get('name') === ADMIN_PROFILE_NAME)
{
throw new SecurityException(Dict::Format('UI:Login:Error:AccessAdmin'));
}
// Note: this may break the rule that says: "a user must have at least ONE profile" !
$oLnkSet = $this->Get('user_list');
while($oLnk = $oLnkSet->Fetch())
@@ -213,25 +210,6 @@ class URP_Profiles extends UserRightsBaseClassGUI
$oLnk->DBDelete();
}
}
/**
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
* for the given attribute in the current state of the object
* @param $sAttCode string $sAttCode The code of the attribute
* @param $aReasons array To store the reasons why the attribute is read-only (info about the synchro replicas)
* @param $sTargetState string The target state in which to evalutate the flags, if empty the current state will be used
* @return integer Flags: the binary combination of the flags applicable to this attribute
*/
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
$iFlags = parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
if (MetaModel::GetConfig()->Get('demo_mode'))
{
$aReasons[] = 'Sorry, profiles are read-only in the demonstration mode!';
$iFlags |= OPT_ATT_READONLY;
}
return $iFlags;
}
}
@@ -242,7 +220,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights,grant_by_profile",
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "userid",
"state_attcode" => "",
@@ -274,72 +252,6 @@ class URP_UserProfile extends UserRightsBaseClassGUI
{
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
}
public function CheckToDelete(&$oDeletionPlan)
{
if (MetaModel::GetConfig()->Get('demo_mode'))
{
// Users deletion is NOT allowed in demo mode
$oDeletionPlan->AddToDelete($this, null);
$oDeletionPlan->SetDeletionIssues($this, array('deletion not allowed in demo mode.'), true);
$oDeletionPlan->ComputeResults();
return false;
}
return parent::CheckToDelete($oDeletionPlan);
}
protected function OnInsert()
{
$this->CheckIfProfileIsAllowed(UR_ACTION_CREATE);
}
protected function OnUpdate()
{
$this->CheckIfProfileIsAllowed(UR_ACTION_MODIFY);
}
protected function OnDelete()
{
$this->CheckIfProfileIsAllowed(UR_ACTION_DELETE);
}
/**
* @param $iActionCode
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \SecurityException
*/
protected function CheckIfProfileIsAllowed($iActionCode)
{
// When initializing or admin, we need to let everything pass trough
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) { return; }
// Only administrators can manage administrators
$iOrigUserId = $this->GetOriginal('userid');
if (!empty($iOrigUserId))
{
$oUser = MetaModel::GetObject('User', $iOrigUserId, true, true);
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator())
{
throw new SecurityException(Dict::Format('UI:Login:Error:AccessRestricted'));
}
}
$oUser = MetaModel::GetObject('User', $this->Get('userid'), true, true);
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator())
{
throw new SecurityException(Dict::Format('UI:Login:Error:AccessRestricted'));
}
if (!UserRights::IsActionAllowed(get_class($this), $iActionCode, DBObjectSet::FromObject($this)))
{
throw new SecurityException(Dict::Format('UI:Error:ObjectCannotBeUpdated'));
}
if (!UserRights::IsAdministrator() && ($this->Get('profile') === ADMIN_PROFILE_NAME))
{
throw new SecurityException(Dict::Format('UI:Login:Error:AccessAdmin'));
}
}
}
class URP_UserOrg extends UserRightsBaseClassGUI
@@ -348,7 +260,7 @@ class URP_UserOrg extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights,grant_by_profile",
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "userid",
"state_attcode" => "",
@@ -380,42 +292,6 @@ class URP_UserOrg extends UserRightsBaseClassGUI
{
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Org', $this->Get('userlogin'), $this->Get('allowed_org_name'));
}
protected function OnInsert()
{
$this->CheckIfOrgIsAllowed();
}
protected function OnUpdate()
{
$this->CheckIfOrgIsAllowed();
}
protected function OnDelete()
{
$this->CheckIfOrgIsAllowed();
}
/**
* @throws \CoreException
*/
protected function CheckIfOrgIsAllowed()
{
if (UserRights::IsAdministrator()) { return; }
$oUser = UserRights::GetUserObject();
$oAddon = UserRights::GetModuleInstance();
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
if (count($aOrgs) > 0)
{
$iOrigOrgId = $this->GetOriginal('allowed_org_id');
if ((!empty($iOrigOrgId) && !in_array($iOrigOrgId, $aOrgs)) || !in_array($this->Get('allowed_org_id'), $aOrgs))
{
throw new SecurityException(Dict::Format('Class:User/Error:OrganizationNotAllowed'));
}
}
}
}
@@ -435,9 +311,11 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Installation: create the very first user
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
CMDBObject::SetTrackInfo('Initialization');
$oChange = CMDBObject::GetCurrentChange();
// Create a change to record the history of the User object
$oChange = MetaModel::NewObject("CMDBChange");
$oChange->Set("date", time());
$oChange->Set("userinfo", "Initialization");
$iChangeId = $oChange->DBInsert();
$iContactId = 0;
// Support drastic data model changes: no organization class (or not writable)!
@@ -497,6 +375,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
}
protected $m_aAdmins = array(); // id -> bool, true if the user has the well-known admin profile
protected $m_aPortalUsers = array(); // id -> bool, true if the user has the well-known portal user profile
protected $m_aProfiles; // id -> object
protected $m_aUserProfiles = array(); // userid,profileid -> object
protected $m_aUserOrgs = array(); // userid -> array of orgid
// Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read)
@@ -504,15 +388,11 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* Read and cache organizations allowed to the given user
*
* @param $oUser
* @param $sClass (not used here but can be used in overloads)
*
* @return array
* @throws \CoreException
* @throws \Exception
*
* @param oUser
* @param sClass -not used here but can be used in overloads
*/
public function GetUserOrgs($oUser, $sClass)
protected function GetUserOrgs($oUser, $sClass)
{
$iUser = $oUser->GetKey();
if (!array_key_exists($iUser, $this->m_aUserOrgs))
@@ -526,6 +406,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sUserOrgQuery), array(), array('userid' => $iUser));
while ($aRow = $oUserOrgSet->FetchAssoc())
{
$oUserOrg = $aRow['UserOrg'];
$oOrg = $aRow['Org'];
$this->m_aUserOrgs[$iUser][] = $oOrg->GetKey();
}
@@ -547,65 +428,114 @@ class UserRightsProfile extends UserRightsAddOnAPI
return $this->m_aUserOrgs[$iUser];
}
/**
* Read and cache profiles of the given user
*/
protected function GetUserProfiles($iUser)
{
if (!array_key_exists($iUser, $this->m_aUserProfiles))
{
$oSearch = new DBObjectSearch('URP_UserProfile');
$oSearch->AllowAllData();
$oCondition = new BinaryExpression(new FieldExpression('userid'), '=', new VariableExpression('userid'));
$oSearch->AddConditionExpression($oCondition);
$this->m_aUserProfiles[$iUser] = array();
$oUserProfileSet = new DBObjectSet($oSearch, array(), array('userid' => $iUser));
while ($oUserProfile = $oUserProfileSet->Fetch())
{
$this->m_aUserProfiles[$iUser][$oUserProfile->Get('profileid')] = $oUserProfile;
}
}
return $this->m_aUserProfiles[$iUser];
}
public function ResetCache()
{
// Loaded by Load cache
$this->m_aProfiles = null;
$this->m_aUserProfiles = array();
$this->m_aUserOrgs = array();
$this->m_aAdmins = array();
$this->m_aPortalUsers = array();
// Cache
$this->m_aObjectActionGrants = array();
}
public function LoadCache()
{
static $bSharedObjectInitialized = false;
if (!$bSharedObjectInitialized)
if (!is_null($this->m_aProfiles)) return;
// Could be loaded in a shared memory (?)
$oKPI = new ExecutionKPI();
if (self::HasSharing())
{
$bSharedObjectInitialized = true;
if (self::HasSharing())
{
SharedObject::InitSharedClassProperties();
}
SharedObject::InitSharedClassProperties();
}
$oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Profiles"));
$this->m_aProfiles = array();
while ($oProfile = $oProfileSet->Fetch())
{
$this->m_aProfiles[$oProfile->GetKey()] = $oProfile;
}
$oKPI->ComputeAndReport('Load of user management cache (excepted Action Grants)');
/*
echo "<pre>\n";
print_r($this->m_aProfiles);
print_r($this->m_aUserProfiles);
print_r($this->m_aUserOrgs);
echo "</pre>\n";
exit;
*/
return true;
}
/**
* @param $oUser User
* @return array
*/
public function IsAdministrator($oUser)
{
// UserRights caches the list for us
return UserRights::HasProfile(ADMIN_PROFILE_NAME, $oUser);
//$this->LoadCache();
$iUser = $oUser->GetKey();
if (!array_key_exists($iUser, $this->m_aAdmins))
{
$bIsAdmin = false;
foreach($this->GetUserProfiles($iUser) as $oUserProfile)
{
if ($oUserProfile->Get('profile') == ADMIN_PROFILE_NAME)
{
$bIsAdmin = true;
break;
}
}
$this->m_aAdmins[$iUser] = $bIsAdmin;
}
return $this->m_aAdmins[$iUser];
}
/**
* @param $oUser User
* @return array
*/
public function IsPortalUser($oUser)
{
// UserRights caches the list for us
return UserRights::HasProfile(PORTAL_PROFILE_NAME, $oUser);
}
/**
* @param $oUser User
* @return bool
*/
public function ListProfiles($oUser)
{
$aRet = array();
$oSearch = new DBObjectSearch('URP_UserProfile');
$oSearch->AllowAllData();
$oSearch->NoContextParameters();
$oSearch->Addcondition('userid', $oUser->GetKey(), '=');
$oProfiles = new DBObjectSet($oSearch);
while ($oUserProfile = $oProfiles->Fetch())
//$this->LoadCache();
$iUser = $oUser->GetKey();
if (!array_key_exists($iUser, $this->m_aPortalUsers))
{
$aRet[$oUserProfile->Get('profileid')] = $oUserProfile->Get('profileid_friendlyname');
$bIsPortalUser = false;
foreach($this->GetUserProfiles($iUser) as $oUserProfile)
{
if ($oUserProfile->Get('profile') == PORTAL_PROFILE_NAME)
{
$bIsPortalUser = true;
break;
}
return $aRet;
}
$this->m_aPortalUsers[$iUser] = $bIsPortalUser;
}
return $this->m_aPortalUsers[$iUser];
}
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
@@ -635,9 +565,71 @@ class UserRightsProfile extends UserRightsAddOnAPI
return true;
}
return $this->MakeSelectFilter($sClass, $aUserOrgs, $aSettings, $sAttCode);
}
$oExpression = new FieldExpression($sAttCode, $sClass);
$oFilter = new DBObjectSearch($sClass);
$oListExpr = ListExpression::FromScalars($aUserOrgs);
$oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr);
$oFilter->AddConditionExpression($oCondition);
if (self::HasSharing())
{
if (($sAttCode == 'id') && isset($aSettings['bSearchMode']) && $aSettings['bSearchMode'])
{
// Querying organizations (or derived)
// and the expected list of organizations will be used as a search criteria
// Therefore the query can also return organization having objects shared with the allowed organizations
//
// 1) build the list of organizations sharing something with the allowed organizations
// Organization <== sharing_org_id == SharedObject having org_id IN {user orgs}
$oShareSearch = new DBObjectSearch('SharedObject');
$oOrgField = new FieldExpression('org_id', 'SharedObject');
$oShareSearch->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
$oSearchSharers = new DBObjectSearch('Organization');
$oSearchSharers->AllowAllData();
$oSearchSharers->AddCondition_ReferencedBy($oShareSearch, 'sharing_org_id');
$aSharers = array();
foreach($oSearchSharers->ToDataArray(array('id')) as $aRow)
{
$aSharers[] = $aRow['id'];
}
// 2) Enlarge the overall results: ... OR id IN(id1, id2, id3)
if (count($aSharers) > 0)
{
$oSharersList = ListExpression::FromScalars($aSharers);
$oFilter->MergeConditionExpression(new BinaryExpression($oExpression, 'IN', $oSharersList));
}
}
$aShareProperties = SharedObject::GetSharedClassProperties($sClass);
if ($aShareProperties)
{
$sShareClass = $aShareProperties['share_class'];
$sShareAttCode = $aShareProperties['attcode'];
$oSearchShares = new DBObjectSearch($sShareClass);
$oSearchShares->AllowAllData();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
$oOrgField = new FieldExpression('org_id', $sShareClass);
$oSearchShares->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
$aShared = array();
foreach($oSearchShares->ToDataArray(array($sShareAttCode)) as $aRow)
{
$aShared[] = $aRow[$sShareAttCode];
}
if (count($aShared) > 0)
{
$oObjId = new FieldExpression('id', $sClass);
$oSharedIdList = ListExpression::FromScalars($aShared);
$oFilter->MergeConditionExpression(new BinaryExpression($oObjId, 'IN', $oSharedIdList));
}
}
} // if HasSharing
return $oFilter;
}
// This verb has been made public to allow the development of an accurate feedback for the current configuration
public function GetProfileActionGrant($iProfile, $sClass, $sAction)
@@ -661,8 +653,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
$sAction = self::$m_aActionCodes[$iActionCode];
$bStatus = null;
// Call the API of UserRights because it caches the list for us
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
$aAttributes = array();
foreach($this->GetUserProfiles($iUser) as $iProfile => $oProfile)
{
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (!is_null($bGrant))
@@ -685,11 +677,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
$aRes = array(
'permission' => $iPermission,
// 'attributes' => $aAttributes,
);
$this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode] = $aRes;
return $aRes;
}
public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
{
$this->LoadCache();
@@ -791,8 +784,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Note: The object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set
$bStatus = null;
// Call the API of UserRights because it caches the list for us
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
foreach($this->GetUserProfiles($iUser) as $iProfile => $oProfile)
{
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($bGrant))

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,27 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* UserRightsProjection
* User management Module, basing the right on profiles and a matrix (similar to UserRightsProfile, but enhanced with dimensions and projection of classes and profile over the dimensions)
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
define('ADMIN_PROFILE_ID', 1);
@@ -78,8 +78,8 @@ class URP_Profiles extends UserRightsBaseClass
MetaModel::Init_SetZListItems('details', array('name', 'description', 'user_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array ('name', 'description'));
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
}
function GetGrantAsHtml($oUserRights, $sClass, $sAction)

View File

@@ -1,328 +0,0 @@
<?php
namespace Html2Text;
if (!function_exists('mb_split'))
{
function mb_split($pattern, $subject, $limit = -1)
{
return preg_split('/'.$pattern.'/', $subject, $limit);
}
}
/**
* Replace all occurrences of the search string with the replacement string.
*
* @author Sean Murphy <sean@iamseanmurphy.com>
* @copyright Copyright 2012 Sean Murphy. All rights reserved.
* @license http://creativecommons.org/publicdomain/zero/1.0/
* @link http://php.net/manual/function.str-replace.php
*
* @param mixed $search
* @param mixed $replace
* @param mixed $subject
* @param int $count
* @return mixed
*/
function mb_str_replace($search, $replace, $subject, &$count = 0) {
if (!is_array($subject)) {
// Normalize $search and $replace so they are both arrays of the same length
$searches = is_array($search) ? array_values($search) : array($search);
$replacements = is_array($replace) ? array_values($replace) : array($replace);
$replacements = array_pad($replacements, count($searches), '');
foreach ($searches as $key => $search) {
$parts = mb_split(preg_quote($search), $subject);
$count += count($parts) - 1;
$subject = implode($replacements[$key], $parts);
}
} else {
// Call mb_str_replace for each subject in array, recursively
foreach ($subject as $key => $value) {
$subject[$key] = mb_str_replace($search, $replace, $value, $count);
}
}
return $subject;
}
/******************************************************************************
* Copyright (c) 2010 Jevon Wright and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* or
*
* LGPL which is available at http://www.gnu.org/licenses/lgpl.html
*
*
* Contributors:
* Jevon Wright - initial API and implementation
* Denis Flaven - some fixes for properly handling UTF-8 characters
****************************************************************************/
class Html2Text {
/**
* Tries to convert the given HTML into a plain text format - best suited for
* e-mail display, etc.
*
* <p>In particular, it tries to maintain the following features:
* <ul>
* <li>Links are maintained, with the 'href' copied over
* <li>Information in the &lt;head&gt; is lost
* </ul>
*
* @param string html the input HTML
* @return string the HTML converted, as best as possible, to text
* @throws Html2TextException if the HTML could not be loaded as a {@link DOMDocument}
*/
static function convert($html) {
// replace &nbsp; with spaces
$html = str_replace("&nbsp;", " ", $html);
$html = mb_str_replace("\xc2\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
$html = static::fixNewlines($html);
$doc = new \DOMDocument();
if (!@$doc->loadHTML('<?xml encoding="UTF-8">'.$html)) // Forces the UTF-8 character set for HTML fragments
{
throw new Html2TextException("Could not load HTML - badly formed?", $html);
}
$output = static::iterateOverNode($doc);
// remove leading and trailing spaces on each line
$output = preg_replace("/[ \t]*\n[ \t]*/im", "\n", $output);
$output = preg_replace("/ *\t */im", "\t", $output);
// remove unnecessary empty lines
$output = preg_replace("/\n\n\n*/im", "\n\n", $output);
// remove leading and trailing whitespace
$output = trim($output);
return $output;
}
/**
* Unify newlines; in particular, \r\n becomes \n, and
* then \r becomes \n. This means that all newlines (Unix, Windows, Mac)
* all become \ns.
*
* @param string text text with any number of \r, \r\n and \n combinations
* @return string the fixed text
*/
static function fixNewlines($text) {
// replace \r\n to \n
$text = str_replace("\r\n", "\n", $text);
// remove \rs
$text = str_replace("\r", "\n", $text);
return $text;
}
static function nextChildName($node) {
// get the next child
$nextNode = $node->nextSibling;
while ($nextNode != null) {
if ($nextNode instanceof \DOMElement) {
break;
}
$nextNode = $nextNode->nextSibling;
}
$nextName = null;
if ($nextNode instanceof \DOMElement && $nextNode != null) {
$nextName = strtolower($nextNode->nodeName);
}
return $nextName;
}
static function prevChildName($node) {
// get the previous child
$nextNode = $node->previousSibling;
while ($nextNode != null) {
if ($nextNode instanceof \DOMElement) {
break;
}
$nextNode = $nextNode->previousSibling;
}
$nextName = null;
if ($nextNode instanceof \DOMElement && $nextNode != null) {
$nextName = strtolower($nextNode->nodeName);
}
return $nextName;
}
static function iterateOverNode($node) {
if ($node instanceof \DOMText) {
// Replace whitespace characters with a space (equivilant to \s)
return preg_replace("/[\\t\\n\\f\\r ]+/im", " ", $node->wholeText);
}
if ($node instanceof \DOMDocumentType) {
// ignore
return "";
}
$nextName = static::nextChildName($node);
$prevName = static::prevChildName($node);
$name = strtolower($node->nodeName);
// start whitespace
switch ($name) {
case "hr":
return "---------------------------------------------------------------\n";
case "style":
case "head":
case "title":
case "meta":
case "script":
// ignore these tags
return "";
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
case "ol":
case "ul":
// add two newlines, second line is added below
$output = "\n";
break;
case "td":
case "th":
// add tab char to separate table fields
$output = "\t";
break;
case "tr":
case "p":
case "div":
// add one line
$output = "\n";
break;
case "li":
$output = "- ";
break;
default:
// print out contents of unknown tags
$output = "";
break;
}
// debug
//$output .= "[$name,$nextName]";
if (isset($node->childNodes)) {
for ($i = 0; $i < $node->childNodes->length; $i++) {
$n = $node->childNodes->item($i);
$text = static::iterateOverNode($n);
$output .= $text;
}
}
// end whitespace
switch ($name) {
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
$output .= "\n";
break;
case "p":
case "br":
// add one line
if ($nextName != "div")
$output .= "\n";
break;
case "div":
// add one line only if the next child isn't a div
if ($nextName != "div" && $nextName != null)
$output .= "\n";
break;
case "a":
// links are returned in [text](link) format
$href = $node->getAttribute("href");
$output = trim($output);
// remove double [[ ]] s from linking images
if (substr($output, 0, 1) == "[" && substr($output, -1) == "]") {
$output = substr($output, 1, strlen($output) - 2);
// for linking images, the title of the <a> overrides the title of the <img>
if ($node->getAttribute("title")) {
$output = $node->getAttribute("title");
}
}
// if there is no link text, but a title attr
if (!$output && $node->getAttribute("title")) {
$output = $node->getAttribute("title");
}
if ($href == null) {
// it doesn't link anywhere
if ($node->getAttribute("name") != null) {
$output = "[$output]";
}
} else {
if ($href == $output || $href == "mailto:$output" || $href == "http://$output" || $href == "https://$output") {
// link to the same address: just use link
$output;
} else {
// replace it
if ($output) {
$output = "[$output]($href)";
} else {
// empty string
$output = $href;
}
}
}
// does the next node require additional whitespace?
switch ($nextName) {
case "h1": case "h2": case "h3": case "h4": case "h5": case "h6":
$output .= "\n";
break;
}
break;
case "img":
if ($node->getAttribute("title")) {
$output = "[" . $node->getAttribute("title") . "]";
} elseif ($node->getAttribute("alt")) {
$output = "[" . $node->getAttribute("alt") . "]";
} else {
$output = "";
}
break;
case "li":
$output .= "\n";
break;
default:
// do nothing
}
return $output;
}
}

View File

@@ -1,28 +0,0 @@
<?php
/******************************************************************************
* Copyright (c) 2010 Jevon Wright and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* or
*
* LGPL which is available at http://www.gnu.org/licenses/lgpl.html
*
*
* Contributors:
* Jevon Wright - initial API and implementation
****************************************************************************/
namespace Html2Text;
class Html2TextException extends \Exception {
var $more_info;
public function __construct($message = "", $more_info = "") {
parent::__construct($message);
$this->more_info = $more_info;
}
}

View File

@@ -1,40 +1,41 @@
<?php
// Copyright (C) 2010-2018 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Simple web page with no includes, header or fancy formatting, useful to
* generate HTML fragments when called by an AJAX method
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT."/application/webpage.class.inc.php");
class ajax_page extends WebPage implements iTabbedPage
class ajax_page extends WebPage
{
/**
* Jquery style ready script
* @var Hash
*/
protected $m_sReadyScript;
protected $m_oTabs;
private $m_sMenu; // If set, then the menu will be updated
protected $m_sCurrentTab;
protected $m_sCurrentTabContainer;
protected $m_aTabs;
/**
* constructor for the web page
@@ -42,93 +43,56 @@ class ajax_page extends WebPage implements iTabbedPage
*/
function __construct($s_title)
{
$sPrintable = utils::ReadParam('printable', '0');
$bPrintable = ($sPrintable == '1');
parent::__construct($s_title, $bPrintable);
parent::__construct($s_title);
$this->m_sReadyScript = "";
//$this->add_header("Content-type: text/html; charset=utf-8");
$this->add_header("Cache-control: no-cache");
$this->m_oTabs = new TabManager();
$this->m_sCurrentTabContainer = '';
$this->m_sCurrentTab = '';
$this->m_aTabs = array();
$this->sContentType = 'text/html';
$this->sContentDisposition = 'inline';
$this->m_sMenu = "";
utils::InitArchiveMode();
}
}
public function AddTabContainer($sTabContainer, $sPrefix = '')
{
$this->add($this->m_oTabs->AddTabContainer($sTabContainer, $sPrefix));
$this->m_aTabs[$sTabContainer] = array('content' =>'', 'prefix' => $sPrefix);
$this->add("\$Tabs:$sTabContainer\$");
}
public function AddToTab($sTabContainer, $sTabLabel, $sHtml)
{
$this->add($this->m_oTabs->AddToTab($sTabContainer, $sTabLabel, $sHtml));
if (!isset($this->m_aTabs[$sTabContainer]['content'][$sTabLabel]))
{
// Set the content of the tab
$this->m_aTabs[$sTabContainer]['content'][$sTabLabel] = $sHtml;
}
else
{
// Append to the content of the tab
$this->m_aTabs[$sTabContainer]['content'][$sTabLabel] .= $sHtml;
}
}
public function SetCurrentTabContainer($sTabContainer = '')
{
return $this->m_oTabs->SetCurrentTabContainer($sTabContainer);
$sPreviousTabContainer = $this->m_sCurrentTabContainer;
$this->m_sCurrentTabContainer = $sTabContainer;
return $sPreviousTabContainer;
}
public function SetCurrentTab($sTabLabel = '')
{
return $this->m_oTabs->SetCurrentTab($sTabLabel);
}
/**
* Add a tab which content will be loaded asynchronously via the supplied URL
*
* Limitations:
* Cross site scripting is not not allowed for security reasons. Use a normal tab with an IFRAME if you want to pull content from another server.
* Static content cannot be added inside such tabs.
*
* @param string $sTabLabel The (localised) label of the tab
* @param string $sUrl The URL to load (on the same server)
* @param boolean $bCache Whether or not to cache the content of the tab once it has been loaded. flase will cause the tab to be reloaded upon each activation.
* @since 2.0.3
*/
public function AddAjaxTab($sTabLabel, $sUrl, $bCache = true)
{
$this->add($this->m_oTabs->AddAjaxTab($sTabLabel, $sUrl, $bCache));
$sPreviousTab = $this->m_sCurrentTab;
$this->m_sCurrentTab = $sTabLabel;
return $sPreviousTab;
}
public function GetCurrentTab()
{
return $this->m_oTabs->GetCurrentTab();
}
public function RemoveTab($sTabLabel, $sTabContainer = null)
{
$this->m_oTabs->RemoveTab($sTabLabel, $sTabContainer);
}
/**
* Finds the tab whose title matches a given pattern
* @return mixed The name of the tab as a string or false if not found
*/
public function FindTab($sPattern, $sTabContainer = null)
{
return $this->m_oTabs->FindTab($sPattern, $sTabContainer);
}
/**
* Make the given tab the active one, as if it were clicked
* DOES NOT WORK: apparently in the *old* version of jquery
* that we are using this is not supported... TO DO upgrade
* the whole jquery bundle...
*/
public function SelectTab($sTabContainer, $sTabLabel)
{
$this->add_ready_script($this->m_oTabs->SelectTab($sTabContainer, $sTabLabel));
return $this->m_sCurrentTab;
}
public function AddToMenu($sHtml)
{
$this->m_sMenu .= $sHtml;
}
/**
* Echoes the content of the whole page
* @return void
@@ -147,26 +111,13 @@ class ajax_page extends WebPage implements iTabbedPage
{
header($s_header);
}
if ($this->m_oTabs->TabsContainerCount() > 0)
if (count($this->m_aTabs) > 0)
{
$this->add_ready_script(
$this->add_ready_script(
<<<EOF
// The "tab widgets" to handle.
var tabs = $('div[id^=tabbedContent]');
// Ugly patch for a change in the behavior of jQuery UI:
// Before jQuery UI 1.9, tabs were always considered as "local" (opposed to Ajax)
// when their href was beginning by #. Starting with 1.9, a <base> tag in the page
// is taken into account and causes "local" tabs to be considered as Ajax
// unless their URL is equal to the URL of the page...
if ($('base').length > 0)
{
$('div[id^=tabbedContent] > ul > li > a').each(function() {
var sHash = location.hash;
var sCleanLocation = location.href.toString().replace(sHash, '').replace(/#$/, '');
$(this).attr("href", sCleanLocation+$(this).attr("href"));
});
}
if ($.bbq)
{
// This selector will be reused when selecting actual tab widget A elements.
@@ -202,20 +153,40 @@ EOF
);
}
// Render the tabs in the page (if any)
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content, $this);
// Additional UI widgets to be activated inside the ajax fragment
// Important: Testing the content type is not enough because some ajax handlers have not correctly positionned the flag (e.g json response corrupted by the script)
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
foreach($this->m_aTabs as $sTabContainerName => $aTabContainer)
{
$this->add_ready_script(
<<<EOF
PrepareWidgets();
EOF
);
$sTabs = '';
$m_aTabs = $aTabContainer['content'];
$sPrefix = $aTabContainer['prefix'];
$container_index = 0;
if (count($m_aTabs) > 0)
{
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$sTabContainerName}\" class=\"light\">\n";
$sTabs .= "<ul>\n";
// Display the unordered list that will be rendered as the tabs
$i = 0;
foreach($m_aTabs as $sTabName => $sTabContent)
{
$sTabs .= "<li><a href=\"#tab_{$sPrefix}$i\" class=\"tab\"><span>".htmlentities($sTabName, ENT_QUOTES, 'UTF-8')."</span></a></li>\n";
$i++;
}
$sTabs .= "</ul>\n";
// Now add the content of the tabs themselves
$i = 0;
foreach($m_aTabs as $sTabName => $sTabContent)
{
$sTabs .= "<div id=\"tab_{$sPrefix}$i\">".$sTabContent."</div>\n";
$i++;
}
$sTabs .= "</div>\n<!-- end of tabs-->\n";
}
$this->s_content = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $this->s_content);
$container_index++;
}
$s_captured_output = $this->ob_get_clean_safe();
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
$s_captured_output = ob_get_contents();
ob_end_clean();
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
{
// inline content != attachment && html => filter all scripts for malicious XSS scripts
echo self::FilterXSS($this->s_content);
@@ -224,38 +195,12 @@ EOF
{
echo $this->s_content;
}
if (!empty($this->m_sMenu))
{
$uid = time();
echo "<div id=\"accordion_temp_$uid\">\n";
echo "<div id=\"accordion\">\n";
echo "<!-- Beginning of the accordion menu -->\n";
echo self::FilterXSS($this->m_sMenu);
echo "<!-- End of the accordion menu-->\n";
echo "</div>\n";
echo "</div>\n";
echo "<script type=\"text/javascript\">\n";
echo "$('#inner_menu').html($('#accordion_temp_$uid').html());\n";
echo "$('#accordion_temp_$uid').remove();\n";
echo "\n</script>\n";
}
//echo $this->s_deferred_content;
if (count($this->a_scripts) > 0)
{
echo "<script type=\"text/javascript\">\n";
echo implode("\n", $this->a_scripts);
echo "\n</script>\n";
}
if (count($this->a_linked_scripts) > 0)
{
echo "<script type=\"text/javascript\">\n";
foreach($this->a_linked_scripts as $sScriptUrl)
{
echo '$.getScript('.json_encode($sScriptUrl).");\n";
}
echo "\n</script>\n";
echo "<script type=\"text/javascript\">\n";
echo implode("\n", $this->a_scripts);
echo "\n</script>\n";
}
if (!empty($this->s_deferred_content))
{
@@ -269,16 +214,10 @@ EOF
echo $this->m_sReadyScript; // Ready Scripts are output as simple scripts
echo "\n</script>\n";
}
if (trim($s_captured_output) != "")
{
echo self::FilterXSS($s_captured_output);
}
if (class_exists('DBSearch'))
{
DBSearch::RecordQueryTrace();
}
}
/**
@@ -293,9 +232,9 @@ EOF
public function add($sHtml)
{
if (($this->m_oTabs->GetCurrentTabContainer() != '') && ($this->m_oTabs->GetCurrentTab() != ''))
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
{
$this->m_oTabs->AddToTab($this->m_oTabs->GetCurrentTabContainer(), $this->m_oTabs->GetCurrentTab(), $sHtml);
$this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml);
}
else
{
@@ -309,18 +248,15 @@ EOF
*/
public function start_capture()
{
$sCurrentTabContainer = $this->m_oTabs->GetCurrentTabContainer();
$sCurrentTab = $this->m_oTabs->GetCurrentTab();
if (!empty($sCurrentTabContainer) && !empty($sCurrentTab))
{
$iOffset = $this->m_oTabs->GetCurrentTabLength();
return array('tc' => $sCurrentTabContainer, 'tab' => $sCurrentTab, 'offset' => $iOffset);
}
else
{
return parent::start_capture();
}
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
{
$iOffset = isset($this->m_aTabs[$this->m_sCurrentTabContainer]['content'][$this->m_sCurrentTab]) ? strlen($this->m_aTabs[$this->m_sCurrentTabContainer]['content'][$this->m_sCurrentTab]): 0;
return array('tc' => $this->m_sCurrentTabContainer, 'tab' => $this->m_sCurrentTab, 'offset' => $iOffset);
}
else
{
return parent::start_capture();
}
}
/**
@@ -331,22 +267,23 @@ EOF
*/
public function end_capture($offset)
{
if (is_array($offset))
{
if ($this->m_oTabs->TabExists($offset['tc'], $offset['tab']))
{
$sCaptured = $this->m_oTabs->TruncateTab($offset['tc'], $offset['tab'], $offset['offset']);
}
else
{
$sCaptured = '';
}
}
else
{
$sCaptured = parent::end_capture($offset);
}
return $sCaptured;
if (is_array($offset))
{
if (isset($this->m_aTabs[$offset['tc']]['content'][$offset['tab']]))
{
$sCaptured = substr($this->m_aTabs[$offset['tc']]['content'][$offset['tab']], $offset['offset']);
$this->m_aTabs[$offset['tc']]['content'][$offset['tab']] = substr($this->m_aTabs[$offset['tc']]['content'][$offset['tab']], 0, $offset['offset']);
}
else
{
$sCaptured = '';
}
}
else
{
$sCaptured = parent::end_capture($offset);
}
return $sCaptured;
}
/**
@@ -389,3 +326,4 @@ EOF
}
}
?>

View File

@@ -1,36 +1,35 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Includes all the classes to have the application up and running
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/application/applicationcontext.class.inc.php');
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
require_once(APPROOT.'/application/sqlblock.class.inc.php');
require_once(APPROOT.'/application/audit.category.class.inc.php');
require_once(APPROOT.'/application/audit.rule.class.inc.php');
require_once(APPROOT.'/application/query.class.inc.php');
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');
//require_once(APPROOT.'/application/menunode.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');

View File

@@ -1,362 +1,361 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class ApplicationContext
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT."/application/utils.inc.php");
/**
* Interface for directing end-users to the relevant application
*/
interface iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId);
}
/**
* Direct end-users to the standard iTop application: UI.php
*/
class iTopStandardURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sPage = DBObject::ComputeStandardUIPage($sClass);
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}pages/$sPage?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Direct end-users to the standard Portal application
*/
class PortalURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}portal/index.php?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Helper class to store and manipulate the parameters that make the application's context
*
* Usage:
* 1) Build the application's context by constructing the object
* (the object will read some of the page's parameters)
*
* 2) Add these parameters to hyperlinks or to forms using the helper, functions
* GetForLink(), GetForForm() or GetAsHash()
*/
class ApplicationContext
{
protected $aNames;
protected $aValues;
protected static $aDefaultValues; // Cache shared among all instances
public function __construct($bReadContext = true)
{
$this->aNames = array(
'org_id', 'menu'
);
if ($bReadContext)
{
$this->ReadContext();
}
}
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*/
protected function ReadContext()
{
if (!isset(self::$aDefaultValues))
{
self::$aDefaultValues = array();
$aContext = utils::ReadParam('c', array(), 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))
{
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
if ($sName == 'org_id')
{
if (MetaModel::IsValidClass('Organization'))
{
$oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->CountWithLimit(2);
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
* @return mixed The value for this parameter
*/
public function GetCurrentValue($sParamName, $defaultValue = '')
{
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
*/
public function GetForLink()
{
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
return implode("&", $aParams);
}
/**
* Returns the context as sequence of input tags to be inserted inside a <form> tag
* return string The context as a sequence of <input type="hidden" /> tags
*/
public function GetForForm()
{
$sContext = "";
foreach($this->aValues as $sName => $sValue)
{
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
}
return $sContext;
}
/**
* Returns the context as a hash array 'parameter_name' => value
* return array The context information
*/
public function GetAsHash()
{
$aReturn = array();
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
*/
public function GetNames()
{
return $this->aNames;
}
/**
* 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
* @return none
*/
public function Reset($sParamName)
{
if (isset($this->aValues[$sParamName]))
{
unset($this->aValues[$sParamName]);
}
}
/**
* Initializes the given object with the default values provided by the context
*/
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
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsWritable())
{
$value = $this->GetCurrentValue($key, null);
if (!is_null($value))
{
$oObj->Set($sAttCode, $value);
}
}
}
}
}
}
static $m_sUrlMakerClass = null;
/**
* Set the current application url provider
* @param sClass string Class implementing iDBObjectURLMaker
* @return void
*/
public static function SetUrlMakerClass($sClass = 'iTopStandardURLMaker')
{
$sPrevious = self::GetUrlMakerClass();
self::$m_sUrlMakerClass = $sClass;
$_SESSION['UrlMakerClass'] = $sClass;
return $sPrevious;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
public static function GetUrlMakerClass()
{
if (is_null(self::$m_sUrlMakerClass))
{
if (isset($_SESSION['UrlMakerClass']))
{
self::$m_sUrlMakerClass = $_SESSION['UrlMakerClass'];
}
else
{
self::$m_sUrlMakerClass = 'iTopStandardURLMaker';
}
}
return self::$m_sUrlMakerClass;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
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 (strlen($sUrl) > 0)
{
if ($bWithNavigationContext)
{
return $sUrl."&".$oAppContext->GetForLink();
}
else
{
return $sUrl;
}
}
else
{
return '';
}
}
protected static $m_aPluginProperties = null;
/**
* Load plugin properties for the current session
* @return void
*/
protected static function LoadPluginProperties()
{
if (isset($_SESSION['PluginProperties']))
{
self::$m_aPluginProperties = $_SESSION['PluginProperties'];
}
else
{
self::$m_aPluginProperties = array();
}
}
/**
* Set plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @param sProperty string Name of the property
* @param value scalar Value (numeric or string)
* @return void
*/
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
$_SESSION['PluginProperties'][$sPluginClass][$sProperty] = $value;
}
/**
* Get plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @return array of sProperty=>value pairs
*/
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];
}
else
{
return array();
}
}
}
?>
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class ApplicationContext
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT."/application/utils.inc.php");
/**
* Interface for directing end-users to the relevant application
*/
interface iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId);
}
/**
* Direct end-users to the standard iTop application: UI.php
*/
class iTopStandardURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sPage = DBObject::ComputeStandardUIPage($sClass);
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}pages/$sPage?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Direct end-users to the standard Portal application
*/
class PortalURLMaker implements iDBObjectURLMaker
{
public static function MakeObjectURL($sClass, $iId)
{
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
$sUrl = "{$sAbsoluteUrl}portal/index.php?operation=details&class=$sClass&id=$iId";
return $sUrl;
}
}
/**
* Helper class to store and manipulate the parameters that make the application's context
*
* Usage:
* 1) Build the application's context by constructing the object
* (the object will read some of the page's parameters)
*
* 2) Add these parameters to hyperlinks or to forms using the helper, functions
* GetForLink(), GetForForm() or GetAsHash()
*/
class ApplicationContext
{
protected $aNames;
protected $aValues;
protected static $aDefaultValues; // Cache shared among all instances
public function __construct($bReadContext = true)
{
$this->aNames = array(
'org_id', 'menu'
);
if ($bReadContext)
{
$this->ReadContext();
}
}
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*/
protected function ReadContext()
{
if (!isset(self::$aDefaultValues))
{
self::$aDefaultValues = array();
$aContext = utils::ReadParam('c', array(), 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))
{
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
if ($sName == 'org_id')
{
if (MetaModel::IsValidClass('Organization'))
{
$oSearchFilter = new DBObjectSearch('Organization');
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->Count();
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
* @return mixed The value for this parameter
*/
public function GetCurrentValue($sParamName, $defaultValue = '')
{
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
*/
public function GetForLink()
{
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
return implode("&", $aParams);
}
/**
* Returns the context as sequence of input tags to be inserted inside a <form> tag
* return string The context as a sequence of <input type="hidden" /> tags
*/
public function GetForForm()
{
$sContext = "";
foreach($this->aValues as $sName => $sValue)
{
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
}
return $sContext;
}
/**
* Returns the context as a hash array 'parameter_name' => value
* return array The context information
*/
public function GetAsHash()
{
$aReturn = array();
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
*/
public function GetNames()
{
return $this->aNames;
}
/**
* 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
* @return none
*/
public function Reset($sParamName)
{
if (isset($this->aValues[$sParamName]))
{
unset($this->aValues[$sParamName]);
}
}
/**
* Initializes the given object with the default values provided by the context
*/
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
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsWritable())
{
$value = $this->GetCurrentValue($key, null);
if (!is_null($value))
{
$oObj->Set($sAttCode, $value);
}
}
}
}
}
}
static $m_sUrlMakerClass = null;
/**
* Set the current application url provider
* @param sClass string Class implementing iDBObjectURLMaker
* @return void
*/
public static function SetUrlMakerClass($sClass = 'iTopStandardURLMaker')
{
$sPrevious = self::GetUrlMakerClass();
self::$m_sUrlMakerClass = $sClass;
$_SESSION['UrlMakerClass'] = $sClass;
return $sPrevious;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
public static function GetUrlMakerClass()
{
if (is_null(self::$m_sUrlMakerClass))
{
if (isset($_SESSION['UrlMakerClass']))
{
self::$m_sUrlMakerClass = $_SESSION['UrlMakerClass'];
}
else
{
self::$m_sUrlMakerClass = 'iTopStandardURLMaker';
}
}
return self::$m_sUrlMakerClass;
}
/**
* Get the current application url provider
* @return string the name of the class
*/
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 (strlen($sUrl) > 0)
{
if ($bWithNavigationContext)
{
return $sUrl."&".$oAppContext->GetForLink();
}
else
{
return $sUrl;
}
}
else
{
return '';
}
}
protected static $m_aPluginProperties = null;
/**
* Load plugin properties for the current session
* @return void
*/
protected static function LoadPluginProperties()
{
if (isset($_SESSION['PluginProperties']))
{
self::$m_aPluginProperties = $_SESSION['PluginProperties'];
}
else
{
self::$m_aPluginProperties = array();
}
}
/**
* Set plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @param sProperty string Name of the property
* @param value scalar Value (numeric or string)
* @return void
*/
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
$_SESSION['PluginProperties'][$sPluginClass][$sProperty] = $value;
}
/**
* Get plugin properties
* @param sPluginClass string Class implementing any plugin interface
* @return array of sProperty=>value pairs
*/
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];
}
else
{
return array();
}
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,28 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* 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
* inside the set
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
@@ -34,7 +33,7 @@ class AuditCategory extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"category" => "application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -47,14 +46,14 @@ class AuditCategory extends cmdbAbstractObject
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 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())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('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', array('name', 'description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('name', 'description', 'definition_set')); // Criteria of the advanced search form
}
}
?>

View File

@@ -1,21 +1,18 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* This class manages the audit "rule" linked to a given audit category.
@@ -23,8 +20,10 @@
* or the "bad" ones. The core audit engines computes the complement to the definition
* set when needed to obtain either the valid objects, or the ones with an error
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/application/audit.category.class.inc.php');
@@ -35,7 +34,7 @@ class AuditRule extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"category" => "application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -57,8 +56,8 @@ class AuditRule extends cmdbAbstractObject
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
// 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', array('category_id', 'name', 'description', 'valid_flag')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the advanced search form
}
}
?>

View File

@@ -1,84 +0,0 @@
<?php
// Copyright (C) 2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Adapter class: when an API requires WebPage and you want to produce something else
*
* @copyright Copyright (C) 2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT."/application/webpage.class.inc.php");
class CaptureWebPage extends WebPage
{
protected $aReadyScripts;
function __construct()
{
parent::__construct('capture web page');
$this->aReadyScripts = array();
}
public function GetHtml()
{
$trash = $this->ob_get_clean_safe();
return $this->s_content;
}
public function GetJS()
{
$sRet = implode("\n", $this->a_scripts);
if (!empty($this->s_deferred_content))
{
$sRet .= "\n\$('body').append('".addslashes(str_replace("\n", '', $this->s_deferred_content))."');";
}
return $sRet;
}
public function GetReadyJS()
{
return "\$(document).ready(function() {\n".implode("\n", $this->aReadyScripts)."\n});";
}
public function GetCSS()
{
return $this->a_styles;
}
public function GetJSFiles()
{
return $this->a_linked_scripts;
}
public function GetCSSFiles()
{
return $this->a_linked_stylesheets;
}
public function output()
{
throw new Exception(__method__.' should not be called');
}
public function add_ready_script($sScript)
{
$this->aReadyScripts[] = $sScript;
}
}

View File

@@ -1,28 +1,27 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* CLI page
* The page adds the content-type text/XML and the encoding into the headers
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT."/application/webpage.class.inc.php");
@@ -35,14 +34,6 @@ class CLIPage implements Page
public function output()
{
if (class_exists('DBSearch'))
{
DBSearch::RecordQueryTrace();
}
if (class_exists('ExecutionKPI'))
{
ExecutionKPI::ReportStats();
}
}
public function add($sText)

File diff suppressed because it is too large Load Diff

View File

@@ -1,28 +1,27 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Simple web page with no includes or fancy formatting, useful to generateXML documents
* The page adds the content-type text/XML and the encoding into the headers
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT."/application/webpage.class.inc.php");
@@ -32,33 +31,18 @@ class CSVPage extends WebPage
function __construct($s_title)
{
parent::__construct($s_title);
$this->add_header("Content-type: text/plain; charset=utf-8");
$this->add_header("Content-type: text/html; charset=utf-8");
$this->add_header("Cache-control: no-cache");
//$this->add_header("Content-Transfer-Encoding: binary");
}
public function output()
{
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
foreach($this->a_headers as $s_header)
{
header($s_header);
}
echo trim($this->s_content);
echo "\n";
if (class_exists('DBSearch'))
{
DBSearch::RecordQueryTrace();
}
if (class_exists('ExecutionKPI'))
{
ExecutionKPI::ReportStats();
}
}
public function small_p($sText)
@@ -109,3 +93,4 @@ class CSVPage extends WebPage
}
}
?>

View File

@@ -1,54 +1,43 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2012 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
require_once(APPROOT.'application/dashboardlayout.class.inc.php');
require_once(APPROOT.'application/dashlet.class.inc.php');
require_once(APPROOT.'core/modelreflection.class.inc.php');
/**
* A user editable dashboard page
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class Dashboard
{
protected $sTitle;
protected $bAutoReload;
protected $iAutoReloadSec;
protected $sLayoutClass;
protected $aWidgetsData;
protected $oDOMNode;
protected $sId;
protected $aCells;
protected $oMetaModel;
public function __construct($sId)
{
$this->sTitle = '';
$this->sLayoutClass = 'DashboardLayoutOneCol';
$this->bAutoReload = false;
$this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval();
$this->sLayoutClass = null;
$this->aCells = array();
$this->oDOMNode = null;
$this->sId = $sId;
}
public function FromXml($sXml)
{
$this->aCells = array(); // reset the content of the dashboard
@@ -56,113 +45,32 @@ abstract class Dashboard
$oDoc = new DOMDocument();
$oDoc->loadXML($sXml);
restore_error_handler();
$this->FromDOMDocument($oDoc);
}
public function FromDOMDocument(DOMDocument $oDoc)
{
$this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0);
if ($oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0))
{
$this->sLayoutClass = $oLayoutNode->textContent;
}
else
{
$this->sLayoutClass = 'DashboardLayoutOneCol';
}
$oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0);
$this->sLayoutClass = $oLayoutNode->textContent;
if ($oTitleNode = $this->oDOMNode->getElementsByTagName('title')->item(0))
{
$this->sTitle = $oTitleNode->textContent;
}
else
{
$this->sTitle = '';
}
$oTitleNode = $this->oDOMNode->getElementsByTagName('title')->item(0);
$this->sTitle = $oTitleNode->textContent;
$this->bAutoReload = false;
$this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval();
if ($oAutoReloadNode = $this->oDOMNode->getElementsByTagName('auto_reload')->item(0))
$oCellsNode = $this->oDOMNode->getElementsByTagName('cells')->item(0);
$oCellsList = $oCellsNode->getElementsByTagName('cell');
foreach($oCellsList as $oCellNode)
{
if ($oAutoReloadEnabled = $oAutoReloadNode->getElementsByTagName('enabled')->item(0))
$aDashletList = array();
$oDashletList = $oCellNode->getElementsByTagName('dashlet');
foreach($oDashletList as $oDomNode)
{
$this->bAutoReload = ($oAutoReloadEnabled->textContent == 'true');
$sDashletClass = $oDomNode->getAttribute('xsi:type');
$sId = $oDomNode->getAttribute('id');
$oNewDashlet = new $sDashletClass($sId);
$oNewDashlet->FromDOMNode($oDomNode);
$aDashletList[] = $oNewDashlet;
}
if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0))
{
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$oAutoReloadInterval->textContent);
}
}
if ($oCellsNode = $this->oDOMNode->getElementsByTagName('cells')->item(0))
{
$oCellsList = $oCellsNode->getElementsByTagName('cell');
$aCellOrder = array();
$iCellRank = 0;
foreach($oCellsList as $oCellNode)
{
$aDashletList = array();
$oCellRank = $oCellNode->getElementsByTagName('rank')->item(0);
if ($oCellRank)
{
$iCellRank = (float)$oCellRank->textContent;
}
$oDashletsNode = $oCellNode->getElementsByTagName('dashlets')->item(0);
{
$oDashletList = $oDashletsNode->getElementsByTagName('dashlet');
$iRank = 0;
$aDashletOrder = array();
foreach($oDashletList as $oDomNode)
{
$oRank = $oDomNode->getElementsByTagName('rank')->item(0);
if ($oRank)
{
$iRank = (float)$oRank->textContent;
}
$oNewDashlet = $this->InitDashletFromDOMNode($oDomNode);
$aDashletOrder[] = array('rank' => $iRank, 'dashlet' => $oNewDashlet);
}
usort($aDashletOrder, array(get_class($this), 'SortOnRank'));
$aDashletList = array();
foreach($aDashletOrder as $aItem)
{
$aDashletList[] = $aItem['dashlet'];
}
$aCellOrder[] = array('rank' => $iCellRank, 'dashlets' => $aDashletList);
}
}
usort($aCellOrder, array(get_class($this), 'SortOnRank'));
foreach($aCellOrder as $aItem)
{
$this->aCells[] = $aItem['dashlets'];
}
}
else
{
$this->aCells = array();
$this->aCells[] = $aDashletList;
}
}
protected function InitDashletFromDOMNode($oDomNode)
{
$sId = $oDomNode->getAttribute('id');
$sDashletType = $oDomNode->getAttribute('xsi:type');
// Test if dashlet can be instanciated, otherwise (uninstalled, broken, ...) we display a placeholder
$sClass = static::GetDashletClassFromType($sDashletType);
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
$oNewDashlet->SetDashletType($sDashletType);
$oNewDashlet->FromDOMNode($oDomNode);
return $oNewDashlet;
}
static function SortOnRank($aItem1, $aItem2)
{
return ($aItem1['rank'] > $aItem2['rank']) ? +1 : -1;
}
/**
* Error handler to turn XML loading warnings into exceptions
*/
@@ -188,66 +96,37 @@ abstract class Dashboard
$oMainNode->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
$oDoc->appendChild($oMainNode);
$this->ToDOMNode($oMainNode);
$oNode = $oDoc->createElement('layout', $this->sLayoutClass);
$oMainNode->appendChild($oNode);
$oNode = $oDoc->createElement('title', $this->sTitle);
$oMainNode->appendChild($oNode);
$oCellsNode = $oDoc->createElement('cells');
$oMainNode->appendChild($oCellsNode);
foreach ($this->aCells as $aCell)
{
$oCellNode = $oDoc->createElement('cell');
$oCellsNode->appendChild($oCellNode);
foreach ($aCell as $oDashlet)
{
$oNode = $oDoc->createElement('dashlet');
$oCellNode->appendChild($oNode);
$oNode->setAttribute('id', $oDashlet->GetID());
$oNode->setAttribute('xsi:type', get_class($oDashlet));
$oDashlet->ToDOMNode($oNode);
}
}
$sXml = $oDoc->saveXML();
return $sXml;
}
public function ToDOMNode($oDefinition)
{
$oDoc = $oDefinition->ownerDocument;
$oNode = $oDoc->createElement('layout', $this->sLayoutClass);
$oDefinition->appendChild($oNode);
$oNode = $oDoc->createElement('title', $this->sTitle);
$oDefinition->appendChild($oNode);
$oAutoReloadNode = $oDoc->createElement('auto_reload');
$oDefinition->appendChild($oAutoReloadNode);
$oNode = $oDoc->createElement('enabled', $this->bAutoReload ? 'true' : 'false');
$oAutoReloadNode->appendChild($oNode);
$oNode = $oDoc->createElement('interval', $this->iAutoReloadSec);
$oAutoReloadNode->appendChild($oNode);
$oCellsNode = $oDoc->createElement('cells');
$oDefinition->appendChild($oCellsNode);
$iCellRank = 0;
foreach ($this->aCells as $aCell)
{
$oCellNode = $oDoc->createElement('cell');
$oCellNode->setAttribute('id', $iCellRank);
$oCellsNode->appendChild($oCellNode);
$oCellRank = $oDoc->createElement('rank', $iCellRank);
$oCellNode->appendChild($oCellRank);
$iCellRank++;
$iDashletRank = 0;
$oDashletsNode = $oDoc->createElement('dashlets');
$oCellNode->appendChild($oDashletsNode);
foreach ($aCell as $oDashlet)
{
$oNode = $oDoc->createElement('dashlet');
$oDashletsNode->appendChild($oNode);
$oNode->setAttribute('id', $oDashlet->GetID());
$oNode->setAttribute('xsi:type', $oDashlet->GetDashletType());
$oDashletRank = $oDoc->createElement('rank', $iDashletRank);
$oNode->appendChild($oDashletRank);
$iDashletRank++;
$oDashlet->ToDOMNode($oNode);
}
}
}
public function FromParams($aParams)
{
$this->sLayoutClass = $aParams['layout_class'];
$this->sTitle = $aParams['title'];
$this->bAutoReload = $aParams['auto_reload'] == 'true';
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
foreach($aParams['cells'] as $aCell)
{
@@ -256,11 +135,8 @@ abstract class Dashboard
{
$sDashletClass = $aDashletParams['dashlet_class'];
$sId = $aDashletParams['dashlet_id'];
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
if (isset($aDashletParams['dashlet_type']))
{
$oNewDashlet->SetDashletType($aDashletParams['dashlet_type']);
}
$oNewDashlet = new $sDashletClass($sId);
$oForm = $oNewDashlet->GetForm();
$oForm->SetParamsContainer($sId);
$oForm->SetPrefix('');
@@ -272,7 +148,7 @@ abstract class Dashboard
}
}
public function Save()
{
@@ -292,32 +168,12 @@ abstract class Dashboard
{
return $this->sTitle;
}
public function SetTitle($sTitle)
{
$this->sTitle = $sTitle;
}
public function GetAutoReload()
{
return $this->bAutoReload;
}
public function SetAutoReload($bAutoReload)
{
$this->bAutoReload = $bAutoReload;
}
public function GetAutoReloadInterval()
{
return $this->iAutoReloadSec;
}
public function SetAutoReloadInterval($iAutoReloadSec)
{
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$iAutoReloadSec);
}
public function AddDashlet($oDashlet)
{
$sId = $this->GetNewDashletId();
@@ -327,7 +183,7 @@ abstract class Dashboard
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$oPage->add('<h1>'.htmlentities(Dict::S($this->sTitle), ENT_QUOTES, 'UTF-8', false).'</h1>');
$oPage->add('<h1>'.Dict::S($this->sTitle).'</h1>');
$oLayout = new $this->sLayoutClass;
$oLayout->Render($oPage, $this->aCells, $bEditMode, $aExtraParams);
if (!$bEditMode)
@@ -362,62 +218,24 @@ abstract class Dashboard
$oPage->add('</div>');
$oForm = new DesignerForm();
$oField = new DesignerHiddenField('dashboard_id', '', $this->sId);
$oForm->AddField($oField);
$oField = new DesignerLongTextField('dashboard_title', Dict::S('UI:DashboardEdit:DashboardTitle'), $this->sTitle);
$oForm->AddField($oField);
$oField = new DesignerBooleanField('auto_reload', Dict::S('UI:DashboardEdit:AutoReload'), $this->bAutoReload);
$oForm->AddField($oField);
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('UI:DashboardEdit:AutoReloadSec'), $this->iAutoReloadSec);
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oForm->AddField($oField);
$this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oForm->RenderAsPropertySheet($oPage, false, ':itop-dashboard');
$oPage->add('</div>');
$sRateTitle = addslashes(Dict::Format('UI:DashboardEdit:AutoReloadSec+', MetaModel::GetConfig()->Get('min_reload_interval')));
$oPage->add_ready_script(
<<<EOF
// Note: the title gets deleted by the validation mechanism
$("#attr_auto_reload_sec").tooltip({items: 'input', content: '$sRateTitle'});
$("#attr_auto_reload_sec").prop('disabled', !$('#attr_auto_reload').is(':checked'));
$('#attr_auto_reload').change( function(ev) {
$("#attr_auto_reload_sec").prop('disabled', !$(this).is(':checked'));
} );
$('#select_layout').buttonset();
$('#select_dashlet').droppable({
accept: '.dashlet',
drop: function(event, ui) {
$( this ).find( ".placeholder" ).remove();
var oDashlet = ui.draggable.data('itopDashlet');
oDashlet._remove_dashlet();
},
});
$('#event_bus').bind('dashlet-selected', function(event, data){
var sDashletId = data.dashlet_id;
var sPropId = 'dashlet_properties_'+sDashletId;
$('.dashlet_properties').each(function() {
var sId = $(this).attr('id');
var bShow = (sId == sPropId);
if (bShow)
{
$(this).show();
}
else
{
$(this).hide();
}
});
$('#select_layout input').click( function() {
var sLayoutClass = $(this).val();
$(':itop-dashboard').dashboard('option', {layout_class: sLayoutClass});
} );
$('#row_attr_dashboard_title').property_field('option', {parent_selector: ':itop-dashboard', auto_apply: false, 'do_apply': function() {
var sTitle = $('#attr_dashboard_title').val();
$(':itop-dashboard').dashboard('option', {title: sTitle});
return true;
}
});
EOF
);
@@ -429,16 +247,30 @@ EOF
$oPage->add('<div class="ui-widget-content ui-corner-all"><div class="ui-widget-header ui-corner-all" style="text-align:center; padding: 2px;">'.Dict::S('UI:DashboardEdit:Dashlets').'</div>');
$sUrl = utils::GetAbsoluteUrlAppRoot();
$oPage->add('<div id="select_dashlet" style="text-align:center; max-height:120px; overflow-y:auto;">');
$aAvailableDashlets = $this->GetAvailableDashlets();
foreach($aAvailableDashlets as $sDashletClass => $aInfo)
$oPage->add('<div id="select_dashlet" style="text-align:center">');
foreach( get_declared_classes() as $sDashletClass)
{
$oPage->add('<span dashlet_class="'.$sDashletClass.'" class="dashlet_icon ui-widget-content ui-corner-all" id="dashlet_'.$sDashletClass.'" title="'.$aInfo['label'].'" style="width:34px; height:34px; display:inline-block; margin:2px;"><img src="'.$sUrl.$aInfo['icon'].'" /></span>');
if (is_subclass_of($sDashletClass, 'Dashlet'))
{
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sDashletClass, 'IsVisible');
$bVisible = call_user_func($aCallSpec);
if ($bVisible)
{
$aCallSpec = array($sDashletClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$oPage->add('<span dashlet_class="'.$sDashletClass.'" class="dashlet_icon ui-widget-content ui-corner-all" id="dashlet_'.$sDashletClass.'" title="'.$aInfo['label'].'" style="width:34px; height:34px; display:inline-block; margin:2px;"><img src="'.$sUrl.$aInfo['icon'].'" /></span>');
}
}
}
}
$oPage->add('</div>');
$oPage->add('</div>');
$oPage->add_ready_script("$('.dashlet_icon').draggable({helper: 'clone', appendTo: 'body', zIndex: 10000, revert:'invalid'});");
$oPage->add_ready_script("$('.layout_cell').droppable({accept:'.dashlet_icon', hoverClass:'dragHover'});");
}
public function RenderDashletsProperties($oPage)
@@ -458,7 +290,7 @@ EOF
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$sId.'" style="display:none">');
$oForm = $oDashlet->GetForm();
$this->SetFormParams($oForm);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
$oForm->RenderAsPropertySheet($oPage, false, ':itop-dashboard');
$oPage->add('</div>');
}
}
@@ -467,38 +299,6 @@ EOF
$oPage->add('</div>');
}
/**
* Return an array of dashlets available for selection.
*
* @return array
*/
protected function GetAvailableDashlets()
{
$aDashlets = array();
foreach( get_declared_classes() as $sDashletClass)
{
// DashletUnknown is not among the selection as it is just a fallback for dashlets that can't instanciated.
if ( is_subclass_of($sDashletClass, 'Dashlet') && !in_array($sDashletClass, array('DashletUnknown', 'DashletProxy')) )
{
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sDashletClass, 'IsVisible');
$bVisible = call_user_func($aCallSpec);
if ($bVisible)
{
$aCallSpec = array($sDashletClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$aDashlets[$sDashletClass] = $aInfo;
}
}
}
}
return $aDashlets;
}
protected function GetNewDashletId()
{
@@ -514,26 +314,16 @@ EOF
}
abstract protected function SetFormParams($oForm);
public static function GetDashletClassFromType($sType, $oFactory = null)
{
if (is_subclass_of($sType, 'Dashlet'))
{
return $sType;
}
return 'DashletUnknown';
}
}
class RuntimeDashboard extends Dashboard
{
protected $bCustomized;
public function __construct($sId)
{
parent::__construct($sId);
$this->bCustomized = false;
$this->oMetaModel = new ModelReflectionRuntime();
}
public function SetCustomFlag($bCustomized)
@@ -558,6 +348,8 @@ class RuntimeDashboard extends Dashboard
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$oUserDashboard->Set('contents', $sXml);
$oUserDashboard->DBUpdate();
}
else
{
@@ -566,10 +358,9 @@ class RuntimeDashboard extends Dashboard
$oUserDashboard->Set('user_id', UserRights::GetUserId());
$oUserDashboard->Set('menu_code', $this->sId);
$oUserDashboard->Set('contents', $sXml);
}
utils::PushArchiveMode(false);
$oUserDashboard->DBWrite();
utils::PopArchiveMode();
$oUserDashboard->DBInsert();
}
}
public function Revert()
@@ -582,42 +373,41 @@ class RuntimeDashboard extends Dashboard
{
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
utils::PushArchiveMode(false);
$oUserDashboard->DBDelete();
utils::PopArchiveMode();
}
}
public function RenderEditionTools($oPage)
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/pencil-menu.png\"><ul>";
$aActions = array();
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
if ($this->bCustomized)
parent::Render($oPage, $bEditMode, $aExtraParams);
if (!$bEditMode)
{
$oRevert = new JSPopupMenuItem('UI:Dashboard:RevertConfirm', Dict::S('UI:Dashboard:Revert'),
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}'); else return false");
$aActions[$oRevert->GetUID()] = $oRevert->GetMenuItem();
}
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
$sEditMenu .= $oPage->RenderPopupMenuItems($aActions);
$sEditMenu = "<td><span id=\"DashboardMenu\"><ul><li><img src=\"../images/edit.png\"><ul>";
$aActions = array();
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:Edit'), "return EditDashboard('{$this->sId}')");
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
$sEditMenu = addslashes($sEditMenu);
//$sEditBtn = addslashes('<div style="display: inline-block; height: 55px; width:200px;vertical-align:center;line-height:60px;text-align:left;"><button onclick="EditDashboard(\''.$this->sId.'\');">Edit This Page</button></div>');
$oPage->add_ready_script(
if ($this->bCustomized)
{
$oRevert = new JSPopupMenuItem('UI:Dashboard:RevertConfirm', Dict::S('UI:Dashboard:Revert'),
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}'); else return false");
$aActions[$oRevert->GetUID()] = $oRevert->GetMenuItem();
}
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
$sEditMenu .= $oPage->RenderPopupMenuItems($aActions);
$sEditMenu = addslashes($sEditMenu);
//$sEditBtn = addslashes('<div style="display: inline-block; height: 55px; width:200px;vertical-align:center;line-height:60px;text-align:left;"><button onclick="EditDashboard(\''.$this->sId.'\');">Edit This Page</button></div>');
$oPage->add_ready_script(
<<<EOF
$('#logOffBtn').parent().before('$sEditMenu');
$('#DashboardMenu>ul').popupmenu();
EOF
);
$oPage->add_script(
);
$oPage->add_script(
<<<EOF
function EditDashboard(sId)
{
@@ -640,42 +430,10 @@ function RevertDashboard(sId)
return false;
}
EOF
);
);
}
}
public function RenderProperties($oPage)
{
parent::RenderProperties($oPage);
$oPage->add_ready_script(
<<<EOF
$('#select_layout input').click( function() {
var sLayoutClass = $(this).val();
$('.itop-dashboard').runtimedashboard('option', {layout_class: sLayoutClass});
} );
$('#row_attr_dashboard_title').property_field('option', {parent_selector: '.itop-dashboard', auto_apply: false, 'do_apply': function() {
var sTitle = $('#attr_dashboard_title').val();
$('.itop-dashboard').runtimedashboard('option', {title: sTitle});
return true;
}
});
$('#row_attr_auto_reload').property_field('option', {parent_selector: '.itop-dashboard', auto_apply: true, 'do_apply': function() {
var bAutoReload = $('#attr_auto_reload').is(':checked');
$('.itop-dashboard').runtimedashboard('option', {auto_reload: bAutoReload});
return true;
}
});
$('#row_attr_auto_reload_sec').property_field('option', {parent_selector: '.itop-dashboard', auto_apply: true, 'do_apply': function() {
var iAutoReloadSec = $('#attr_auto_reload_sec').val();
$('.itop-dashboard').runtimedashboard('option', {auto_reload_sec: iAutoReloadSec});
return true;
}
});
EOF
);
}
public function RenderEditor($oPage)
{
$oPage->add('<div id="dashboard_editor">');
@@ -696,8 +454,6 @@ EOF
$sId = addslashes($this->sId);
$sLayoutClass = addslashes($this->sLayoutClass);
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = addslashes($this->sTitle);
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
@@ -716,7 +472,7 @@ $('#dashboard_editor').dialog({
title: '$sDialogTitle',
buttons: [
{ text: "$sOkButtonLabel", click: function() {
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
var oDashboard = $(':itop-dashboard').data('dashboard');
if (oDashboard.is_dirty())
{
if (!confirm('$sAutoApplyConfirmationMessage'))
@@ -732,7 +488,7 @@ $('#dashboard_editor').dialog({
oDashboard.save();
} },
{ text: "$sCancelButtonLabel", click: function() {
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
var oDashboard = $(':itop-dashboard').data('dashboard');
if (oDashboard.is_modified())
{
if (!confirm('$sCancelConfirmationMessage'))
@@ -748,14 +504,40 @@ $('#dashboard_editor').dialog({
close: function() { $(this).remove(); }
});
$('#dashboard_editor .ui-layout-center').runtimedashboard({
$('#dashboard_editor .ui-layout-center').dashboard({
dashboard_id: '$sId', layout_class: '$sLayoutClass', title: '$sTitle',
auto_reload: $sAutoReload, auto_reload_sec: $sAutoReloadSec,
submit_to: '$sUrl', submit_parameters: {operation: 'save_dashboard'},
render_to: '$sUrl', render_parameters: {operation: 'render_dashboard'},
new_dashlet_parameters: {operation: 'new_dashlet'}
});
$('#select_dashlet').droppable({
accept: '.dashlet',
drop: function(event, ui) {
$( this ).find( ".placeholder" ).remove();
var oDashlet = ui.draggable;
oDashlet.remove();
},
});
$('#event_bus').bind('dashlet-selected', function(event, data){
var sDashletId = data.dashlet_id;
var sPropId = 'dashlet_properties_'+sDashletId;
$('.dashlet_properties').each(function() {
var sId = $(this).attr('id');
var bShow = (sId == sPropId);
if (bShow)
{
$(this).show();
}
else
{
$(this).hide();
}
});
});
dashboard_prop_size = GetUserPreference('dashboard_prop_size', 350);
$('#dashboard_editor').layout({
east: {
@@ -776,7 +558,7 @@ $('#dashboard_editor').layout({
window.onbeforeunload = function() {
if (!window.bLeavingOnUserAction)
{
var oDashboard = $('.itop-dashboard').data('itopRuntimedashboard');
var oDashboard = $(':itop-dashboard').data('dashboard');
if (oDashboard)
{
if (oDashboard.is_dirty())
@@ -798,65 +580,33 @@ EOF
public static function GetDashletCreationForm($sOQL = null)
{
$oAppContext = new ApplicationContext();
$sContextMenuId = $oAppContext->GetCurrentValue('menu', null);
$oForm = new DesignerForm();
// Get the list of all 'dashboard' menus in which we can insert a dashlet
$aAllMenus = ApplicationMenu::ReflectionMenuNodes();
$sRootMenuId = ApplicationMenu::GetRootMenuId($sContextMenuId);
$aAllowedDashboards = array();
$sDefaultDashboard = null;
// Store the parent menus for acces check
$aParentMenus = array();
foreach($aAllMenus as $idx => $aMenu)
{
/** @var MenuNode $oMenu */
$oMenu = $aMenu['node'];
if (count(ApplicationMenu::GetChildren($oMenu->GetIndex())) > 0)
{
$aParentMenus[$oMenu->GetMenuId()] = $aMenu;
}
}
foreach($aAllMenus as $idx => $aMenu)
foreach($aAllMenus as $idx => $aMenu)
{
$oMenu = $aMenu['node'];
if ($oMenu instanceof DashboardMenuNode)
{
// Get the root parent for access check
$sParentId = $aMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
while (isset($aParentMenus[$aParentMenu['parent']]))
{
// grand parent exists
$sParentId = $aParentMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
}
$oParentMenu = $aParentMenu['node'];
if ($oMenu->IsEnabled() && $oParentMenu->IsEnabled())
{
$sMenuLabel = $oMenu->GetTitle();
$sParentLabel = Dict::S('Menu:'.$sParentId);
if ($sParentLabel != $sMenuLabel)
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
}
else
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
}
if (empty($sDefaultDashboard) && ($sRootMenuId == ApplicationMenu::GetRootMenuId($oMenu->GetMenuId())))
{
$sDefaultDashboard = $oMenu->GetMenuId();
}
}
}
$sParentId = $aMenu['parent'];
if ($oMenu instanceof DashboardMenuNode)
{
$sMenuLabel = $oMenu->GetTitle();
$sParentLabel = Dict::S('Menu:'.$sParentId);
if ($sParentLabel != $sMenuLabel)
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
}
else
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
}
}
}
asort($aAllowedDashboards);
$aKeys = array_keys($aAllowedDashboards); // Select the first one by default
$sDefaultDashboard = $aKeys[0];
$oField = new DesignerComboField('menu_id', Dict::S('UI:DashletCreation:Dashboard'), $sDefaultDashboard);
$oField->SetAllowedValues($aAllowedDashboards);
$oField->SetMandatory(true);
@@ -889,8 +639,7 @@ EOF
foreach($aDashlets as $sDashletClass => $aDashletInfo)
{
$oSubForm = new DesignerForm();
$oMetaModel = new ModelReflectionRuntime();
$oDashlet = new $sDashletClass($oMetaModel, 0);
$oDashlet = new $sDashletClass(0);
$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
$oSelectorField->AddSubForm($oSubForm, $aDashletInfo['label'], $aDashletInfo['class']);
@@ -917,7 +666,7 @@ EOF
$oPage->add_ready_script(
<<<EOF
$('#dashlet_creation_dlg').dialog({
width: 600,
width: 400,
modal: true,
title: '$sDialogTitle',
buttons: [

View File

@@ -1,29 +1,4 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Dashboard presentation
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class DashboardLayout
{
public function __construct()
@@ -104,19 +79,17 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
// Trim the list of cells to remove the invisible/empty ones at the end of the array
$aCells = $this->TrimCellsArray($aCells);
$oPage->add('<table style="width:100%;table-layout:fixed;"><tbody>');
$oPage->add('<table style="width:100%"><tbody>');
$iCellIdx = 0;
$fColSize = 100 / $this->iNbCols;
$sStyle = $bEditMode ? 'border: 1px #ccc dashed; width:'.$fColSize.'%;' : 'width: '.$fColSize.'%;';
$sClass = $bEditMode ? 'layout_cell edit_mode' : 'dashboard';
$sStyle = $bEditMode ? 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode"' : 'style="width: '.$fColSize.'%;" class="dashboard"';
$iNbRows = ceil(count($aCells) / $this->iNbCols);
for($iRows = 0; $iRows < $iNbRows; $iRows++)
{
$oPage->add('<tr>');
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
{
$sCellClass = ($iRows == $iNbRows-1) ? $sClass.' layout_last_used_rank' : $sClass;
$oPage->add("<td style=\"$sStyle\" class=\"$sCellClass\" data-dashboard-cell-index=\"$iCellIdx\">");
$oPage->add("<td $sStyle>");
if (array_key_exists($iCellIdx, $aCells))
{
$aDashlets = $aCells[$iCellIdx];
@@ -146,7 +119,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
}
if ($bEditMode) // Add one row for extensibility
{
$sStyle = 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode layout_extension" data-dashboard-cell-index="'.$iCellIdx.'"';
$sStyle = 'style="border: 1px #ccc dashed; width:'.$fColSize.'%;" class="layout_cell edit_mode layout_extension"';
$oPage->add('<tr>');
for($iCols = 0; $iCols < $this->iNbCols; $iCols++)
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
<portals>
<portal id="legacy_portal" _delta="define">
<url>portal/index.php</url>
<rank>1.0</rank>
<handler/>
<allow>
</allow>
<deny/>
</portal>
<portal id="backoffice" _delta="define">
<url>pages/UI.php</url>
<rank>2.0</rank>
<handler/>
<allow/>
<deny>
<profile id="Portal user"/>
</deny>
</portal>
</portals>
<menus>
<menu id="AdminTools" xsi:type="MenuGroup" _delta="define">
<rank>80</rank>
</menu>
</menus>
</itop_design>

View File

@@ -1,25 +1,25 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2012 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Data Table to display a set of objects in a tabular manner in HTML
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html GPL
*/
class DataTable
@@ -31,8 +31,7 @@ class DataTable
protected $iNbObjects; // Total number of objects inthe set
protected $bUseCustomSettings; // Whether or not the current display uses custom settings
protected $oDefaultSettings; // the default settings for displaying such a list
protected $bShowObsoleteData;
/**
* @param $iListId mixed Unique ID for this div/table in the page
* @param $oSet DBObjectSet The set of data to display
@@ -41,14 +40,13 @@ class DataTable
*/
public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null)
{
$this->iListId = utils::GetSafeId($iListId); // Make a "safe" ID for jQuery
$this->iListId = $iListId;
$this->oSet = $oSet;
$this->aClassAliases = $aClassAliases;
$this->sTableId = $sTableId;
$this->iNbObjects = $oSet->Count();
$this->bUseCustomSettings = false;
$this->oDefaultSettings = null;
$this->bShowObsoleteData = $oSet->GetShowObsoleteData();
}
public function Display(WebPage $oPage, DataTableSettings $oSettings, $bActionsMenu, $sSelectMode, $bViewLink, $aExtraParams)
@@ -62,10 +60,6 @@ class DataTable
{
// Custom settings overload the default ones
$this->bUseCustomSettings = true;
if ($this->oDefaultSettings->iDefaultPageSize == 0)
{
$oCustomSettings->iDefaultPageSize = 0;
}
}
else
{
@@ -77,40 +71,8 @@ class DataTable
$this->oSet->SetLimit($oCustomSettings->iDefaultPageSize);
}
$this->oSet->SetOrderBy($oCustomSettings->GetSortOrder());
// Load only the requested columns
$aColumnsToLoad = array();
foreach($oCustomSettings->aColumns as $sAlias => $aColumnsInfo)
{
foreach($aColumnsInfo as $sAttCode => $aData)
{
if ($sAttCode != '_key_')
{
if ($aData['checked'])
{
$aColumnsToLoad[$sAlias][] = $sAttCode;
}
else
{
// See if this column is a must to load
$sClass = $this->aClassAliases[$sAlias];
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->alwaysLoadInTables())
{
$aColumnsToLoad[$sAlias][] = $sAttCode;
}
}
}
}
}
$this->oSet->OptimizeColumnLoad($aColumnsToLoad);
$bToolkitMenu = true;
if (isset($aExtraParams['toolkit_menu']))
{
$bToolkitMenu = (bool) $aExtraParams['toolkit_menu'];
}
if (UserRights::IsPortalUser())
{
// Portal users have a limited access to data, for now they can only see what's configured for them
@@ -147,9 +109,7 @@ class DataTable
$sHtml .= "<tr><td class=\"datacontents\">$sDataTable</td></tr>";
$sHtml .= "</table>\n";
$oPage->add_at_the_end($sConfigDlg);
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
$aOptions = array(
'sPersistentId' => '',
'sFilter' => $this->oSet->GetFilter()->serialize(),
@@ -174,7 +134,6 @@ class DataTable
}
$sJSOptions = json_encode($aOptions);
$oPage->add_ready_script("$('#datatable_{$this->iListId}').datatable($sJSOptions);");
return $sHtml;
}
@@ -213,8 +172,6 @@ class DataTable
if ($iPageSize < 1) // Display all
{
$sPagerStyle = 'style="display:none"'; // no limit: display the full table, so hide the "pager" UI
// WARNING: mPDF does not take the "display" style into account
// when applied to a <td> or a <table> tag, so make sure you apply this to a div
}
else
{
@@ -265,8 +222,7 @@ class DataTable
$sSelectionMode = ($iNbPages == 1) ? '' : 'positive';
$sHtml =
<<<EOF
<td colspan="2">
<div $sPagerStyle>
<td $sPagerStyle colspan="2">
<table id="pager{$this->iListId}" class="pager"><tr>
<td>$sPages</td>
<td><img src="../images/first.png" class="first"/></td>
@@ -279,7 +235,6 @@ class DataTable
</td>
</tr>
</table>
</div>
</td>
EOF;
return $sHtml;
@@ -295,24 +250,17 @@ EOF;
protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
{
if (!$oPage->IsPrintableVersion())
{
$sMenuTitle = Dict::S('UI:ConfigureThisList');
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png?t='.utils::GetCacheBusterTimestamp().'"><ul>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$aActions = array(
$oMenuItem1->GetUID() => $oMenuItem1->GetMenuItem(),
);
$this->oSet->Rewind();
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $this->oSet, $aActions, $this->sTableId, $this->iListId);
$this->oSet->Rewind();
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
}
else
{
$sHtml = '';
}
$sMenuTitle = Dict::S('UI:ConfigureThisList');
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png"><ul>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$aActions = array(
$oMenuItem1->GetUID() => $oMenuItem1->GetMenuItem(),
);
$this->oSet->Rewind();
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $this->oSet, $aActions);
$this->oSet->Rewind();
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
return $sHtml;
}
@@ -393,12 +341,6 @@ EOF;
protected function GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$bLocalize = true;
if (isset($aExtraParams['localize_values']))
{
$bLocalize = (bool) $aExtraParams['localize_values'];
}
$aValues = array();
$this->oSet->Seek(0);
$iMaxObjects = $iPageSize;
@@ -408,61 +350,41 @@ EOF;
$aRow = array();
foreach($this->aClassAliases as $sAlias => $sClassName)
{
if (is_object($aObjects[$sAlias]))
$sHilightClass = $aObjects[$sAlias]->GetHilightClass();
if ($sHilightClass != '')
{
$sHilightClass = $aObjects[$sAlias]->GetHilightClass();
if ($sHilightClass != '')
$aRow['@class'] = $sHilightClass;
}
if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject)
{
if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]))
{
$aRow['@class'] = $sHilightClass;
$sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"';
}
if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject)
else
{
if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]))
{
$sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"';
}
else
{
$sDisabled = '';
}
if ($sSelectMode == 'single')
{
$aRow['form::select'] = "<input type=\"radio\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
}
else
{
$aRow['form::select'] = "<input type=\"checkBox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
}
$sDisabled = '';
}
foreach($aColumns[$sAlias] as $sAttCode => $aData)
if ($sSelectMode == 'single')
{
if ($aData['checked'])
{
if ($sAttCode == '_key_')
{
$aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink();
}
else
{
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize);
}
}
$aRow['form::select'] = "<input type=\"radio\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
}
else
{
$aRow['form::select'] = "<input type=\"checkBox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
}
}
else
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
foreach($aColumns[$sAlias] as $sAttCode => $aData)
if ($aData['checked'])
{
if ($aData['checked'])
if ($sAttCode == '_key_')
{
if ($sAttCode == '_key_')
{
$aRow['key_'.$sAlias] = '';
}
else
{
$aRow[$sAttCode.'_'.$sAlias] = '';
}
$aRow['key_'.$sAlias] = $aObjects[$sAlias]->GetHyperLink();
}
else
{
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode);
}
}
}
@@ -491,7 +413,6 @@ EOF;
{
$aExtraParams['query_params'][$sName] = $sValue;
}
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
$sHtml .= "<tr><td>";
$sHtml .= $oPage->GetTable($aAttribs, $aValues);
@@ -555,22 +476,44 @@ EOF;
$aDefaultSort[] = "[".($iColOffset).",".($bAscending ? '0' : '1')."]";
}
}
$sFakeSortList = '';
$sSortList = '';
if (count($aDefaultSort) > 0)
{
$sFakeSortList = '['.implode(',', $aDefaultSort).']';
$sSortList = ', sortList: ['.implode(',', $aDefaultSort).']';
}
$sOQL = addslashes($this->oSet->GetFilter()->serialize());
$oPage->add_ready_script(
<<<EOF
var oTable = $('#{$this->iListId} table.listResults');
oTable.tableHover();
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
oTable.tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList'] $sSortList} ).tablesorterPager({container: $('#pager{$this->iListId}'), totalRows:$iCount, size: $iPageSize, filter: '$sOQL', extra_params: '$sExtraParams', select_mode: '$sSelectModeJS', displayKey: $sDisplayKey, columns: $sJSColumns, class_aliases: $sJSClassAliases $sCssCount});
EOF
);
if ($sFakeSortList != '')
//if ($iNbPages == 1)
if (false)
{
$oPage->add_ready_script("oTable.trigger(\"fakesorton\", [$sFakeSortList]);");
if (isset($aExtraParams['cssCount']))
{
$sCssCount = $aExtraParams['cssCount'];
if ($sSelectMode == 'single')
{
$sSelectSelector = ":radio[name^=selectObj]";
}
else if ($sSelectMode == 'multiple')
{
$sSelectSelector = ":checkbox[name^=selectObj]";
}
$oPage->add_ready_script(
<<<EOF
$('#{$this->iListId} table.listResults $sSelectSelector').change(function() {
var c = $('{$sCssCount}');
var v = $('#{$this->iListId} table.listResults $sSelectSelector:checked').length;
c.val(v);
$('#{$this->iListId} .selectedCount').text(v);
c.trigger('change');
});
EOF
);
}
}
return $sHtml;
}
@@ -580,7 +523,7 @@ EOF
$iPageSize = ($iDefaultPageSize < 1) ? 1 : $iDefaultPageSize;
$iPageIndex = 1 + floor($iStart / $iPageSize);
$sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".json_encode($sHtml)."');");
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".str_replace("\n", ' ', addslashes($sHtml))."');");
if ($iDefaultPageSize < 1)
{
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().hide()");
@@ -592,34 +535,6 @@ EOF
}
}
/**
* Simplified version of the data table with less "decoration" (and no paging)
* which is optimized for printing
*/
class PrintableDataTable extends DataTable
{
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
{
return $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, -1, $bViewLink, $aExtraParams);
}
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
if ($iPageSize < 1)
{
$iPageSize = -1; // convention: no pagination
}
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sHtml = $oPage->GetTable($aAttribs, $aValues);
return $sHtml;
}
}
class DataTableSettings implements Serializable
{
public $aClassAliases;
@@ -727,12 +642,6 @@ class DataTableSettings implements Serializable
{
$sSort = $aSortOrder['friendlyname'] ? 'asc' : 'desc';
}
$sNormalizedFName = MetaModel::NormalizeFieldSpec($sClass, 'friendlyname');
if(array_key_exists($sNormalizedFName, $aSortOrder))
{
$sSort = $aSortOrder[$sNormalizedFName] ? 'asc' : 'desc';
}
$aColumns[$sAlias]['_key_'] = $oSettings->GetFieldData($sAlias, '_key_', null, true /* bChecked */, $sSort);
}
foreach($aList as $sAttCode)
@@ -785,7 +694,7 @@ class DataTableSettings implements Serializable
}
}
static public function GetTableSettings($aClassAliases, $sTableId = null, $bOnlyOnTable = false)
static public function GetTableSettings($aClassAliases, $sTableId = null)
{
$pref = null;
$oSettings = new DataTableSettings($aClassAliases, $sTableId);
@@ -798,11 +707,8 @@ class DataTableSettings implements Serializable
if ($pref == null)
{
if (!$bOnlyOnTable)
{
// Try the global preferred values for this class / set of classes
$pref = appUserPreferences::GetPref($oSettings->GetPrefsKey(null), null);
}
// Try the global preferred values for this class / set of classes
$pref = appUserPreferences::GetPref($oSettings->GetPrefsKey(null), null);
if ($pref == null)
{
// no such settings, use the default values provided by the data model
@@ -832,13 +738,12 @@ class DataTableSettings implements Serializable
return $aSortOrder;
}
public function Save($sTargetTableId = null)
public function Save()
{
$sSaveId = is_null($sTargetTableId) ? $this->sTableId : $sTargetTableId;
if ($sSaveId == null) return false; // Cannot save, the table is not identified, use SaveAsDefault instead
if ($this->sTableId == null) return false; // Cannot save, the table is not identified, use SaveAsDefault instead
$sSettings = $this->serialize();
appUserPreferences::SetPref($this->GetPrefsKey($sSaveId), $sSettings);
appUserPreferences::SetPref($this->GetPrefsKey($this->sTableId), $sSettings);
return true;
}
@@ -907,15 +812,8 @@ class DataTableSettings implements Serializable
}
else if ($oAttDef->IsExternalField())
{
if ($oAttDef->IsFriendlyName())
{
$sLabel = Dict::Format('UI:ExtKey_AsFriendlyName', $oAttDef->GetLabel());
}
else
{
$oExtAttDef = $oAttDef->GetExtAttDef();
$sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel());
}
$oExtAttDef = $oAttDef->GetExtAttDef();
$sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel());
}
elseif ($oAttDef instanceof AttributeFriendlyName)
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,536 +0,0 @@
<?php
require_once('xlsxwriter.class.php');
class ExcelExporter
{
protected $sToken;
protected $aStatistics;
protected $sState;
protected $fStartTime;
protected $oSearch;
protected $aObjectsIDs;
protected $aTableHeaders;
protected $aAuthorizedClasses;
protected $iChunkSize = 1000;
protected $iPosition;
protected $sOutputFilePath;
protected $bAdvancedMode;
public function __construct($sToken = null)
{
$this->aStatistics = array(
'objects_count' => 0,
'total_duration' => 0,
'data_retrieval_duration' => 0,
'excel_build_duration' => 0,
'excel_write_duration' => 0,
'peak_memory_usage' => 0,
);
$this->fStartTime = microtime(true);
$this->oSearch = null;
$this->sState = 'new';
$this->aObjectsIDs = array();
$this->iPosition = 0;
$this->aAuthorizedClasses = null;
$this->aTableHeaders = null;
$this->sOutputFilePath = null;
$this->bAdvancedMode = false;
$this->CheckDataDir();
if ($sToken == null)
{
$this->sToken = $this->GetNewToken();
}
else
{
$this->sToken = $sToken;
$this->ReloadState();
}
}
public function __destruct()
{
if (($this->sState != 'done') && ($this->sState != 'error') && ($this->sToken != null))
{
// Operation in progress, save the state
$this->SaveState();
}
else
{
// Operation completed, cleanup the temp files
@unlink($this->GetStateFile());
@unlink($this->GetDataFile());
}
self::CleanupOldFiles();
}
public function SetChunkSize($iChunkSize)
{
$this->iChunkSize = $iChunkSize;
}
public function SetOutputFilePath($sDestFilePath)
{
$this->sOutputFilePath = $sDestFilePath;
}
public function SetAdvancedMode($bAdvanced)
{
$this->bAdvancedMode = $bAdvanced;
}
public function SaveState()
{
$aState = array(
'state' => $this->sState,
'statistics' => $this->aStatistics,
'filter' => $this->oSearch->serialize(),
'position' => $this->iPosition,
'chunk_size' => $this->iChunkSize,
'object_ids' => $this->aObjectsIDs,
'output_file_path' => $this->sOutputFilePath,
'advanced_mode' => $this->bAdvancedMode,
);
file_put_contents($this->GetStateFile(), json_encode($aState));
return $this->sToken;
}
public function ReloadState()
{
if ($this->sToken == null)
{
throw new Exception('ExcelExporter not initialized with a token, cannot reload state');
}
if (!file_exists($this->GetStateFile()))
{
throw new Exception("ExcelExporter: missing status file '".$this->GetStateFile()."', cannot reload state.");
}
$sJson = file_get_contents($this->GetStateFile());
$aState = json_decode($sJson, true);
if ($aState === null)
{
throw new Exception("ExcelExporter:corrupted status file '".$this->GetStateFile()."', not a JSON, cannot reload state.");
}
$this->sState = $aState['state'];
$this->aStatistics = $aState['statistics'];
$this->oSearch = DBObjectSearch::unserialize($aState['filter']);
$this->iPosition = $aState['position'];
$this->iChunkSize = $aState['chunk_size'];
$this->aObjectsIDs = $aState['object_ids'];
$this->sOutputFilePath = $aState['output_file_path'];
$this->bAdvancedMode = $aState['advanced_mode'];
}
public function SetObjectList($oSearch)
{
$this->oSearch = $oSearch;
}
public function Run()
{
$sCode = 'error';
$iPercentage = 100;
$sMessage = Dict::Format('ExcelExporter:ErrorUnexpected_State', $this->sState);
$fTime = microtime(true);
try
{
switch($this->sState)
{
case 'new':
$oIDSet = new DBObjectSet($this->oSearch);
$oIDSet->OptimizeColumnLoad(array('id'));
$this->aObjectsIDs = array();
while($oObj = $oIDSet->Fetch())
{
$this->aObjectsIDs[] = $oObj->GetKey();
}
$sCode = 'retrieving-data';
$iPercentage = 5;
$sMessage = Dict::S('ExcelExporter:RetrievingData');
$this->iPosition = 0;
$this->aStatistics['objects_count'] = count($this->aObjectsIDs);
$this->aStatistics['data_retrieval_duration'] += microtime(true) - $fTime;
// The first line of the file is the "headers" specifying the label and the type of each column
$this->GetFieldsList($oIDSet, $this->bAdvancedMode);
$sRow = json_encode($this->aTableHeaders);
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
fwrite($hFile, $sRow."\n");
fclose($hFile);
// Next state
$this->sState = 'retrieving-data';
break;
case 'retrieving-data':
$oCurrentSearch = clone $this->oSearch;
$aIDs = array_slice($this->aObjectsIDs, $this->iPosition, $this->iChunkSize);
$oCurrentSearch->AddCondition('id', $aIDs, 'IN');
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
$oSet = new DBObjectSet($oCurrentSearch);
$this->GetFieldsList($oSet, $this->bAdvancedMode);
while($aObjects = $oSet->FetchAssoc())
{
$aRow = array();
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
{
$oObj = $aObjects[$sAlias];
if ($this->bAdvancedMode)
{
$aRow[] = $oObj->GetKey();
}
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$value = $oObj->Get($sAttCodeEx);
if ($value instanceOf ormCaseLog)
{
// 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!
$sExcelVal = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
}
else
{
$sExcelVal = $oAttDef->GetEditValue($value, $oObj);
}
$aRow[] = $sExcelVal;
}
}
$sRow = json_encode($aRow);
fwrite($hFile, $sRow."\n");
}
fclose($hFile);
if (($this->iPosition + $this->iChunkSize) > count($this->aObjectsIDs))
{
// Next state
$this->sState = 'building-excel';
$sCode = 'building-excel';
$iPercentage = 80;
$sMessage = Dict::S('ExcelExporter:BuildingExcelFile');
}
else
{
$sCode = 'retrieving-data';
$this->iPosition += $this->iChunkSize;
$iPercentage = 5 + round(75 * ($this->iPosition / count($this->aObjectsIDs)));
$sMessage = Dict::S('ExcelExporter:RetrievingData');
}
break;
case 'building-excel':
$hFile = @fopen($this->GetDataFile(), 'rb');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for reading.');
}
$sHeaders = fgets($hFile);
$aHeaders = json_decode($sHeaders, true);
$aData = array();
while($sLine = fgets($hFile))
{
$aRow = json_decode($sLine);
$aData[] = $aRow;
}
fclose($hFile);
@unlink($this->GetDataFile());
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$writer->setAuthor(UserRights::GetUserFriendlyName());
$writer->writeSheet($aData,'Sheet1', $aHeaders);
$fExcelTime = microtime(true) - $fStartExcel;
$this->aStatistics['excel_build_duration'] = $fExcelTime;
$fTime = microtime(true);
$writer->writeToFile($this->GetExcelFilePath());
$fExcelSaveTime = microtime(true) - $fTime;
$this->aStatistics['excel_write_duration'] = $fExcelSaveTime;
// Next state
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
case 'done':
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
}
}
catch(Exception $e)
{
$sCode = 'error';
$sMessage = $e->getMessage();
}
$this->aStatistics['total_duration'] += microtime(true) - $fTime;
$peak_memory = memory_get_peak_usage(true);
if ($peak_memory > $this->aStatistics['peak_memory_usage'])
{
$this->aStatistics['peak_memory_usage'] = $peak_memory;
}
return array(
'code' => $sCode,
'message' => $sMessage,
'percentage' => $iPercentage,
);
}
public function GetExcelFilePath()
{
if ($this->sOutputFilePath == null)
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.xlsx';
}
else
{
return $this->sOutputFilePath;
}
}
public static function GetExcelFileFromToken($sToken)
{
return @file_get_contents(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
}
public static function CleanupFromToken($sToken)
{
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.status');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.data');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
}
public function Cleanup()
{
self::CleanupFromToken($this->sToken);
}
/**
* Delete all files in the data/bulk_export directory which are older than 1 day
* unless a different delay is configured.
*/
public static function CleanupOldFiles()
{
$aFiles = glob(APPROOT.'data/bulk_export/*.*');
$iDelay = MetaModel::GetConfig()->Get('xlsx_exporter_cleanup_old_files_delay');
if($iDelay > 0)
{
foreach($aFiles as $sFile)
{
$iModificationTime = filemtime($sFile);
if($iModificationTime < (time() - $iDelay))
{
// Temporary files older than one day are deleted
//echo "Supposed to delete: '".$sFile." (Unix Modification Time: $iModificationTime)'\n";
@unlink($sFile);
}
}
}
}
public function DisplayStatistics(Page $oPage)
{
$aStats = array(
'Number of objects exported' => $this->aStatistics['objects_count'],
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
);
if ($oPage instanceof CLIPage)
{
$oPage->add($this->GetStatistics('text'));
}
else
{
$oPage->add($this->GetStatistics('html'));
}
}
public function GetStatistics($sFormat = 'html')
{
$sStats = '';
$aStats = array(
'Number of objects exported' => $this->aStatistics['objects_count'],
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
);
if ($sFormat == 'text')
{
foreach($aStats as $sLabel => $sValue)
{
$sStats .= "+------------------------------+----------+\n";
$sStats .= sprintf("|%-30s|%10s|\n", $sLabel, $sValue);
}
$sStats .= "+------------------------------+----------+";
}
else
{
$sStats .= '<table><tbody>';
foreach($aStats as $sLabel => $sValue)
{
$sStats .= "<tr><td>$sLabel</td><td>$sValue</td></tr>";
}
$sStats .= '</tbody></table>';
}
return $sStats;
}
public static function HumanDisplay($iSize)
{
$aUnits = array('B','KB','MB','GB','TB','PB');
return @round($iSize/pow(1024,($i=floor(log($iSize,1024)))),2).' '.$aUnits[$i];
}
protected function CheckDataDir()
{
if(!is_dir(APPROOT."data/bulk_export"))
{
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(APPROOT."data/bulk_export"))
{
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
}
}
protected function GetStateFile($sToken = null)
{
if ($sToken == null)
{
$sToken = $this->sToken;
}
return APPROOT."data/bulk_export/$sToken.status";
}
protected function GetDataFile()
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.data';
}
protected function GetNewToken()
{
$iNum = rand();
do
{
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = $this->GetStateFile($sToken);
$hFile = @fopen($sFileName, 'x');
}
while($hFile === false);
fclose($hFile);
return $sToken;
}
protected function GetFieldsList($oSet, $bFieldsAdvanced = false, $bLocalize = true, $aFields = null)
{
$this->aFieldsList = array();
$oAppContext = new ApplicationContext();
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$this->aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$this->aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$aAttribs = array();
$this->aTableHeaders = array();
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
{
$aList[$sAlias] = array();
foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
{
if (is_null($aFields) || (count($aFields) == 0))
{
// Standard list of attributes (no link sets)
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
{
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
if ($bFieldsAdvanced)
{
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
{
$sRemoteClass = $oAttDef->GetTargetClass();
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
{
$this->aFieldsList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
}
}
}
}
else
{
// Any other attribute
$this->aFieldsList[$sAlias][$sAttCodeEx] = $oAttDef;
}
}
}
else
{
// User defined list of attributes
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
{
$this->aFieldsList[$sAlias][$sAttCode] = $oAttDef;
}
}
}
if ($bFieldsAdvanced)
{
$this->aTableHeaders['id'] = '0';
}
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$sLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx, isset($aParams['showMandatoryFields'])) : $sAttCodeEx;
if($oAttDef instanceof AttributeDateTime)
{
$this->aTableHeaders[$sLabel] = 'datetime';
}
else
{
$this->aTableHeaders[$sLabel] = 'string';
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Persistent class InputOutputTask
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class iTopWizardWebPage
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once('itopwebpage.class.inc.php');

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class NiceWebPage
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT."/application/webpage.class.inc.php");
@@ -33,43 +32,19 @@ class NiceWebPage extends WebPage
var $m_aReadyScripts;
var $m_sRootUrl;
public function __construct($s_title, $bPrintable = false)
public function __construct($s_title)
{
parent::__construct($s_title, $bPrintable);
parent::__construct($s_title);
$this->m_aReadyScripts = array();
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-1.12.4.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-1.4.1.min.js'); // Needed since many other plugins still rely on oldies like $.browser
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.11.4.custom.css');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.11.4.custom.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/hovertip.js');
$this->add_linked_script("../js/jquery-1.7.1.min.js");
$this->add_linked_stylesheet('../css/ui-lightness/jquery-ui-1.8.17.custom.css');
$this->add_linked_script('../js/jquery-ui-1.8.17.custom.min.js');
$this->add_linked_script("../js/hovertip.js");
// table sorting
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablesorter.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablesorter.pager.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablehover.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/field_sorter.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/datatable.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.positionBy.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.popupmenu.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/searchformforeignkeys.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/latinise/latinise.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_handler.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_handler_history.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_raw.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_string.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_field.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_numeric.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_enum.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_key.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_hierarchical_key.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_time.js');
$this->add_dict_entries('UI:Combo');
$this->add_ready_script(
$this->add_linked_script("../js/jquery.tablesorter.min.js");
$this->add_linked_script("../js/jquery.tablesorter.pager.js");
$this->add_linked_script("../js/jquery.tablehover.js");
$this->add_ready_script(
<<< EOF
//add new widget called TruncatedList to properly display truncated lists when they are sorted
$.tablesorter.addWidget({
@@ -113,12 +88,11 @@ class NiceWebPage extends WebPage
$("table.listResults").tableHover(); // hover tables
EOF
);
$this->add_saas("css/light-grey.scss");
$this->add_linked_stylesheet("../css/light-grey.css");
$this->m_sRootUrl = $this->GetAbsoluteUrlAppRoot();
$sAbsURLAppRoot = addslashes($this->m_sRootUrl);
$sAbsURLModulesRoot = addslashes($this->GetAbsoluteUrlModulesRoot());
$sEnvironment = addslashes(utils::GetCurrentEnvironment());
$sAppContext = addslashes($this->GetApplicationContext());
@@ -133,23 +107,6 @@ function GetAbsoluteUrlModulesRoot()
{
return '$sAbsURLModulesRoot';
}
function GetAbsoluteUrlModulePage(sModule, sPage, aArguments)
{
// aArguments is optional, it default to an empty hash
aArguments = typeof aArguments !== 'undefined' ? aArguments : {};
var sUrl = '$sAbsURLAppRoot'+'pages/exec.php?exec_module='+sModule+'&exec_page='+sPage+'&exec_env='+'$sEnvironment';
for (var sArgName in aArguments)
{
if (aArguments.hasOwnProperty(sArgName))
{
sUrl = sUrl + '&'+sArgName+'='+aArguments[sArgname];
}
}
return sUrl;
}
function AddAppContext(sURL)
{
var sContext = '$sAppContext';
@@ -238,7 +195,7 @@ EOF
*/
public function output()
{
//$this->set_base($this->m_sRootUrl.'pages/');
$this->set_base($this->m_sRootUrl.'pages/');
if (count($this->m_aReadyScripts)>0)
{
$this->add_script("\$(document).ready(function() {\n".implode("\n", $this->m_aReadyScripts)."\n});");

View File

@@ -1,194 +0,0 @@
<?php
require_once(APPROOT.'lib/tcpdf/tcpdf.php');
/**
* Custom class derived from TCPDF for providing custom headers and footers
* @author denis
*
*/
class iTopPDF extends TCPDF
{
protected $sDocumentTitle;
public function SetDocumentTitle($sDocumentTitle)
{
$this->sDocumentTitle = $sDocumentTitle;
}
/**
* Builds the custom header. Called for each new page.
* @see TCPDF::Header()
*/
public function Header()
{
// Title
// Set font
$this->SetFont('dejavusans', 'B', 10);
$iPageNumberWidth = 25;
$aMargins = $this->getMargins();
// Display the title (centered)
$this->SetXY($aMargins['left'] + $iPageNumberWidth, 0);
$this->MultiCell($this->getPageWidth() - $aMargins['left'] - $aMargins['right'] - 2*$iPageNumberWidth, 15, $this->sDocumentTitle, 0, 'C', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
$this->SetFont('dejavusans', '', 10);
// Display the page number (right aligned)
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
$this->MultiCell($iPageNumberWidth, 15, 'Page '.$this->page, 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
// Branding logo
$sBrandingIcon = APPROOT.'images/itop-logo.png';
if (file_exists(MODULESROOT.'branding/main-logo.png'))
{
$sBrandingIcon = MODULESROOT.'branding/main-logo.png';
}
$this->Image($sBrandingIcon, $aMargins['left'], 5, 0, 10);
}
// Page footer
public function Footer()
{
// No footer
}
}
/**
* Special class of WebPage for printing into a PDF document
*/
class PDFPage extends WebPage
{
/**
* Instance of the TCPDF object for creating the PDF
* @var TCPDF
*/
protected $oPdf;
public function __construct($s_title, $sPageFormat = 'A4', $sPageOrientation = 'L')
{
parent::__construct($s_title);
define(K_PATH_FONTS, APPROOT.'lib/tcpdf/fonts');
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, 'UTF-8', false);
// set document information
$this->oPdf->SetCreator(PDF_CREATOR);
$this->oPdf->SetAuthor('iTop');
$this->oPdf->SetTitle($s_title);
$this->oPdf->SetDocumentTitle($s_title);
$this->oPdf->setFontSubsetting(true);
// Set font
// dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
$this->oPdf->SetFont('dejavusans', '', 10, '', true);
// set auto page breaks
$this->oPdf->SetAutoPageBreak(true, 15); // 15 mm break margin at the bottom
$this->oPdf->SetTopMargin(15);
// Add a page, we're ready to start
$this->oPdf->AddPage();
$this->SetContentDisposition('inline', $s_title.'.pdf');
$this->SetDefaultStyle();
}
/**
* Sets a default style (suitable for printing) to be included each time $this->oPdf->writeHTML() is called
*/
protected function SetDefaultStyle()
{
$this->add_style(
<<<EOF
table {
padding: 2pt;
}
table.listResults td {
border: 0.5pt solid #000 ;
}
table.listResults th {
background-color: #eee;
border: 0.5pt solid #000 ;
}
a {
text-decoration: none;
color: #000;
}
table.section td {
vertical-align: middle;
font-size: 10pt;
background-color:#eee;
}
td.icon {
width: 30px;
}
EOF
);
}
/**
* Get access to the underlying TCPDF object
* @return TCPDF
*/
public function get_tcpdf()
{
$this->flush();
return $this->oPdf;
}
/**
* Writes the currently buffered HTML content into the PDF. This can be useful:
* - to sync the flow in case you want to access the underlying TCPDF object for some specific/graphic output
* - to process the HTML by smaller chunks instead of processing the whole page at once for performance reasons
*/
public function flush()
{
if (strlen($this->s_content) > 0)
{
$sHtml = '';
if (count($this->a_styles) > 0)
{
$sHtml .= "<style>\n".implode("\n", $this->a_styles)."\n</style>\n";
}
$sHtml .= $this->s_content;
$this->oPdf->writeHTML($sHtml); // The style(s) must be supplied each time we call writeHtml
$this->s_content = '';
}
}
/**
* Whether or not the page is a PDF page
* @return boolean
*/
public function is_pdf()
{
return true;
}
/**
* Generates the PDF document and returns the PDF content as a string
* @return string
* @see WebPage::output()
*/
public function output()
{
$this->add_header('Content-type: application/x-pdf');
if (!empty($this->sContentDisposition))
{
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
}
foreach($this->a_headers as $s_header)
{
header($s_header);
}
$this->flush();
echo $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
public function get_pdf()
{
$this->flush();
return $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
}

View File

@@ -1,66 +0,0 @@
<?php
class PortalDispatcher
{
protected $sPortalid;
protected $aData;
public function __construct($sPortalId)
{
$this->sPortalid = $sPortalId;
$this->aData = PortalDispatcherData::GetData($sPortalId);
}
public function IsUserAllowed()
{
$bRet = true;
$aProfiles = UserRights::ListProfiles();
foreach($this->aData['deny'] as $sDeniedProfile)
{
// If one denied profile is present, it's enough => return false
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)
{
$bRet = false;
}
foreach($this->aData['allow'] as $sAllowProfile)
{
// If one "allow" profile is present, it's enough => return true
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))
{
$sRet = $aOverloads[$this->sPortalid];
}
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,26 +1,26 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class PortalWebPage
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT."/application/nicewebpage.class.inc.php");
@@ -49,12 +49,9 @@ class PortalWebPage extends NiceWebPage
*/
protected $m_sWelcomeMsg;
protected $m_aMenuButtons;
protected $m_oCtx;
public function __construct($sTitle, $sAlternateStyleSheet = '')
{
$this->m_oCtx = new ContextTag('GUI:Portal');
$this->m_sWelcomeMsg = '';
$this->m_aMenuButtons = array();
parent::__construct($sTitle);
@@ -62,12 +59,10 @@ class PortalWebPage extends NiceWebPage
$this->add_header("Cache-control: no-cache");
$this->add_linked_stylesheet("../css/jquery.treeview.css");
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
$sAbsURLAppRoot = addslashes(utils::GetAbsoluteUrlAppRoot()); // Pass it to Javascript scripts
$sAbsURLModulesRoot = addslashes(utils::GetAbsoluteUrlModulesRoot()); // Pass it to Javascript scripts
$oAppContext = new ApplicationContext();
$sAppContext = addslashes($oAppContext->GetForLink());
$this->add_dict_entry('UI:FillAllMandatoryFields');
if ($sAlternateStyleSheet != '')
{
$this->add_linked_stylesheet("../portal/$sAlternateStyleSheet/portal.css");
@@ -91,94 +86,6 @@ class PortalWebPage extends NiceWebPage
$this->add_linked_script("../js/forms-json-utils.js");
$this->add_linked_script("../js/swfobject.js");
$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
$this->add_linked_script('../js/jquery.multiselect.js');
$this->add_linked_script("../js/ajaxfileupload.js");
$this->add_linked_script("../js/ckeditor/ckeditor.js");
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
$this->add_linked_script("../js/jquery-ui-timepicker-addon.js");
$this->add_linked_script("../js/jquery-ui-timepicker-addon-i18n.min.js");
$this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css");
$sJSDisconnectedMessage = json_encode(Dict::S('UI:DisconnectedDlgMessage'));
$sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle'));
$sJSLoginAgain = json_encode(Dict::S('UI:LoginAgain'));
$sJSStayOnThePage = json_encode(Dict::S('UI:StayOnThePage'));
$aDaysMin = array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min'));
$aMonthsShort = array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short'));
$sTimeFormat = AttributeDateTime::GetFormat()->ToTimeFormat();
$oTimeFormat = new DateTimeFormat($sTimeFormat);
$sJSLangShort = json_encode(strtolower(substr(Dict::GetUserLanguage(), 0, 2)));
// Date picker options
$aPickerOptions = array(
'showOn' => 'button',
'buttonImage' => '../images/calendar.png',
'buttonImageOnly' => true,
'dateFormat' => AttributeDate::GetFormat()->ToDatePicker(),
'constrainInput' => false,
'changeMonth' => true,
'changeYear' => true,
'dayNamesMin' => $aDaysMin,
'monthNamesShort' => $aMonthsShort,
'firstDay' => (int) Dict::S('Calendar-FirstDayOfWeek'),
);
$sJSDatePickerOptions = json_encode($aPickerOptions);
// Time picker additional options
$aPickerOptions['showOn'] = '';
$aPickerOptions['buttonImage'] = null;
$aPickerOptions['timeFormat'] = $oTimeFormat->ToDatePicker();
$aPickerOptions['controlType'] = 'select';
$aPickerOptions['closeText'] = Dict::S('UI:Button:Ok');
$sJSDateTimePickerOptions = json_encode($aPickerOptions);
if ($sJSLangShort != '"en"')
{
// More options that cannot be passed via json_encode since they must be evaluated client-side
$aMoreJSOptions = ",
'timeText': $.timepicker.regional[$sJSLangShort].timeText,
'hourText': $.timepicker.regional[$sJSLangShort].hourText,
'minuteText': $.timepicker.regional[$sJSLangShort].minuteText,
'secondText': $.timepicker.regional[$sJSLangShort].secondText,
'currentText': $.timepicker.regional[$sJSLangShort].currentText
}";
$sJSDateTimePickerOptions = substr($sJSDateTimePickerOptions, 0, -1).$aMoreJSOptions;
}
$this->add_script(
<<< EOF
function PrepareWidgets()
{
// note: each action implemented here must be idempotent,
// because this helper function might be called several times on a given page
$(".date-pick").datepicker($sJSDatePickerOptions);
// Hack for the date and time picker addon issue on Chrome (see #1305)
// The workaround is to instantiate the widget on demand
// It relies on the same markup, thus reverting to the original implementation should be straightforward
$(".datetime-pick:not(.is-widget-ready)").each(function(){
var oInput = this;
$(oInput).addClass('is-widget-ready');
$('<img class="datetime-pick-button" src="../images/calendar.png">')
.insertAfter($(this))
.on('click', function(){
$(oInput)
.datetimepicker($sJSDateTimePickerOptions)
.datetimepicker('show')
.datetimepicker('option', 'onClose', function(dateText,inst){
$(oInput).datetimepicker('destroy');
})
.on('click keypress', function(){
$(oInput).datetimepicker('hide');
});
});
});
}
EOF
);
$this->add_ready_script(
<<<EOF
try
@@ -219,29 +126,17 @@ try
}
});
PrepareWidgets();
$(".date-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd',
constrainInput: false,
changeMonth: true,
changeYear: true
});
//$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry,.caselog_entry_html').toggle(); });
$(document).ajaxSend(function(event, jqxhr, options) {
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');
});
$(document).ajaxError(function(event, jqxhr, options) {
if (jqxhr.status == 401)
{
$('<div>'+$sJSDisconnectedMessage+'</div>').dialog({
modal:true,
title: $sJSTitle,
close: function() { $(this).remove(); },
minWidth: 400,
buttons: [
{ text: $sJSLoginAgain, click: function() { window.location.href= GetAbsoluteUrlAppRoot()+'pages/UI.php' } },
{ text: $sJSStayOnThePage, click: function() { $(this).dialog('close'); } }
]
});
}
});
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry').toggle(); });
}
catch(err)
{
@@ -312,7 +207,7 @@ EOF
{
var form = $('FORM');
form.unbind('submit'); // De-activate validation
window.location.href = window.location.href.replace(/operation=[^&]*&?/, '');
window.location.href = '?operation=';
return false;
}
@@ -321,37 +216,10 @@ EOF
var next_step = $('input[id=next_step]');
next_step.val(sStep);
}
// For disabling the CKEditor at init time when the corresponding textarea is disabled !
CKEDITOR.plugins.add( 'disabler',
{
init : function( editor )
{
editor.on( 'instanceReady', function(e)
{
e.removeListener();
$('#'+ editor.name).trigger('update');
});
}
});
EOF
);
// For Wizard helper to process the ajax replies
$this->add('<div id="ajax_content"></div>');
// Customize the logo (unless a customer CSS has been defined)
if ($sAlternateStyleSheet == '')
{
if (file_exists(MODULESROOT.'branding/portal-logo.png'))
{
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/portal-logo.png';
$this->add_style("div#portal #logo {background: url(\"$sDisplayIcon\") no-repeat scroll 0 0 transparent;}");
}
}
}
}
public function SetCurrentTab($sTabLabel = '')
{
@@ -382,31 +250,16 @@ EOF
public function output()
{
$sApplicationBanner = '';
if (!MetaModel::DBHasAccess(ACCESS_USER_WRITE))
{
$sReadOnly = Dict::S('UI:AccessRO-Users');
$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
$sApplicationBanner .= '<div class="app-message">';
$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
$sApplicationBanner .= '&nbsp;<b>'.$sReadOnly.'</b>';
if (strlen($sAdminMessage) > 0)
{
$sApplicationBanner .= '&nbsp;: '.$sAdminMessage.'';
}
$sApplicationBanner .= '</div>';
}
$sMenu = '';
if ($this->m_bEnableDisconnectButton)
{
$this->AddMenuButton('logoff', 'Portal:Disconnect', utils::GetAbsoluteUrlAppRoot().'pages/logoff.php?operation=do_logoff'); // This menu is always present and is the last one
$this->AddMenuButton('logoff', 'Portal:Disconnect', utils::GetAbsoluteUrlAppRoot().'pages/logoff.php'); // This menu is always present and is the last one
}
foreach($this->m_aMenuButtons as $aMenuItem)
{
$sMenu .= "<a class=\"button\" id=\"{$aMenuItem['id']}\" href=\"{$aMenuItem['hyperlink']}\"><span>".Dict::S($aMenuItem['label'])."</span></a>";
}
$this->s_content = '<div id="portal"><div id="welcome">'.$this->m_sWelcomeMsg.'</div><div id="banner"><div id="logo"></div><div id="menu">'.$sMenu.'</div></div>'.$sApplicationBanner.'<div id="content">'.$this->s_content.'</div></div>';
$this->s_content = '<div id="portal"><div id="welcome">'.$this->m_sWelcomeMsg.'</div><div id="banner"><div id="logo"></div><div id="menu">'.$sMenu.'</div></div><div id="content">'.$this->s_content.'</div></div>';
parent::output();
}
@@ -426,9 +279,13 @@ EOF
{
// Home-made and very limited display of an object set
$sUniqueId = $sClass.$this->GetUniqueId();
$this->add("<div id=\"$sUniqueId\">\n"); // The id here MUST be the same as currentId, otherwise the pagination will be broken
cmdbAbstractObject::DisplaySet($this, $oSet, array('currentId' => $sUniqueId, 'menu' => false, 'toolkit_menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList)));
//
//$oSet->Seek(0);// juste pour que le warning soit moins crado
//$oSet->Fetch();// juste pour que le warning soit moins crado
//
$this->add("<div id=\"listOf$sClass\">\n");
cmdbAbstractObject::DisplaySet($this, $oSet, array('currentId' => $this->GetUniqueId()."$sClass", 'menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList)));
$this->add("</div>\n");
}
else
@@ -524,7 +381,7 @@ EOF
}
$oObjSearch->AddCondition_ReferencedBy($oLinkSet->GetFilter(), $sRemoteAttCode);
$aExtraParams = array('menu' => false, 'toolkit_menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList));
$aExtraParams = array('menu' => false, 'zlist' => false, 'extra_fields' => implode(',', $aZList));
$oBlock = new DisplayBlock($oObjSearch, 'list', false);
$oBlock->Display($this, 1, $aExtraParams);
}
@@ -534,7 +391,8 @@ EOF
}
}
protected function DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix, $sFieldName = null, $aFilterParams = array())
protected function DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix, $sFieldName = null)
{
if (is_null($sFieldName))
{
@@ -565,7 +423,7 @@ EOF
{
throw new Exception("Attribute specification '$sAttSpec', '$sAttCode' should be either a link set or an external key");
}
$this->DisplaySearchField($sTargetClass, $sSubSpec, $aExtraParams, $sPrefix, $sFieldName, $aFilterParams);
$this->DisplaySearchField($sTargetClass, $sSubSpec, $aExtraParams, $sPrefix, $sFieldName);
}
else
{
@@ -579,24 +437,7 @@ EOF
if ($oAttDef->IsExternalKey())
{
$sTargetClass = $oAttDef->GetTargetClass();
$sFilterDefName = 'PORTAL_TICKETS_SEARCH_FILTER_'.$sAttSpec;
if (defined($sFilterDefName))
{
try
{
$oFitlerWithParams = DBObjectSearch::FromOQL(constant($sFilterDefName));
$sFilterOQL = $oFitlerWithParams->ToOQL(true, $aFilterParams);
$oAllowedValues = new DBObjectSet(DBObjectSearch::FromOQL($sFilterOQL), array(), $aFilterParams);
}
catch(OQLException $e)
{
throw new Exception("Incorrect filter '$sFilterDefName' for attribute '$sAttcode': ".$e->getMessage());
}
}
else
{
$oAllowedValues = new DBObjectSet(new DBObjectSearch($sTargetClass));
}
$oAllowedValues = new DBObjectSet(new DBObjectSearch($sTargetClass));
$iFieldSize = $oAttDef->GetMaxSize();
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
@@ -616,8 +457,7 @@ EOF
if (is_null($aAllowedValues))
{
// Any value is possible, display an input box
$sSanitizedValue = htmlentities($sFilterValue, ENT_QUOTES, 'UTF-8');
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label>&nbsp;<input class=\"textSearch\" name=\"$sPrefix$sFieldName\" value=\"$sSanitizedValue\"/>\n");
$this->add("<label>".MetaModel::GetFilterLabel($sClass, $sAttSpec).":</label>&nbsp;<input class=\"textSearch\" name=\"$sPrefix$sFieldName\" value=\"$sFilterValue\"/>\n");
}
else
{
@@ -654,30 +494,9 @@ EOF
}
}
/**
* Get The organization of the current user (i.e. the organization of its contact)
* @throws Exception
*/
function GetUserOrg()
{
$oOrg = null;
$iContactId = UserRights::GetContactId();
$oContact = MetaModel::GetObject('Contact', $iContactId, false); // false => Can fail
if (is_object($oContact))
{
$oOrg = MetaModel::GetObject('Organization', $oContact->Get('org_id'), false); // false => can fail
}
else
{
throw new Exception(Dict::S('Portal:ErrorNoContactForThisUser'));
}
return $oOrg;
}
public function DisplaySearchForm($sClass, $aAttList, $aExtraParams, $sPrefix, $bClosed = true)
{
$oUserOrg = $this->GetUserOrg();
$aFilterParams = array('org_id' => $oUserOrg->GetKey(), 'contact_id' => UserRights::GetContactId());
$sCSSClass = ($bClosed) ? 'DrawerClosed' : '';
$this->add("<div id=\"ds_$sPrefix\" class=\"SearchDrawer $sCSSClass\">\n");
$this->add_ready_script(
@@ -694,17 +513,13 @@ EOF
foreach($aAttList as $sAttSpec)
{
//$oAppContext->Reset($sAttSpec); // Make sure the same parameter will not be passed twice
$this->DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix, null, $aFilterParams);
$this->DisplaySearchField($sClass, $sAttSpec, $aExtraParams, $sPrefix);
}
$this->add("</p>\n");
$this->add("<p align=\"right\"><input type=\"submit\" value=\"".Dict::S('UI:Button:Search')."\"></p>\n");
foreach($aExtraParams as $sName => $sValue)
{
// Note: use DumpHiddenParams() to transmit arrays as hidden params
if (is_scalar($sValue))
{
$this->add("<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n");
}
$this->add("<input type=\"hidden\" name=\"$sName\" value=\"$sValue\" />\n");
}
// $this->add($oAppContext->GetForForm());
$this->add("</form>\n");
@@ -783,7 +598,7 @@ EOF
{
$sFieldName = str_replace('->', PARAM_ARROW_SEP, $sAttSpec);
$value = utils::ReadPostedParam($sPrefix.$sFieldName, null, 'raw_data');
if (!is_null($value) && (is_array($value) ? count($value)>0 : strlen($value)>0))
if (!is_null($value) && strlen($value) > 0)
{
$oFilter->AddConditionAdvanced($sAttSpec, $value);
$iCountParams++;
@@ -816,7 +631,7 @@ EOF
$sClass = get_class($oObj);
$sStimulus = trim(utils::ReadPostedParam('apply_stimulus', ''));
$aExpectedAttributes = array();
$sTargetState = '';
if (!empty($sStimulus))
{
// Compute the target state
@@ -826,10 +641,10 @@ EOF
{
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
}
$aExpectedAttributes = $oObj->GetTransitionAttributes($sStimulus /*, current state*/);
}
$sTargetState = $aTransitions[$sStimulus]['target_state'];
}
$oObj->UpdateObjectFromPostedForm('' /* form prefix */, $aAttList, $aExpectedAttributes);
$oObj->UpdateObjectFromPostedForm('' /* form prefix */, $aAttList, $sTargetState);
// Optional: apply a stimulus
//
@@ -841,35 +656,26 @@ EOF
}
}
if ($oObj->IsModified())
// Record the change
//
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
$sUserString = CMDBChange::GetCurrentUserName();
$oMyChange->Set("userinfo", $sUserString);
$iChangeId = $oMyChange->DBInsert();
$oObj->DBUpdateTracked($oMyChange);
// Trigger ?
//
$aClasses = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL);
$sClassList = implode(", ", CMDBSource::Quote($aClasses));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnPortalUpdate AS t WHERE t.target_class IN ($sClassList)"));
while ($oTrigger = $oSet->Fetch())
{
// Record the change
//
$oObj->DBUpdate();
// Trigger ?
//
$aClasses = MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL);
$sClassList = implode(", ", CMDBSource::Quote($aClasses));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnPortalUpdate AS t WHERE t.target_class IN ($sClassList)"));
while ($oTrigger = $oSet->Fetch())
{
$oTrigger->DoActivate($oObj->ToArgs('this'));
}
$this->p("<h1>".Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())."</h1>\n");
}
$bLockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
if ($bLockEnabled)
{
// Release the concurrent lock, if any
$sOwnershipToken = utils::ReadPostedParam('ownership_token', null, false, 'raw_data');
if ($sOwnershipToken !== null)
{
// We're done, let's release the lock
iTopOwnershipLock::ReleaseLock(get_class($oObj), $oObj->GetKey(), $sOwnershipToken);
}
$oTrigger->DoActivate($oObj->ToArgs('this'));
}
$this->p("<h1>".Dict::Format('UI:Class_Object_Updated', MetaModel::GetName(get_class($oObj)), $oObj->GetName())."</h1>\n");
}
/**
@@ -911,7 +717,7 @@ EOF
}
}
$oObj = MetaModel::GetObject($sClass, $iId, false);
$oObj = MetaModel::GetObject($sClass, $iId, false);
if (!is_object($oObj))
{
throw new Exception("Could not find the object $sClass/$iId");
@@ -926,7 +732,7 @@ EOF
$this->m_sWizardId = $sId;
// multipart... needed for file upload
$this->add("<form id=\"{$this->m_sWizardId}\" method=\"$sMethod\" enctype=\"multipart/form-data\" onsubmit=\"window.bInSubmit = true;\">\n");
$this->add("<form id=\"{$this->m_sWizardId}\" method=\"$sMethod\" enctype=\"multipart/form-data\">\n");
$aPreviousSteps = $this->GetWizardStepHistory();
if (utils::ReadParam('step_back', 0) == 1)
@@ -941,7 +747,7 @@ EOF
}
$sStepHistory = implode(',', $aPreviousSteps);
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"".htmlentities($sStepHistory, ENT_QUOTES, 'UTF-8')."\">");
$this->add("<input type=\"hidden\" id=\"step_history\" name=\"step_history\" value=\"$sStepHistory\">");
if (!is_null($sNextStep))
{
@@ -952,7 +758,7 @@ EOF
$sTransactionId = utils::GetNewTransactionId();
$this->SetTransactionId($sTransactionId);
$this->add("<input type=\"hidden\" id=\"transaction_id\" name=\"transaction_id\" value=\"$sTransactionId\">\n");
$this->add_ready_script("$(window).on('unload', function() { OnUnload('$sTransactionId') } );\n");
$this->add_ready_script("$(window).unload(function() { OnUnload('$sTransactionId') } );\n");
}
public function WizardFormButtons($iButtonFlags)
@@ -964,10 +770,7 @@ EOF
}
if ($iButtonFlags & BUTTON_BACK)
{
if (utils::ReadParam('step_back', 1) != 1)
{
$aButtons[] = "<input id=\"btn_back\" type=\"submit\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"GoBack('{$this->m_sWizardId}');\">";
}
$aButtons[] = "<input id=\"btn_back\" type=\"submit\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"GoBack('{$this->m_sWizardId}');\">";
}
if ($iButtonFlags & BUTTON_NEXT)
{

View File

@@ -1,29 +1,28 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Persistent class Event and derived
* Application internal events
* There is also a file log
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
abstract class Query extends cmdbAbstractObject
@@ -32,7 +31,7 @@ abstract class Query extends cmdbAbstractObject
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui,application,grant_by_profile",
"category" => "core/cmdb,view_in_gui,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -47,15 +46,14 @@ abstract class Query extends cmdbAbstractObject
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("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("fields", array("allowed_values"=>null, "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'fields')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default 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
}
}
@@ -65,7 +63,7 @@ class QueryOQL extends Query
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui,application,grant_by_profile",
"category" => "core/cmdb,view_in_gui,application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -83,58 +81,26 @@ class QueryOQL extends Query
MetaModel::Init_SetZListItems('details', array('name', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'fields', 'oql')); // Criteria of the std 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
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
if (!$bEditMode)
{
$sFields = trim($this->Get('fields'));
$bExportV1Recommended = ($sFields == '');
if ($bExportV1Recommended)
{
$oFieldAttDef = MetaModel::GetAttributeDef('QueryOQL', 'fields');
$oPage->add('<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:Query:UrlV1', $oFieldAttDef->GetLabel()).'</div></div>');
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
}
else
{
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&date_format='.urlencode((string)AttributeDateTime::GetFormat()).'&query='.$this->GetKey();
}
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
$sOql = $this->Get('oql');
$sMessage = null;
try
$oSearch = DBObjectSearch::FromOQL($sOql);
$aParameters = $oSearch->GetQueryParams();
foreach($aParameters as $sParam => $val)
{
$oSearch = DBObjectSearch::FromOQL($sOql);
$aParameters = $oSearch->GetQueryParams();
foreach($aParameters as $sParam => $val)
{
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
}
$oPage->p(Dict::S('UI:Query:UrlForExcel').':<br/><textarea cols="80" rows="3" READONLY>'.$sUrl.'</textarea>');
if (count($aParameters) == 0)
{
$oBlock = new DisplayBlock($oSearch, 'list');
$aExtraParams = array(
//'menu' => $sShowMenu,
'table_id' => 'query_preview_'.$this->getKey(),
);
$sBlockId = 'block_query_preview_'.$this->GetKey(); // make a unique id (edition occuring in the same DOM)
$oBlock->Display($oPage, $sBlockId, $aExtraParams);
}
}
catch (OQLException $e)
{
$sMessage = '<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:RunQuery:Error', $e->getHtmlDesc()).'</div></div>';
$oPage->p($sMessage);
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
}
$oPage->p(Dict::S('UI:Query:UrlForExcel').':<br/><textarea cols="80" rows="3" READONLY>'.$sUrl.'</textarea>');
}
return $aFieldsMap;
}
}

View File

@@ -1,338 +0,0 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Persistent class Shortcut and derived
* Shortcuts of any kind
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class Shortcut extends DBObject implements iDisplay
{
public static function Init()
{
$aParams = array
(
"category" => "gui,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_shortcut",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"display_template" => "",
);
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())));
// 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
// 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
}
abstract public function RenderContent(WebPage $oPage, $aExtraParams = array());
protected function OnInsert()
{
$this->Set('user_id', UserRights::GetUserId());
}
public function StartRenameDialog($oPage)
{
$oPage->add('<div id="shortcut_rename_dlg">');
$oForm = new DesignerForm();
$sDefault = $this->Get('name');
$oField = new DesignerTextField('name', Dict::S('Class:Shortcut/Attribute:name'), $sDefault);
$oField->SetMandatory(true);
$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
function ShortcutRenameOK()
{
var oForm = $(this).find('form');
var sFormId = oForm.attr('id');
var oParams = null;
var aErrors = ValidateForm(sFormId, false);
if (aErrors.length == 0)
{
oParams = ReadFormParams(sFormId);
}
oParams.operation = 'shortcut_rename_go';
oParams.id = $iShortcut;
var me = $(this);
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', oParams, function(data) {
me.dialog( "close" );
me.remove();
$('body').append(data);
});
}
$('#shortcut_rename_dlg form').bind('submit', function() { return false; });
$('#shortcut_rename_dlg').dialog({
width: 400,
modal: true,
title: '$sDialogTitle',
buttons: [
{ text: "$sOkButtonLabel", click: ShortcutRenameOK},
{ text: "$sCancelButtonLabel", click: function() {
$(this).dialog( "close" ); $(this).remove();
} },
],
close: function() { $(this).remove(); }
});
EOF
);
}
// Minimual implementation of iDisplay: to make the shortcut be listable
//
public static function MapContextParam($sContextParam)
{
return (($sContextParam == 'menu') ? null : $sContextParam);
}
public function GetHilightClass()
{
return HILIGHT_CLASS_NONE;
}
public static function GetUIPage()
{
return '';
}
function DisplayDetails(WebPage $oPage, $bEditMode = false)
{
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
return array();
}
// End of the minimal implementation of iDisplay
}
class ShortcutOQL extends Shortcut
{
public static function Init()
{
$aParams = array
(
"category" => "gui,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_shortcut_oql",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
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())));
// 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
// 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
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
$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;
default:
case 'none':
}
$bSearchPane = true;
$bSearchOpen = true;
try
{
OQLMenuNode::RenderOQLSearch($this->Get('oql'), $this->Get('name'), 'shortcut_'.$this->GetKey(), $bSearchPane, $bSearchOpen, $oPage, $aExtraParams, true);
}
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)
{
$aTableSettings = json_decode($sTableSettings, true);
$oFilter = DBObjectSearch::FromOQL($this->Get('oql'));
$oCustomSettings = new DataTableSettings($oFilter->GetSelectedClasses());
$oCustomSettings->iDefaultPageSize = $aTableSettings['iPageSize'];
$oCustomSettings->aColumns = $aTableSettings['oColumns'];
$oCustomSettings->Save('shortcut_'.$this->GetKey());
}
public static function GetCreationForm($sOQL = null, $sTableSettings = null)
{
$oForm = new DesignerForm();
// Find a unique default name
// -> The class of the query + an index if necessary
if ($sOQL == null)
{
$sDefault = '';
}
else
{
$oBMSearch = new DBObjectSearch('Shortcut');
$oBMSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oBMSet = new DBObjectSet($oBMSearch);
$aNames = $oBMSet->GetColumnAsArray('name');
$oSearch = DBObjectSearch::FromOQL($sOQL);
$sDefault = utils::MakeUniqueName($oSearch->GetClass(), $aNames);
}
$oField = new DesignerTextField('name', Dict::S('Class:Shortcut/Attribute:name'), $sDefault);
$oField->SetMandatory(true);
$oForm->AddField($oField);
/*
$oField = new DesignerComboField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), 'none');
$oAttDef = MetaModel::GetAttributeDef(__class__, 'auto_reload');
$oField->SetAllowedValues($oAttDef->GetAllowedValues());
$oField->SetMandatory(true);
$oForm->AddField($oField);
*/
$oField = new DesignerBooleanField('auto_reload', Dict::S('Class:ShortcutOQL/Attribute:auto_reload'), false);
$oForm->AddField($oField);
$oField = new DesignerIntegerField('auto_reload_sec', Dict::S('Class:ShortcutOQL/Attribute:auto_reload_sec'), MetaModel::GetConfig()->GetStandardReloadInterval());
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oField->SetMandatory(false);
$oForm->AddField($oField);
$oField = new DesignerHiddenField('oql', '', $sOQL);
$oForm->AddField($oField);
$oField = new DesignerHiddenField('table_settings', '', $sTableSettings);
$oForm->AddField($oField);
return $oForm;
}
public static function GetCreationDlgFromOQL($oPage, $sOQL, $sTableSettings)
{
$oPage->add('<div id="shortcut_creation_dlg">');
$oForm = self::GetCreationForm($sOQL, $sTableSettings);
$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(
<<<EOF
// Note: the title gets deleted by the validation mechanism
$("#attr_auto_reload_sec").tooltip({items: 'input', content: '$sRateTitle'});
$("#attr_auto_reload_sec").prop('disabled', !$('#attr_auto_reload').is(':checked'));
$('#attr_auto_reload').change( function(ev) {
$("#attr_auto_reload_sec").prop('disabled', !$(this).is(':checked'));
} );
function ShortcutCreationOK()
{
var oForm = $('#shortcut_creation_dlg form');
var sFormId = oForm.attr('id');
var oParams = null;
var aErrors = ValidateForm(sFormId, false);
if (aErrors.length == 0)
{
oParams = ReadFormParams(sFormId);
}
oParams.operation = 'shortcut_list_create';
var me = $('#shortcut_creation_dlg');
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?$sContext', oParams, function(data) {
me.dialog( "close" );
me.remove();
$('body').append(data);
});
}
$('#shortcut_creation_dlg form').bind('submit', function() { ShortcutCreationOK(); return false; });
$('#shortcut_creation_dlg').dialog({
width: 400,
modal: true,
title: '$sDialogTitle',
buttons: [
{ text: "$sOkButtonLabel", click: ShortcutCreationOK },
{ text: "$sCancelButtonLabel", click: function() {
$(this).dialog( "close" ); $(this).remove();
} },
],
close: function() { $(this).remove(); }
});
EOF
);
}
}
?>

View File

@@ -0,0 +1,531 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* SqlBlock - display tables or charts, given an SQL query - use cautiously!
*
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/pages/php-ofc-library/open-flash-chart.php');
/**
* Helper class to design optimized dashboards, based on an SQL query
*
*/
class SqlBlock
{
protected $m_sQuery;
protected $m_aColumns;
protected $m_sTitle;
protected $m_sType;
protected $m_aParams;
public function __construct($sQuery, $aColumns, $sTitle, $sType, $aParams = array())
{
$this->m_sQuery = $sQuery;
$this->m_aColumns = $aColumns;
$this->m_sTitle = $sTitle;
$this->m_sType = $sType;
$this->m_aParams = $aParams;
}
/**
* Constructs a SqlBlock object from an XML template
/*
*
* <sqlblock>
* <sql>SELECT date_format(start_date, '%d') AS Date, count(*) AS Count FROM ticket WHERE DATE_SUB(NOW(), INTERVAL 15 DAY) &lt; start_date AND finalclass = 'UserIssue' GROUP BY date_format(start_date, '%d') AND $CONDITION(param1, ticket.org_id)$</sql>
* <type>table</type>
* <title>UserRequest:Overview-Title</title>
* <parameter>
* <name>param1</name>
* <type>context</type>
* <mapping>org_id</mapping>
* </parameter>
* <column>
* <name>Date</name>
* <label>UserRequest:Overview-Date</label>
* <drilldown></drilldown>
* </column>
* <column>
* <name>Count</name>
* <label>UserRequest:Overview-Count</label>
* <drilldown>SELECT UserIssue WHERE date_format(start_date, '%d') = :Date</drilldown>
* </column>
* </sqlblock>
*
* Tags
* - sql: a (My)SQL query. Do not forget to use html entities (e.g. &lt; for <)
* - type: table (default), bars or pie. If bars or pie is selected only the two first columns are taken into account.
* - title: optional title, typed in clear or given as a dictionnary entry
* - parameter: specifies how to map the context parameters (namely org_id) to a given named parameter in the query.
* The expression $CONDITION(<param_name>, <sql_column_name>) will be automatically replaced by:
* either the string "1" if there is no restriction on the organisation in iTop
* or the string "(<sql_column_name>=<value_of_org_id>)" if there is a limitation to one organizations in iTop
* or the string "(<sql_column_name> IN (<values_of_org_id>))" if there is a limitation to a given set of organizations in iTop
* - column: specification of a column (not displayed if omitted)
* - column / name: name of the column in the SQL query (use aliases)
* - column / label: label, typed in clear or given as a dictionnary entry
* - column / drilldown: NOT IMPLEMENTED YET - OQL with parameters corresponding to column names (in the query)
*
* @param $sTemplate string The XML template
* @return DisplayBlock The DisplayBlock object, or null if the template is invalid
*/
public static function FromTemplate($sTemplate)
{
$oXml = simplexml_load_string('<root>'.$sTemplate.'</root>', 'SimpleXMLElement', LIBXML_NOCDATA);
if (false)
{
// Debug
echo "<pre>\n";
print_r($oXml);
echo "</pre>\n";
}
if (isset($oXml->title))
{
$sTitle = (string)$oXml->title;
}
if (isset($oXml->type))
{
$sType = (string)$oXml->type;
}
else
{
$sType = 'table';
}
if (!isset($oXml->sql))
{
throw new Exception('Missing tag "sql" in sqlblock');
}
$sQuery = (string)$oXml->sql;
$aColumns = array();
if (isset($oXml->column))
{
foreach ($oXml->column AS $oColumnData)
{
if (!isset($oColumnData->name))
{
throw new Exception("Missing tag 'name' in sqlblock/column");
}
$sName = (string) $oColumnData->name;
if (strlen($sName) == 0)
{
throw new Exception("Empty tag 'name' in sqlblock/column");
}
$aColumns[$sName] = array();
if (isset($oColumnData->label))
{
$sLabel = (string)$oColumnData->label;
if (strlen($sLabel) > 0)
{
$aColumns[$sName]['label'] = Dict::S($sLabel);
}
}
if (isset($oColumnData->drilldown))
{
$sDrillDown = (string)$oColumnData->drilldown;
if (strlen($sDrillDown) > 0)
{
$aColumns[$sName]['drilldown'] = $sDrillDown;
}
}
}
}
$aParams = array();
if (isset($oXml->parameter))
{
foreach ($oXml->parameter AS $oParamData)
{
if (!isset($oParamData->name))
{
throw new Exception("Missing tag 'name' for parameter in sqlblock/column");
}
$sName = (string) $oParamData->name;
if (strlen($sName) == 0)
{
throw new Exception("Empty tag 'name' for parameter in sqlblock/column");
}
if (!isset($oParamData->mapping))
{
throw new Exception("Missing tag 'mapping' for parameter in sqlblock/column");
}
$sMapping = (string) $oParamData->mapping;
if (strlen($sMapping) == 0)
{
throw new Exception("Empty tag 'mapping' for parameter in sqlblock/column");
}
if (isset($oParamData->type))
{
$sParamType = $oParamData->type;
}
else
{
$sParamType = 'context';
}
$aParams[$sName] = array('mapping' => $sMapping, 'type' => $sParamType);
}
}
return new SqlBlock($sQuery, $aColumns, $sTitle, $sType, $aParams);
}
/**
* Applies the defined parameters into the SQL query
* @return string the SQL query to execute
*/
public function BuildQuery()
{
$oAppContext = new ApplicationContext();
$sQuery = $this->m_sQuery;
$sQuery = str_replace('$DB_PREFIX$', MetaModel::GetConfig()->GetDBSubname(), $sQuery); // put the tables DB prefix (if any)
foreach($this->m_aParams as $sName => $aParam)
{
if ($aParam['type'] == 'context')
{
$sSearchPattern = '/\$CONDITION\('.$sName.',([^\)]+)\)\$/';
$value = $oAppContext->GetCurrentValue($aParam['mapping']);
if (empty($value))
{
$sSQLExpr = '(1)';
}
else
{
// Special case for managing the hierarchy of organizations
if (($aParam['mapping'] == 'org_id') && ( MetaModel::IsValidClass('Organization')))
{
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
if ($sHierarchicalKeyCode != false)
{
// organizations are in hierarchy... gather all the orgs below the given one...
$sOQL = "SELECT Organization AS node JOIN Organization AS root ON node.$sHierarchicalKeyCode BELOW root.id WHERE root.id = :value";
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('value' => $value));
$aOrgIds = array();
while($oOrg = $oSet->Fetch())
{
$aOrgIds[]= $oOrg->GetKey();
}
$sSQLExpr = '($1 IN('.implode(',', $aOrgIds).'))';
}
else
{
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
}
}
else
{
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
}
}
$sQuery = preg_replace($sSearchPattern, $sSQLExpr, $sQuery);
}
}
return $sQuery;
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
if (empty($aExtraParams['currentId']))
{
$sId = 'sqlblock_'.$oPage->GetUniqueId(); // Works only if the page is not an Ajax one !
}
else
{
$sId = $aExtraParams['currentId'];
}
// $oPage->add($this->GetRenderContent($oPage, $aExtraParams, $sId));
$sQuery = $this->BuildQuery();
$res = CMDBSource::Query($sQuery);
$aQueryCols = CMDBSource::GetColumns($res);
// Prepare column definitions (check + give default values)
//
foreach($this->m_aColumns as $sName => $aColumnData)
{
if (!in_array($sName, $aQueryCols))
{
throw new Exception("Unknown column name '$sName' in sqlblock column");
}
if (!isset($aColumnData['label']))
{
$this->m_aColumns[$sName]['label'] = $sName;
}
if (isset($aColumnData['drilldown']) && !empty($aColumnData['drilldown']))
{
// Check if the OQL is valid
try
{
$this->m_aColumns[$sName]['filter'] = DBObjectSearch::FromOQL($aColumnData['drilldown']);
}
catch(OQLException $e)
{
unset($aColumnData['drilldown']);
}
}
}
if (strlen($this->m_sTitle) > 0)
{
$oPage->add("<h2>".Dict::S($this->m_sTitle)."</h2>\n");
}
switch ($this->m_sType)
{
case 'bars':
case 'pie':
$aColNames = array_keys($this->m_aColumns);
$sXColName = $aColNames[0];
$sYColName = $aColNames[1];
$aData = array();
$aRows = array();
while($aRow = CMDBSource::FetchArray($res))
{
$aData[$aRow[$sXColName]] = $aRow[$sYColName];
$aRows[$aRow[$sXColName]] = $aRow;
}
$this->RenderChart($oPage, $sId, $aData, $this->m_aColumns[$sYColName]['drilldown'], $aRows);
break;
default:
case 'table':
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (!empty($sContext))
{
$sContext = '&'.$sContext;
}
$aDisplayConfig = array();
foreach($this->m_aColumns as $sName => $aColumnData)
{
$aDisplayConfig[$sName] = array('label' => $aColumnData['label'], 'description' => '');
}
$aDisplayData = array();
while($aRow = CMDBSource::FetchArray($res))
{
$aSQLColNames = array_keys($aRow);
$aDisplayRow = array();
foreach($this->m_aColumns as $sName => $aColumnData)
{
if (isset($aColumnData['filter']))
{
$sFilter = $aColumnData['drilldown'];
$sClass = $aColumnData['filter']->GetClass();
$sFilter = str_replace('SELECT '.$sClass, '', $sFilter);
foreach($aSQLColNames as $sColName)
{
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRow[$sColName] )."'", $sFilter);
}
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&oql_clause='.urlencode($sFilter).'&format=html'.$sContext;
$aDisplayRow[$sName] = '<a href="'.$sURL.'">'.$aRow[$sName]."</a>";
}
else
{
$aDisplayRow[$sName] = $aRow[$sName];
}
}
$aDisplayData[] = $aDisplayRow;
}
$oPage->table($aDisplayConfig, $aDisplayData);
break;
}
}
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
{
$sHtml = '';
return $sHtml;
}
protected function RenderChart($oPage, $sId, $aValues, $sDrillDown = '', $aRows = array())
{
// 1- Compute Open Flash Chart data
//
$aValueKeys = array();
$index = 0;
if ((count($aValues) > 0) && ($sDrillDown != ''))
{
$oFilter = DBObjectSearch::FromOQL($sDrillDown);
$sClass = $oFilter->GetClass();
$sOQLClause = str_replace('SELECT '.$sClass, '', $sDrillDown);
$aSQLColNames = array_keys(current($aRows)); // Read the list of columns from the current (i.e. first) element of the array
$oAppContext = new ApplicationContext();
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&format=html&'.$oAppContext->GetForLink().'&oql_clause=';
}
$aURLs = array();
foreach($aValues as $key => $value)
{
// Make sure that values are integers (so that max() will work....)
// and build an array of STRING with the keys (numeric keys are transformed into string by PHP :-(
$aValues[$key] = (int)$value;
$aValueKeys[] = (string)$key;
// Build the custom query for the 'drill down' on each element
if ($sDrillDown != '')
{
$sFilter = $sOQLClause;
foreach($aSQLColNames as $sColName)
{
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRows[$key][$sColName] )."'", $sFilter);
$aURLs[$index] = $sURL.urlencode($sFilter);
}
}
$index++;
}
$oChart = new open_flash_chart();
if ($this->m_sType == 'bars')
{
$oChartElement = new bar_glass();
if (count($aValues) > 0)
{
$maxValue = max($aValues);
}
else
{
$maxValue = 1;
}
$oYAxis = new y_axis();
$aMagicValues = array(1,2,5,10);
$iMultiplier = 1;
$index = 0;
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
while($maxValue > $iTop)
{
$index++;
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
if (($index % count($aMagicValues)) == 0)
{
$iMultiplier = $iMultiplier * 10;
}
}
//echo "oYAxis->set_range(0, $iTop, $iMultiplier);\n";
$oYAxis->set_range(0, $iTop, $iMultiplier);
$oChart->set_y_axis( $oYAxis );
$aBarValues = array();
foreach($aValues as $iValue)
{
$oBarValue = new bar_value($iValue);
$oBarValue->on_click("ofc_drilldown_{$sId}");
$aBarValues[] = $oBarValue;
}
$oChartElement->set_values($aBarValues);
//$oChartElement->set_values(array_values($aValues));
$oXAxis = new x_axis();
$oXLabels = new x_axis_labels();
// set them vertical
$oXLabels->set_vertical();
// set the label text
$oXLabels->set_labels($aValueKeys);
// Add the X Axis Labels to the X Axis
$oXAxis->set_labels( $oXLabels );
$oChart->set_x_axis( $oXAxis );
}
else
{
$oChartElement = new pie();
$oChartElement->set_start_angle( 35 );
$oChartElement->set_animate( true );
$oChartElement->set_tooltip( '#label# - #val# (#percent#)' );
$oChartElement->set_colours( array('#FF8A00', '#909980', '#2C2B33', '#CCC08D', '#596664') );
$aData = array();
foreach($aValues as $sValue => $iValue)
{
$oPieValue = new pie_value($iValue, $sValue); //@@ BUG: not passed via ajax !!!
$oPieValue->on_click("ofc_drilldown_{$sId}");
$aData[] = $oPieValue;
}
$oChartElement->set_values( $aData );
$oChart->x_axis = null;
}
// Title given in HTML
//$oTitle = new title($this->m_sTitle);
//$oChart->set_title($oTitle);
$oChart->set_bg_colour('#FFFFFF');
$oChart->add_element( $oChartElement );
$sData = $oChart->toPrettyString();
$sData = json_encode($sData);
// 2- Declare the Javascript function that will render the chart data\
//
$oPage->add_script(
<<< EOF
function ofc_get_data_{$sId}()
{
return $sData;
}
EOF
);
if (count($aURLs) > 0)
{
$sURLList = '';
foreach($aURLs as $index => $sURL)
{
$sURLList .= "\taURLs[$index] = '".addslashes($sURL)."';\n";
}
$oPage->add_script(
<<< EOF
function ofc_drilldown_{$sId}(index)
{
var aURLs = new Array();
{$sURLList}
var sURL = aURLs[index];
window.location.href = sURL; // Navigate !
}
EOF
);
}
// 3- Insert the Open Flash chart
//
$oPage->add("<div id=\"$sId\"><div>\n");
$oPage->add_ready_script(
<<<EOF
swfobject.embedSWF( "../images/open-flash-chart.swf",
"{$sId}",
"100%", "300","9.0.0",
"expressInstall.swf",
{"get-data":"ofc_get_data_{$sId}", "id":"{$sId}"},
{'wmode': 'transparent'}
);
EOF
);
}
}
?>

View File

@@ -1,39 +1,36 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* File to include to initialize the datamodel in memory
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/core/contexttag.class.inc.php');
session_name('itop-'.md5(APPROOT));
session_start();
$sSwitchEnv = utils::ReadParam('switch_env', null);
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)))
if (isset($_REQUEST['switch_env']))
{
$_SESSION['itop_env'] = $sSwitchEnv;
$sEnv = $sSwitchEnv;
$sEnv = $_REQUEST['switch_env'];
$_SESSION['itop_env'] = $sEnv;
// TODO: reset the credentials as well ??
}
else if (isset($_SESSION['itop_env']))
@@ -46,4 +43,6 @@ else
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, true /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv);
MetaModel::Startup($sConfigFile);
?>

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class DisplayTemplate
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/application/displayblock.class.inc.php');
@@ -283,29 +282,9 @@ class ObjectDetailsTemplate extends DisplayTemplate
$sStateAttCode = MetaModel :: GetStateAttributeCode(get_class($this->m_oObj));
$aTemplateFields = array();
preg_match_all('/\\$this->([a-z0-9_]+)\\$/', $this->m_sTemplate, $aMatches);
foreach ($aMatches[1] as $sAttCode)
{
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
{
$aTemplateFields[] = $sAttCode;
}
else
{
$aParams['this->'.$sAttCode] = "<!--Unknown attribute: $sAttCode-->";
}
}
$aTemplateFields = $aMatches[1];
preg_match_all('/\\$this->field\\(([a-z0-9_]+)\\)\\$/', $this->m_sTemplate, $aMatches);
foreach ($aMatches[1] as $sAttCode)
{
if (MetaModel::IsValidAttCode(get_class($this->m_oObj), $sAttCode))
{
$aTemplateFields[] = $sAttCode;
}
else
{
$aParams['this->field('.$sAttCode.')'] = "<!--Unknown attribute: $sAttCode-->";
}
}
$aTemplateFields = array_merge($aTemplateFields, $aMatches[1]);
$aFieldsComments = (isset($aParams['fieldsComments'])) ? $aParams['fieldsComments'] : array();
$aFieldsMap = array();
@@ -353,15 +332,11 @@ class ObjectDetailsTemplate extends DisplayTemplate
if ($iFlags & OPT_ATT_SLAVE)
{
$iSynchroFlags = $this->m_oObj->GetSynchroReplicaFlags($sAttCode, $aReasons);
$sSynchroIcon = "&nbsp;<img id=\"synchro_$iInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
$sSynchroIcon = "&nbsp;<img id=\"synchro_$sInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
$sTip = '';
foreach($aReasons as $aRow)
{
$sDescription = htmlentities($aRow['description'], ENT_QUOTES, 'UTF-8');
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
$sTip .= "<div class='synchro-source'>";
$sTip .= "<div class='synchro-source-title'>Synchronized with {$aRow['name']}</div>";
$sTip .= "<div class='synchro-source-description'>$sDescription</div>";
$sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>";
}
$oPage->add_ready_script("$('#synchro_$iInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
}

View File

@@ -0,0 +1,20 @@
<div class="page_header" style="padding:0.5em;">
<h1><itopstring>UI:NotificationsMenu:Title</itopstring></h1>
</div>
<itoptoggle name="UI:NotificationsMenu:Help" open="true">
<div style="padding: 1em; font-size:10pt;background:#E8F3CF;margin-top: 0.25em;">
<img src="../images/bell.png" style="margin-top: -60px; margin-right: 10px; float: right;">
<itopstring>UI:NotificationsMenu:HelpContent</itopstring>
</div>
</itoptoggle>
<p>&nbsp;</p>
<itoptabs>
<itoptab name="UI:NotificationsMenu:Triggers">
<h2><itopstring>UI:NotificationsMenu:AvailableTriggers</itopstring></h2>
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT Trigger</itopblock>
</itoptab>
<itoptab name="UI:NotificationsMenu:Actions">
<h2><itopstring>UI:NotificationsMenu:AvailableActions</itopstring></h2>
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT ActionEmail</itopblock>
</itoptab>
</itoptabs>

View File

@@ -1,109 +1,28 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* This class records the pending "transactions" corresponding to forms that have not been
* submitted yet, in order to prevent double submissions. When created a transaction remains valid
* until the user's session expires. This class is actually a wrapper to the underlying implementation
* which choice is configured via the parameter 'transaction_storage'
*
* until the user's session expires
* @package iTop
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class privUITransaction
{
/**
* Create a new transaction id, store it in the session and return its id
* @param void
* @return int The identifier of the new transaction
*/
public static function GetNewTransactionId()
{
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
if (!$bTransactionsEnabled)
{
return 'notransactions'; // Any value will do
}
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
if (!class_exists($sClass, false))
{
IssueLog::Error("Incorrect value '".MetaModel::GetConfig()->Get('transaction_storage')."' for 'transaction_storage', the class '$sClass' does not exists. Using privUITransactionSession instead for storing sessions.");
$sClass = 'privUITransactionSession';
}
return (string)$sClass::GetNewTransactionId();
}
/**
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
* the session so that another call to IsTransactionValid for the same transaction id
* will return false
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
* @param bool $bRemoveTransaction True if the transaction must be removed
* @return bool True if the transaction is valid, false otherwise
*/
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
if (!$bTransactionsEnabled)
{
return true; // All values are valid
}
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
if (!class_exists($sClass, false))
{
$sClass = 'privUITransactionSession';
}
return $sClass::IsTransactionValid($id, $bRemoveTransaction);
}
/**
* Removes the transaction specified by its id
* @param int $id The Identifier (as returned by GetNewTranscationId) of the transaction to be removed.
* @return void
*/
public static function RemoveTransaction($id)
{
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
if (!$bTransactionsEnabled)
{
return; // Nothing to do
}
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
if (!class_exists($sClass, false))
{
$sClass = 'privUITransactionSession';
}
$sClass::RemoveTransaction($id);
}
}
/**
* The original (and by default) mechanism for storing transaction information
* as an array in the $_SESSION variable
*
*/
class privUITransactionSession
{
/**
* Create a new transaction id, store it in the session and return its id
@@ -175,178 +94,4 @@ class privUITransactionSession
}
}
}
/**
* An alternate implementation for storing the transactions as temporary files
* Useful when using an in-memory storage for the session which do not
* guarantee mutual exclusion for writing
*/
class privUITransactionFile
{
/**
* Create a new transaction id, store it in the session and return its id
* @param void
* @return int The identifier of the new transaction
*/
public static function GetNewTransactionId()
{
if (!is_dir(APPROOT.'data/transactions'))
{
if (!is_writable(APPROOT.'data'))
{
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
}
if (!@mkdir(APPROOT.'data/transactions'))
{
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
}
}
if (!is_writable(APPROOT.'data/transactions'))
{
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
}
self::CleanupOldTransactions();
$id = basename(tempnam(APPROOT.'data/transactions', self::GetUserPrefix()));
self::Info('GetNewTransactionId: Created transaction: '.$id);
return (string)$id;
}
/**
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
* the session so that another call to IsTransactionValid for the same transaction id
* will return false
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
* @param bool $bRemoveTransaction True if the transaction must be removed
* @return bool True if the transaction is valid, false otherwise
*/
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
$sFilepath = APPROOT.'data/transactions/'.$id;
clearstatcache(true, $sFilepath);
$bResult = file_exists($sFilepath);
if ($bResult)
{
if ($bRemoveTransaction)
{
$bResult = @unlink($sFilepath);
if (!$bResult)
{
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
}
else
{
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
}
}
}
else
{
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
}
return $bResult;
}
/**
* Removes the transaction specified by its id
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
* @return void
*/
public static function RemoveTransaction($id)
{
$bSuccess = true;
$sFilepath = APPROOT.'data/transactions/'.$id;
clearstatcache(true, $sFilepath);
if(!file_exists($sFilepath))
{
$bSuccess = false;
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
}
$bSuccess = @unlink($sFilepath);
if (!$bSuccess)
{
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
}
else
{
self::Info('RemoveTransaction: OK '.$id);
}
return $bSuccess;
}
/**
* Cleanup old transactions which have been pending since more than 24 hours
* Use filemtime instead of filectime since filectime may be affected by operations on the directory (like changing the access rights)
*/
protected static function CleanupOldTransactions()
{
$iLimit = time() - 24*3600;
clearstatcache();
$aTransactions = glob(APPROOT.'data/transactions/*-*');
foreach($aTransactions as $sFileName)
{
if (filemtime($sFileName) < $iLimit)
{
@unlink($sFileName);
self::Info('CleanupOldTransactions: Deleted transaction: '.$sFileName);
}
}
}
/**
* For debugging purposes: gets the pending transactions of the current user
* as an array, with the date of the creation of the transaction file
*/
protected static function GetPendingTransactions()
{
clearstatcache();
$aResult = array();
$aTransactions = glob(APPROOT.'data/transactions/'.self::GetUserPrefix().'*');
foreach($aTransactions as $sFileName)
{
$aResult[] = date('Y-m-d H:i:s', filemtime($sFileName)).' - '.basename($sFileName);
}
sort($aResult);
return $aResult;
}
protected static function GetUserPrefix()
{
$sPrefix = substr(UserRights::GetUser(), 0, 10);
$sPrefix = preg_replace('/[^a-zA-Z0-9-_]/', '_', $sPrefix);
return $sPrefix.'-';
}
protected static function Info($sText)
{
self::Write('Info | '.$sText);
}
protected static function Warning($sText)
{
self::Write('Warning | '.$sText);
}
protected static function Error($sText)
{
self::Write('Error | '.$sText);
}
protected static function Write($sText)
{
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
if ($bLogEnabled)
{
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
if ($hLogFile !== false)
{
flock($hLogFile, LOCK_EX);
$sDate = date('Y-m-d H:i:s');
fwrite($hLogFile, "$sDate | $sText\n");
fflush($hLogFile);
flock($hLogFile, LOCK_UN);
fclose($hLogFile);
}
}
}
}
?>

View File

@@ -1,33 +1,31 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class UIExtKeyWidget
* UI wdiget for displaying and editing external keys when
* A simple drop-down list is not enough...
*
*
* The layout is the following
*
*
* +-- #label_<id> (input)-------+ +-----------+
* | | | Browse... |
* +-----------------------------+ +-----------+
*
*
* And the popup dialog has the following layout:
*
*
* +------------------- ac_dlg_<id> (div)-----------+
* + +--- ds_<id> (div)---------------------------+ |
* | | +------------- fs_<id> (form)------------+ | |
@@ -54,23 +52,22 @@
* | | +--------+ +-----+ | |
* | +--------------------------------------------+ |
* +------------------------------------------------+
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UIExtKeyWidget
class UIExtKeyWidget
{
const ENUM_OUTPUT_FORMAT_CSV = 'csv';
const ENUM_OUTPUT_FORMAT_JSON = 'json';
protected $iId;
protected $sTargetClass;
protected $sAttCode;
protected $bSearchMode;
//public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
static public function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false)
{
@@ -97,36 +94,36 @@ class UIExtKeyWidget
$this->sAttCode = $sAttCode;
$this->bSearchMode = $bSearchMode;
}
/**
* Get the HTML fragment corresponding to the ext key editing widget
* Get the HTML fragment corresponding to the linkset editing widget
* @param WebPage $oP The web page used for all the output
* @param array $aArgs Extra context arguments
* @param Hash $aArgs Extra context arguments
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true)
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select')
{
if (!is_null($bSearchMode))
{
$this->bSearchMode = $bSearchMode;
}
$sTitle = addslashes($sTitle);
$sTitle = addslashes($sTitle);
$oPage->add_linked_script('../js/extkeywidget.js');
$oPage->add_linked_script('../js/forms-json-utils.js');
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
$bCreate = (!$this->bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
$bExtensions = true;
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey\">";
$sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
if($this->bSearchMode)
{
$sWizHelper = 'null';
$sWizHelperJSON = "''";
$sJSSearchMode = 'true';
}
}
else
{
if (isset($aArgs['wizHelper']))
@@ -144,22 +141,16 @@ class UIExtKeyWidget
{
throw new Exception('Implementation: null value for allowed values definition');
}
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
// Don't automatically launch the search if the table is huge
$bDoSearch = !utils::IsHighCardinality($this->sTargetClass);
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
if (!$oAllowedValues->CountExceeds($iMaxComboLength))
elseif ($oAllowedValues->Count() < $iMaxComboLength)
{
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
switch($sDisplayStyle)
{
case 'radio':
case 'radio_horizontal':
case 'radio_vertical':
$sValidationField = null;
$sValidationField = "<span id=\"v_{$this->iId}\"></span>";
$sHTMLValue = '';
$bVertical = ($sDisplayStyle != 'radio_horizontal');
$bExtensions = false;
$oAllowedValues->Rewind();
@@ -167,73 +158,48 @@ class UIExtKeyWidget
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);
}
$sHTMLValue = $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", $bMandatory, $bVertical, $sValidationField);
$aEventsList[] ='change';
break;
case 'select':
case 'list':
default:
$sSelectMode = 'true';
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
$sHTMLValue .= "<div class=\"field_select_wrapper\">\n";
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
if ($this->bSearchMode)
{
if ($bSearchMultiple)
{
$sHTMLValue .= "<select class=\"multiselect\" multiple title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}[]\" id=\"$this->iId\">\n";
}
else
{
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
}
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
}
else
{
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
}
$oAllowedValues->Rewind();
while($oObj = $oAllowedValues->Fetch())
{
$key = $oObj->GetKey();
$display_value = $oObj->GetName();
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true') )
{
// When there is only once choice, select it by default
$sSelected = 'selected';
$sSelected = ' selected';
}
else
{
$sSelected = (is_array($value) && in_array($key, $value)) || ($value == $key) ? 'selected' : '';
$sSelected = ($value == $key) ? ' selected' : '';
}
$sHTMLValue .= "<option value=\"$key\" $sSelected>$display_value</option>\n";
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
}
$sHTMLValue .= "</select>\n";
$sHTMLValue .= "</div>\n";
if (($this->bSearchMode) && $bSearchMultiple)
{
$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);");
}
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
@@ -245,15 +211,8 @@ EOF
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)
{
$value = null;
}
$sSelectMode = 'false';
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
{
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
@@ -263,19 +222,20 @@ EOF
$sDisplayValue = $this->GetObjectName($value);
}
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 30; //@@@ $this->oAttDef->GetMaxSize();
// the input for the auto-complete
$sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.Search();\"/></span>";
$sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/>&nbsp;";
$sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif\" onClick=\"oACWidget_{$this->iId}.Search();\"/>&nbsp;";
// another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"$value\" />\n";
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
// Scripts to start the autocomplete and bind some events to it
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
$('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }});
$('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
@@ -290,7 +250,7 @@ EOF
}
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
{
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/></span>";
$sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/>&nbsp;";
$oPage->add_ready_script(
<<<EOF
if ($('#ac_tree_{$this->iId}').length == 0)
@@ -302,9 +262,7 @@ EOF
}
if ($bCreate && $bExtensions)
{
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"/></span>";
$sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/>&nbsp;";
$oPage->add_ready_script(
<<<EOF
if ($('#ajax_{$this->iId}').length == 0)
@@ -314,17 +272,14 @@ EOF
EOF
);
}
$sHTMLValue .= "</div>";
// Note: This test is no longer necessary as we changed the markup to extract validation decoration in the standard .field_input_xxx container
//if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
//{
$sHTMLValue .= "<span class=\"form_validation\" id=\"v_{$this->iId}\"></span><span class=\"field_status\" id=\"fstatus_{$this->iId}\"></span>";
//}
if ($sDisplayStyle == 'select')
{
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
}
$sHTMLValue .= "</span>"; // end of no wrap
return $sHTMLValue;
}
public function GetSearchDialog(WebPage $oPage, $sTitle, $oCurrObject = null)
{
$sHTML = '<div class="wizContainer" style="vertical-align:top;"><div id="dc_'.$this->iId.'">';
@@ -332,9 +287,8 @@ EOF
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
$aArgs = array('this' => $oCurrObject);
$aParams = array('query_params' => $aArgs);
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aArgs);
$aParams = array('query_params' => array('this' => $oCurrObject));
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aParams);
$oFilter = $oSet->GetFilter();
}
else
@@ -344,16 +298,7 @@ EOF
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId,
array(
'menu' => false,
'currentId' => $this->iId,
'table_id' => "dr_{$this->iId}",
'table_inner_id' => "{$this->iId}_results",
'selection_mode' => true,
'selection_type' => 'single',
'cssCount' => '#count_'.$this->iId)
);
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => true, 'currentId' => $this->iId));
$sHTML .= "<form id=\"fr_{$this->iId}\" OnSubmit=\"return oACWidget_{$this->iId}.DoOk();\">\n";
$sHTML .= "<div id=\"dr_{$this->iId}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHTML .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
@@ -377,13 +322,9 @@ EOF
/**
* Search for objects to be selected
*
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param $sFilter
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
* @param null $oObj
*
* @throws \OQLException
* @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search
*/
public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null)
{
@@ -391,113 +332,47 @@ EOF
{
throw new Exception('Implementation: null value for allowed values definition');
}
$oFilter = DBObjectSearch::FromOQL($sFilter);
if (strlen($sRemoteClass) > 0)
try
{
$oFilter->ChangeClass($sRemoteClass);
$oFilter = DBObjectSearch::FromOQL($sFilter);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj)));
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
}
catch(MissingQueryArgument $e)
{
// When used in a search form the $this parameter may be missing, in this case return all possible values...
// TODO check if we can improve this behavior...
$sOQL = 'SELECT '.$sRemoteClass;
$oFilter = DBObjectSearch::FromOQL($sOQL);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
//$oBlock = new DisplayBlock($oFilter, 'list', false);
//$oBlock->Display($oP, $this->iId.'_results', array('cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
// 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', false, array('query_params' => array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId)));
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, '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()
*
* @throws CoreException
* @throws OQLException
*/
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $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
*/
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains)
{
if (is_null($sFilter))
{
throw new Exception('Implementation: null value for allowed values definition');
}
// Current extkey value, so we can display event if it is not available anymore (eg. archived).
$iCurrentExtKeyId = (is_null($oObj) || $this->sAttCode === '') ? 0 : $oObj->Get($this->sAttCode);
$oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities
$iMax = 150;
$oValuesSet->SetLimit($iMax);
$oValuesSet->SetSort(false);
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
if (empty($sOperation) || 'equals_start_with' == $sOperation)
{
$aValues = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals');
asort($aValues);
$iMax -= count($aValues);
if ($iMax > 0 ) {
$oValuesSet->SetLimit($iMax);
$aValuesStartWith = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
asort($aValuesStartWith);
foreach ($aValuesStartWith as $sKey => $sFriendlyName) {
if (!isset($aValues[$sKey])) {
$aValues[$sKey] = $sFriendlyName;
}
}
}
}
else
{
$aValues = array();
}
$iMax -= count($aValues);
if ($iMax > 0 && (empty($sOperation) || 'contains' == $sOperation))
$aValues = $oValuesSet->GetValues(array('this' => $oObj), $sContains);
foreach($aValues as $sKey => $sFriendlyName)
{
$oValuesSet->SetLimit($iMax);
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
asort($aValuesContains);
foreach($aValuesContains as $sKey => $sFriendlyName)
{
if (!isset($aValues[$sKey]))
{
$aValues[$sKey] = $sFriendlyName;
}
}
}
switch($sOutputFormat)
{
case static::ENUM_OUTPUT_FORMAT_JSON:
$aJsonMap = array();
foreach ($aValues as $sKey => $sLabel)
{
$aJsonMap[] = array('value' => $sKey, 'label' => $sLabel);
}
$oP->SetContentType('application/json');
$oP->add(json_encode($aJsonMap));
break;
case static::ENUM_OUTPUT_FORMAT_CSV:
foreach($aValues as $sKey => $sFriendlyName)
{
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
}
break;
default:
throw new Exception('Invalid output format, "'.$sOutputFormat.'" given.');
break;
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
}
}
/**
* Get the display name of the selected object, to fill back the autocomplete
*/
@@ -516,56 +391,11 @@ EOF
return '';
}
}
/**
* Get the form to select a leaf class from the $this->sTargetClass (that should be abstract)
* Note: Inspired from UILinksWidgetDirect::GetObjectCreationDialog()
*
* @param WebPage $oPage
*
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
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);
$aPossibleClasses = array();
foreach($aSubClasses as $sCandidateClass)
{
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
{
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
}
}
$sDialogTitle = '';
$oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
$oPage->add('<form>');
$sClassLabel = MetaModel::GetName($this->sTargetClass);
$oPage->add('<p>'.Dict::Format('UI:SelectTheTypeOf_Class_ToCreate', $sClassLabel));
$oPage->add('<nobr><select name="class">');
asort($aPossibleClasses);
foreach($aPossibleClasses as $sClassName => $sClassLabel)
{
$oPage->add("<option value=\"$sClassName\">$sClassLabel</option>");
}
$oPage->add('</select>');
$oPage->add('&nbsp; <button type="submit" class="action" style="margin-top:15px;"><span>' . Dict::S('UI:Button:Ok') . '</span></button></nobr></p>');
$oPage->add('</form>');
$oPage->add('</div></div></div>');
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').bind('submit.uilinksWizard', oACWidget_{$this->iId}.DoSelectObjectClass);");
}
/**
* Get the form to create a new object of the 'target' class
*/
public function GetObjectCreationForm(WebPage $oPage, $oCurrObject, $aPrefillFormParam)
public function GetObjectCreationForm(WebPage $oPage, $oCurrObject)
{
// Set all the default values in an object and clone this "default" object
$oNewObj = MetaModel::NewObject($this->sTargetClass);
@@ -573,7 +403,7 @@ EOF
// 1st - set context values
$oAppContext = new ApplicationContext();
$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 != ''))
{
@@ -590,27 +420,17 @@ EOF
}
}
}
// 3rd - set values from the page argument 'default'
$oNewObj->UpdateObjectFromArg('default');
$sDialogTitle = '';
$sDialogTitle = addslashes($this->sTitle);
$oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
$oPage->add("<h1>".MetaModel::GetClassIcon($this->sTargetClass)."&nbsp;".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sTargetClass))."</h1>\n");
$aFieldsFlags = array();
$aFieldsComments = array();
foreach(MetaModel::ListAttributeDefs($this->sTargetClass) as $sAttCode => $oAttDef)
{
if (($oAttDef instanceof AttributeBlob) || (false))
{
$aFieldsFlags[$sAttCode] = OPT_ATT_READONLY;
$aFieldsComments[$sAttCode] = '&nbsp;<img src="../images/transp-lock.png" style="vertical-align:middle" title="'.htmlentities(Dict::S('UI:UploadNotSupportedInThisMode')).'"/>';
}
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true, 'fieldsFlags' => $aFieldsFlags, 'fieldsComments' => $aFieldsComments));
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true));
$oPage->add('</div></div></div>');
// $oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: $(window).width()*0.8, height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
$oPage->add_ready_script("$('#dcr_{$this->iId} form').bind('submit.uilinksWizard', oACWidget_{$this->iId}.DoCreateObject);");
}
@@ -627,12 +447,21 @@ EOF
{
throw new Exception('Implementation: null value for allowed values definition');
}
$oFilter = DBObjectSearch::FromOQL($sFilter);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj, 'current_extkey_id' => $currValue));
$oSet->SetShowObsoleteData(utils::ShowObsoleteData());
try
{
$oFilter = DBObjectSearch::FromOQL($sFilter);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj));
}
catch(MissingQueryArgument $e)
{
// When used in a search form the $this parameter may be missing, in this case return all possible values...
// TODO check if we can improve this behavior...
$sOQL = 'SELECT '.$this->m_sTargetClass;
$oFilter = DBObjectSearch::FromOQL($sOQL);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oSet = new DBObjectSet($oFilter);
}
$sHKAttCode = MetaModel::IsHierarchicalClass($this->sTargetClass);
$this->DumpTree($oPage, $oSet, $sHKAttCode, $currValue);
@@ -641,7 +470,7 @@ EOF
$oPage->add('</div>');
$oPage->add("<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\">&nbsp;&nbsp;");
$oPage->add("<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
$oPage->add('</div></div>');
$oPage->add_ready_script("\$('#tree_$this->iId ul').treeview();\n");
$oPage->add_ready_script("\$('#dlg_tree_$this->iId').dialog({ width: 'auto', height: 'auto', autoOpen: true, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.OnHKResize, close: oACWidget_{$this->iId}.OnHKClose });\n");
@@ -652,23 +481,21 @@ EOF
*/
public function DoCreateObject($oPage)
{
try
$oObj = MetaModel::NewObject($this->sTargetClass);
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
if (count($aErrors) == 0)
{
$oObj = MetaModel::NewObject($this->sTargetClass);
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
if (count($aErrors) == 0)
{
$oObj->DBInsert();
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
}
else
{
return array('error' => implode(' ', $aErrors), 'id' => 0);
}
$oMyChange = MetaModel::NewObject("CMDBChange");
$oMyChange->Set("date", time());
$sUserString = CMDBChange::GetCurrentUserName();
$oMyChange->Set("userinfo", $sUserString);
$iChangeId = $oMyChange->DBInsert();
$oObj->DBInsertTracked($oMyChange);
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
}
catch(Exception $e)
else
{
return array('error' => $e->getMessage(), 'id' => 0);
return array('name' => implode(' ', $aErrors), 'id' => 0);
}
}
@@ -686,7 +513,7 @@ EOF
$aTree[$iParentId][$oObj->GetKey()] = $oObj->GetName();
$aNodes[$oObj->GetKey()] = $oObj;
}
$aParents = array_keys($aTree);
$aRoots = array();
foreach($aParents as $id)
@@ -701,7 +528,7 @@ EOF
$this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
}
}
function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
{
$bSelect = true;
@@ -712,7 +539,6 @@ EOF
$aSortedRoots = $aTree[$iRootId];
asort($aSortedRoots);
$oP->add("<ul>\n");
$fUniqueId = microtime(true);
foreach($aSortedRoots as $id => $sName)
{
if ($bSelect)
@@ -720,14 +546,14 @@ EOF
$sChecked = ($aNodes[$id]->GetKey() == $currValue) ? 'checked' : '';
if ($bMultiple)
{
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="checkbox" value="'.$aNodes[$id]->GetKey().'" name="selectObject[]" '.$sChecked.'>&nbsp;';
$sSelect = '<input type="checkbox" value="'.$aNodes[$id]->GetKey().'" name="selectObject[]" '.$sChecked.'>&nbsp;';
}
else
{
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'>&nbsp;';
$sSelect = '<input type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'>&nbsp;';
}
}
$oP->add('<li>'.$sSelect.'<label for="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'">'.$aNodes[$id]->GetName().'</label>');
$oP->add('<li>'.$sSelect.$aNodes[$id]->GetHyperlink());
$this->DumpNodes($oP, $id, $aTree, $aNodes, $currValue);
$oP->add("</li>\n");
}
@@ -736,3 +562,4 @@ EOF
}
}
?>

View File

@@ -1,34 +1,32 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class UIHTMLEditorWidget
* UI wdiget for displaying and editing one-way encrypted passwords
*
* @author Romain Quetiez
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Phil Eddies
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class UIHTMLEditorWidget
{
protected $m_iId;
protected $m_oAttDef;
protected $m_sAttCode;
protected $m_sNameSuffix;
protected $m_sFieldPrefix;
@@ -37,11 +35,10 @@ class UIHTMLEditorWidget
protected $m_sValue;
protected $m_sMandatory;
public function __construct($iInputId, $oAttDef, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $sValue, $sMandatory)
public function __construct($iInputId, $sAttCode, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $sValue, $sMandatory)
{
$this->m_iId = $iInputId;
$this->m_oAttDef = $oAttDef;
$this->m_sAttCode = $oAttDef->GetCode();
$this->m_sAttCode = $sAttCode;
$this->m_sNameSuffix = $sNameSuffix;
$this->m_sHelpText = $sHelpText;
$this->m_sValidationField = $sValidationField;
@@ -64,30 +61,14 @@ class UIHTMLEditorWidget
$sHelpText = $this->m_sHelpText;
$sValidationField = $this->m_sValidationField;
$sHtmlValue = "<div class=\"field_input_zone field_input_html\"><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"$iId\">$sValue</textarea></div>$sValidationField";
$sHtmlValue = "<table><tr><td><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"$iId\">$sValue</textarea></td><td>$sValidationField</td></tr></table>";
// Replace the text area with CKEditor
// To change the default settings of the editor,
// a) edit the file /js/ckeditor/config.js
// b) or override some of the configuration settings, using the second parameter of ckeditor()
$aConfig = array();
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
$aConfig['language'] = $sLanguage;
$aConfig['contentsLanguage'] = $sLanguage;
$aConfig['extraPlugins'] = 'disabler';
$sWidthSpec = addslashes(trim($this->m_oAttDef->GetWidth()));
if ($sWidthSpec != '')
{
$aConfig['width'] = $sWidthSpec;
}
$sHeightSpec = addslashes(trim($this->m_oAttDef->GetHeight()));
if ($sHeightSpec != '')
{
$aConfig['height'] = $sHeightSpec;
}
$sConfigJS = json_encode($aConfig);
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, $sConfigJS);"); // Transform $iId into a CKEdit
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, { language : '$sLanguage' , contentsLanguage : '$sLanguage', extraPlugins: 'disabler' });"); // Transform $iId into a CKEdit
// Please read...
// ValidateCKEditField triggers a timer... calling itself indefinitely
@@ -98,26 +79,9 @@ class UIHTMLEditorWidget
// Could also be bound to 'instanceReady.ckeditor'
$oPage->add_ready_script("$('#$iId').bind('validate', function(evt, sFormId) { return ValidateCKEditField('$iId', '', {$this->m_sMandatory}, sFormId, '') } );\n");
$oPage->add_ready_script(
<<<EOF
$('#$iId').bind('update', function(evt){
BlockField('cke_$iId', $('#$iId').attr('disabled'));
//Delayed execution - ckeditor must be properly initialized before setting readonly
var retryCount = 0;
var oMe = $('#$iId');
var delayedSetReadOnly = function () {
if (oMe.data('ckeditorInstance').editable() == undefined && retryCount++ < 10) {
setTimeout(delayedSetReadOnly, retryCount * 100); //Wait a while longer each iteration
}
else
{
oMe.data('ckeditorInstance').setReadOnly(oMe.prop('disabled'));
}
};
setTimeout(delayedSetReadOnly, 50);
});
EOF
);
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); } );\n");
return $sHtmlValue;
}
}
?>

View File

@@ -1,522 +0,0 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class UILinksWidgetDirect
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UILinksWidgetDirect
{
protected $sClass;
protected $sAttCode;
protected $sInputid;
protected $sNameSuffix;
protected $sLinkedClass;
/**
* UILinksWidgetDirect constructor.
* @param string $sClass
* @param string $sAttCode
* @param string $sInputId
* @param string $sNameSuffix
*/
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '')
{
$this->sClass = $sClass;
$this->sAttCode = $sAttCode;
$this->sInputid = $sInputId;
$this->sNameSuffix = $sNameSuffix;
$this->aZlist = array();
$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())
{
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'details'));
break;
default:
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'list'));
array_unshift($aZList, 'friendlyname');
}
foreach($aZList as $sLinkedAttCode)
{
if ($sLinkedAttCode != $sExtKeyToMe)
{
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
if ((!$oAttDef->IsExternalField() || ($oAttDef->GetKeyAttCode() != $sExtKeyToMe)) &&
(!$oAttDef->IsLinkSet()) )
{
$this->aZlist[] = $sLinkedAttCode;
}
}
}
}
/**
* @param WebPage $oPage
* @param DBObjectSet|ormLinkSet $oValue
* @param array $aArgs
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
*/
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
switch($oLinksetDef->GetEditMode())
{
case LINKSET_EDITMODE_NONE: // The linkset is read-only
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
break;
case LINKSET_EDITMODE_ADDONLY: // The only possible action is to open (in a new window) the form to create a new object
if ($oCurrentObj && !$oCurrentObj->IsNew())
{
$sTargetClass = $oLinksetDef->GetLinkedClass();
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink();
$oPage->p("<a target=\"_blank\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sTargetClass&$sParams&{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."</a>\n");
}
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
break;
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj);
break;
case LINKSET_EDITMODE_ADDREMOVE: // The whole linkset can be edited 'in-place'
$sTargetClass = $oLinksetDef->GetLinkedClass();
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$oExtKeyDef = MetaModel::GetAttributeDef($sTargetClass, $sExtKeyToMe);
$aButtons = array('add');
if ($oExtKeyDef->IsNullAllowed())
{
$aButtons = array('add', 'remove');
}
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons);
break;
case LINKSET_EDITMODE_ACTIONS:
default:
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, true /* bDisplayMenu*/);
}
}
/**
* @param WebPage $oPage
* @param DBObjectSet $oValue
* @param array $aArgs
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
* @param bool $bDisplayMenu
*/
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
{
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sTargetClass = $oLinksetDef->GetLinkedClass();
if ($oCurrentObj && $oCurrentObj->IsNew() && $bDisplayMenu)
{
$oPage->p(Dict::Format('UI:BeforeAdding_Class_ObjectsSaveThisObject', MetaModel::GetName($sTargetClass)));
}
else
{
$oFilter = new DBObjectSearch($sTargetClass);
$oFilter->AddCondition($oLinksetDef->GetExtKeyToMe(), $oCurrentObj->GetKey(),'=');
$aDefaults = array($oLinksetDef->GetExtKeyToMe() => $oCurrentObj->GetKey());
$oAppContext = new ApplicationContext();
foreach($oAppContext->GetNames() as $sKey)
{
// The linked object inherits the parent's value for the context
if (MetaModel::IsValidAttCode($this->sClass, $sKey) && $oCurrentObj)
{
$aDefaults[$sKey] = $oCurrentObj->Get($sKey);
}
}
$aParams = array(
'target_attr' => $oLinksetDef->GetExtKeyToMe(),
'object_id' => $oCurrentObj ? $oCurrentObj->GetKey() : null,
'menu' => $bDisplayMenu,
'menu_actions_target' => '_blank',
'default' => $aDefaults,
'table_id' => $this->sClass.'_'.$this->sAttCode,
);
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oPage, $this->sInputid, $aParams);
}
}
/**
* @param WebPage $oPage
* @param string $sProposedRealClass
*/
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '', $oSourceObj = null)
{
// 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
$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)
{
$sRealClass = $sProposedRealClass;
}
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
}
}
// Only one of the subclasses can be instantiated...
if (count($aPossibleClasses) == 1)
{
$aKeys = array_keys($aPossibleClasses);
$sRealClass = $aKeys[0];
}
if ($sRealClass != '')
{
$oPage->add("<h1>".MetaModel::GetClassIcon($sRealClass)."&nbsp;".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($sRealClass))."</h1>\n");
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$aFieldFlags = array( $sExtKeyToMe => OPT_ATT_HIDDEN);
$oObj = DBObject::MakeDefaultInstance($sRealClass);
$aPrefillParam = array('source_obj' => $oSourceObj);
$oObj->PrefillForm('creation_from_editinplace', $aPrefillParam);
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
}
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)
{
$oPage->add("<option value=\"$sClassName\">$sClassLabel</option>");
}
$oPage->add('</select>');
$oPage->add('&nbsp; <button type="button" onclick="$(\'#'.$this->sInputid.'\').directlinks(\'subclassSelected\');">'.Dict::S('UI:Button:Apply').'</button><span class="indicator" style="display:inline-block;width:16px"></span></nobr></p>');
}
$oPage->add('</div></div>');
}
/**
* @param WebPage $oPage
* @param DBObjectSet $oValue
* @param array $aArgs
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
* @param array $aButtons
*/
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
{
$aAttribs = $this->GetTableConfig();
$oValue->Rewind();
$oPage->add('<table class="listContainer" id="'.$this->sInputid.'"><tr><td>');
$aData = array();
while($oLinkObj = $oValue->Fetch())
{
$aRow = array();
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
$aData[] = $aRow;
}
$oPage->table($aAttribs, $aData);
$oPage->add('</td></tr></table>'); //listcontainer
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
$aLabels = array(
'delete' => Dict::S('UI:Button:Delete'),
// 'modify' => 'Modify...' ,
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
'remove' => Dict::S('UI:Button:Remove'),
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
);
$oContext = new ApplicationContext();
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
$sJSONLabels = json_encode($aLabels);
$sJSONButtons = json_encode($aButtons);
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
// Don't automatically launch the search if the table is huge
$bDoSearch = !utils::IsHighCardinality($this->sLinkedClass);
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper, do_search: $sJSDoSearch});");
}
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
* @param $aAlreadyLinked
*
* @throws \CoreException
* @throws \Exception
* @throws \OQLException
*/
public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked)
{
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oHiddenFilter = new DBObjectSearch($this->sLinkedClass);
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($this->sLinkedClass, $this->sClass))
{
// Prevent linking to self if the linked object is of the same family
// and already present in the database
if (!$oCurrentObj->IsNew())
{
$oHiddenFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
}
}
if (count($aAlreadyLinked) > 0)
{
$oHiddenFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
}
$oHiddenCriteria = $oHiddenFilter->GetCriteria();
$aArgs = $oHiddenFilter->GetInternalParams();
$sHiddenCriteria = $oHiddenCriteria->Render($aArgs);
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinkSetDef->GetValuesDef();
if ($valuesDef === null)
{
$oFilter = new DBObjectSearch($this->sLinkedClass);
}
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)
{
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
$oFilter->SetInternalParams($aArgs);
}
$oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}",
array(
'result_list_outer_selector' => "SearchResultsToAdd_{$this->sInputid}",
'table_id' => "add_{$this->sInputid}",
'table_inner_id' => "ResultsToAdd_{$this->sInputid}",
'selection_mode' => true,
'cssCount' => "#count_{$this->sInputid}",
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sHiddenCriteria,
)
);
$sHtml .= "<form id=\"ObjectsAddForm_{$this->sInputid}\">\n";
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->sInputid}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
$sHtml .= "</div>\n";
$sHtml .= "<input type=\"hidden\" id=\"count_{$this->sInputid}\" value=\"0\"/>";
$sHtml .= "<button type=\"button\" class=\"cancel\">".Dict::S('UI:Button:Cancel')."</button>&nbsp;&nbsp;<button type=\"button\" class=\"ok\" disabled=\"disabled\">".Dict::S('UI:Button:Add')."</button>";
$sHtml .= "</div>\n";
$sHtml .= "</form>\n";
$oPage->add($sHtml);
}
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of $this->sLinkedClass
* @param array $aAlreadyLinked Array of indentifiers of objects which are already linke to the current object (or about to be linked)
* @param DBObject $oCurrentObj The object currently being edited... if known...
* @throws Exception
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null)
{
if ($sRemoteClass == '')
{
$sRemoteClass = $this->sLinkedClass;
}
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinkSetDef->GetValuesDef();
if ($valuesDef === null)
{
$oFilter = new DBObjectSearch($sRemoteClass);
}
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))
{
// Prevent linking to self if the linked object is of the same family
// and already present in the database
if (!$oCurrentObj->IsNew())
{
$oFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
}
}
if (count($aAlreadyLinked) > 0)
{
$oFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
}
if ($oCurrentObj != null)
{
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
$oFilter->SetInternalParams($aArgs);
}
$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
}
/**
* @param WebPage $oP
* @param $oFullSetFilter
*/
public function DoAddObjects(WebPage $oP, $oFullSetFilter)
{
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
foreach($aLinkedObjectIds as $iObjectId)
{
$oLinkObj = MetaModel::GetObject($this->sLinkedClass, $iObjectId);
$oP->add($this->GetObjectRow($oP, $oLinkObj, $oLinkObj->GetKey()));
}
}
public function GetObjectModificationDlg()
{
}
protected function GetTableConfig()
{
$aAttribs = array();
$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);\" 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());
}
return $aAttribs;
}
/**
* @param WebPage $oPage
* @param string $sRealClass
* @param array $aValues
* @param int $iTempId
* @return mixed
*/
public function GetRow($oPage, $sRealClass, $aValues, $iTempId)
{
if ($sRealClass == '')
{
$sRealClass = $this->sLinkedClass;
}
$oLinkObj = new $sRealClass();
$oLinkObj->UpdateObjectFromPostedForm($this->sInputid);
return $this->GetObjectRow($oPage, $oLinkObj, $iTempId);
}
/**
* @param WebPage $oPage
* @param $oLinkObj
* @param int $iTempId
* @return mixed
*/
protected function GetObjectRow($oPage, $oLinkObj, $iTempId)
{
$aAttribs = $this->GetTableConfig();
$aRow = array();
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.($iTempId).'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
return $oPage->GetTableRow($aRow, $aAttribs);
}
/**
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
* @param DBObject $oSourceObj
* @param DBSearch|DBObjectSearch $oSearch
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{
$oAppContext = new ApplicationContext();
$sSrcClass = get_class($oSourceObj);
$sDestClass = $oSearch->GetClass();
foreach($oAppContext->GetNames() as $key)
{
// Find the value of the object corresponding to each 'context' parameter
$aCallSpec = array($sSrcClass, 'MapContextParam');
$sAttCode = '';
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))
{
$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');
$sAttCode = '';
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))
{
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}
}

View File

@@ -1,31 +1,30 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class UILinksWidget
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'application/webpage.class.inc.php');
require_once(APPROOT.'application/displayblock.class.inc.php');
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UILinksWidget
{
@@ -39,22 +38,7 @@ class UILinksWidget
protected $m_sLinkedClass;
protected $m_sRemoteClass;
protected $m_bDuplicatesAllowed;
protected $m_aEditableFields;
protected $m_aTableConfig;
/**
* UILinksWidget constructor.
*
* @param string $sClass
* @param string $sAttCode
* @param int $iInputId
* @param string $sNameSuffix
* @param bool $bDuplicatesAllowed
*
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \Exception
*/
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
{
$this->m_sClass = $sClass;
@@ -63,15 +47,12 @@ class UILinksWidget
$this->m_iInputId = $iInputId;
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
$this->m_aEditableFields = array();
/** @var AttributeLinkedSetIndirect $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
$this->m_sLinkedClass = $oAttDef->GetLinkedClass();
$this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$this->m_sExtKeyToMe = $oAttDef->GetExtKeyToMe();
/** @var AttributeExternalKey $oLinkingAttDef */
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
$sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass);
@@ -110,30 +91,21 @@ class UILinksWidget
}
}
}
/**
* A one-row form for editing a link record
*
* @param WebPage $oP Web page used for the ouput
* @param DBObject $oLinkedObj Remote object
* @param DBObject $oLinkedObj The object to which all the elements of the linked set refer to
* @param mixed $linkObjOrId Either the object linked or a unique number for new link records to add
* @param array $aArgs Extra context arguments
* @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
* @param int $iUniqueId A unique identifier of new links
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
*
* @return array The HTML fragment of the one-row form
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
* @param Hash $aArgs Extra context arguments
* @return string The HTML fragment of the one-row form
*/
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array(), $oCurrentObj )
{
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
$aRow = array();
$aFieldsMap = array();
if(is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
if(is_object($linkObjOrId))
{
$key = $linkObjOrId->GetKey();
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
@@ -142,97 +114,74 @@ class UILinksWidget
$aArgs['prefix'] = $sPrefix;
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$key}";
$aArgs['this'] = $linkObjOrId;
if($bReadOnly)
{
$aRow['form::checkbox'] = "";
foreach($this->m_aEditableFields as $sFieldCode)
{
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
$aRow[$sFieldCode] = $sDisplayValue;
}
}
else
{
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$key\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
foreach($this->m_aEditableFields as $sFieldCode)
{
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
$sSafeId = utils::GetSafeId($sFieldId);
$sValue = $linkObjOrId->Get($sFieldCode);
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'.
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId, $sNameSuffix, 0, $aArgs).
'</div></div></div>';
$aFieldsMap[$sFieldCode] = $sSafeId;
}
}
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"$key\">";
foreach($this->m_aEditableFields as $sFieldCode)
{
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
$sSafeId = self::MakeID($sFieldId);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $linkObjOrId->Get($sFieldCode), '' /* DisplayValue */, $sSafeId, $sNameSuffix, 0, $aArgs);
$aFieldsMap[$sFieldCode] = $sSafeId;
}
$sState = $linkObjOrId->GetState();
}
else
{
// form for creating a new record
if (is_object($linkObjOrId))
{
// New link existing only in memory
$oNewLinkObj = $linkObjOrId;
$iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
}
else
{
$iRemoteObjKey = $linkObjOrId;
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
$oNewLinkObj->Set($this->m_sExtKeyToRemote, $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
}
$sPrefix .= "[-$iUniqueId][";
$sPrefix .= "[$linkObjOrId][";
$iRemoteObjKey = -$linkObjOrId;
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, -$linkObjOrId);
$oNewLinkObj->Set($this->m_sExtKeyToRemote, $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
$sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix;
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".($iUniqueId < 0 ? -$iUniqueId : $iUniqueId);
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".(-$linkObjOrId);
$aArgs['this'] = $oNewLinkObj;
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"-$iUniqueId\">";
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$linkObjOrId\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"\">";
foreach($this->m_aEditableFields as $sFieldCode)
{
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.-$iUniqueId.']';
$sSafeId = utils::GetSafeId($sFieldId);
$sValue = $oNewLinkObj->Get($sFieldCode);
$sDisplayValue = $oNewLinkObj->GetEditValue($sFieldCode);
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId.']';
$sSafeId = self::MakeID($sFieldId);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'.
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId /* id */, $sNameSuffix, 0, $aArgs).
'</div></div></div>';
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $oNewLinkObj->Get($sFieldCode) /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, $sSafeId /* id */, $sNameSuffix, 0, $aArgs);
$aFieldsMap[$sFieldCode] = $sSafeId;
}
$sState = '';
// Rows created with ajax call need OnLinkAdded call.
// Rows added before loading the form cannot call OnLinkAdded.
if ($iUniqueId > 0)
{
$oP->add_script(
<<<EOF
PrepareWidgets();
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
$oP->add_script(
<<<EOF
$(".date-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd',
constrainInput: false,
changeMonth: true,
changeYear: true
});
$(".datetime-pick").datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: 'yy-mm-dd 00:00:00',
constrainInput: false,
changeMonth: true,
changeYear: true
});
EOF
);
}
);
}
$sExtKeyToMeId = self::MakeID($sPrefix.$this->m_sExtKeyToMe);
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
if(!$bReadOnly)
{
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
}
$sExtKeyToRemoteId = self::MakeID($sPrefix.$this->m_sExtKeyToRemote);
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
@@ -250,14 +199,15 @@ EOF
}
return $aRow;
}
protected function MakeID($sName)
{
return str_replace(array('[', ']', '-'), '_', $sName);
}
/**
* Display one row of the whole form
* @param WebPage $oP
* @param array $aConfig
* @param array $aRow
* @param int $iRowId
* @return string
* @return none
*/
protected function DisplayFormRow(WebPage $oP, $aConfig, $aRow, $iRowId)
{
@@ -275,13 +225,13 @@ EOF
/**
* Display the table with the form for editing all the links at once
* @param WebPage $oP The web page used for the output
* @param array $aConfig The table's header configuration
* @param array $aData The tabular data to be displayed
* @param Hash $aConfig The table's header configuration
* @param Hash $aData The tabular data to be displayed
* @return string Html fragment representing the form table
*/
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
{
$sHtml = "<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\">";
$sHtml = '';
$sHtml .= "<table class=\"listResults\">\n";
// Header
$sHtml .= "<thead>\n";
@@ -301,11 +251,11 @@ EOF
$sEmptyRowStyle = 'style="display:none;"';
}
$sHtml .= "<tr $sEmptyRowStyle id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."<input type=\"hidden\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"></td></td>";
foreach($aData as $iRowId => $aRow)
{
$sHtml .= $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
}
$sHtml .= "<tr $sEmptyRowStyle id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."</td></tr>";
$sHtml .= "</tbody>\n";
// Footer
@@ -313,64 +263,45 @@ EOF
return $sHtml;
}
/**
* Get the HTML fragment corresponding to the linkset editing widget
*
* @param WebPage $oPage
* @param DBObject|ormLinkSet $oValue
* @param array $aArgs Extra context arguments
* @param WebPage $oP The web page used for all the output
* @param DBObjectSet The initial value of the linked set
* @param Hash $aArgs Extra context arguments
* @param string $sFormPrefix prefix of the fields in the current form
* @param DBObject $oCurrentObj the current object to which the linkset is related
*
* @return string The HTML fragment to be inserted into the page
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \DictExceptionMissingString
*/
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
{
$sHtmlValue = '';
$sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
$sHtmlValue .= "<div id=\"linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
$sHtmlValue .= "<input type=\"hidden\" id=\"{$sFormPrefix}{$this->m_iInputId}\">\n";
$oValue->Rewind();
$aForm = array();
$iAddedId = 1; // Unique id for new links
while($oCurrentLink = $oValue->Fetch())
{
// We try to retrieve the remote object as usual
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */);
// If successful, it means that we can edit its link
if($oLinkedObj !== null)
{
$bReadOnly = false;
}
// Else we retrieve it without restrictions (silos) and will display its link as readonly
else
{
$bReadOnly = true;
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */, true);
}
$aRow = array();
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote));
if ($oCurrentLink->IsNew())
{
$key = -$oLinkedObj->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $key, $aArgs, $oCurrentObj);
}
else
{
$key = $oCurrentLink->GetKey();
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
}
if ($oCurrentLink->IsNew())
{
$key = -($iAddedId++);
}
else
{
$key = $oCurrentLink->GetKey();
}
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
}
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
// Don't automatically launch the search if the table is huge
$bDoSearch = !utils::IsHighCardinality($this->m_sRemoteClass);
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
$oPage->add_ready_script(<<<EOF
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}', $sJSDoSearch);
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper);
oWidget{$this->m_iInputId}.Init();
EOF
);
@@ -379,27 +310,16 @@ EOF
$sHtmlValue .= "<span style=\"clear:both;\"><p>&nbsp;</p></span>\n";
$sHtmlValue .= "</div>\n";
$oPage->add_at_the_end("<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\"></div>"); // To prevent adding forms inside the main form
return $sHtmlValue;
return $sHtmlValue;
}
/**
* @param string $sClass
* @param string $sAttCode
*
* @return string
* @throws \Exception
*/
protected static function GetTargetClass($sClass, $sAttCode)
{
/** @var AttributeLinkedSet $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sLinkedClass = $oAttDef->GetLinkedClass();
$sTargetClass = '';
switch(get_class($oAttDef))
{
case 'AttributeLinkedSetIndirect':
/** @var AttributeExternalKey $oLinkingAttDef */
/** @var AttributeLinkedSetIndirect $oAttDef */
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
$sTargetClass = $oLinkingAttDef->GetTargetClass();
break;
@@ -411,60 +331,20 @@ EOF
return $sTargetClass;
}
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
* @param $sJson
* @param array $aAlreadyLinkedIds
*
* @throws DictExceptionMissingString
* @throws Exception
*/
public function GetObjectPickerDialog($oPage, $oCurrentObj, $sJson, $aAlreadyLinkedIds = array(), $aPrefillFormParam = array())
public function GetObjectPickerDialog($oPage, $oCurrentObj)
{
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oAlreadyLinkedFilter = new DBObjectSearch($this->m_sRemoteClass);
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
{
$oAlreadyLinkedFilter->AddCondition('id', $aAlreadyLinkedIds, 'NOTIN');
$oAlreadyLinkedExpression = $oAlreadyLinkedFilter->GetCriteria();
$sAlreadyLinkedExpression = $oAlreadyLinkedExpression->Render();
}
else
{
$sAlreadyLinkedExpression = '';
}
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
if(!empty($oCurrentObj))
{
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$aPrefillFormParam['filter'] = $oFilter;
$aPrefillFormParam['dest_class'] = $this->m_sRemoteClass;
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
}
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}",
array(
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}",
'table_id' => 'add_'.$this->m_sAttCode,
'table_inner_id' => "ResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}",
'selection_mode' => true,
'json' => $sJson,
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sAlreadyLinkedExpression,
));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}", array('open' => true));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\" OnSubmit=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\">\n";
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
$sHtml .= "</div>\n";
$sHtml .= "<input type=\"hidden\" id=\"count_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"0\"/>";
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\">&nbsp;&nbsp;<input id=\"btn_ok_{$this->m_sAttCode}{$this->m_sNameSuffix}\" disabled=\"disabled\" type=\"button\" onclick=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\" value=\"".Dict::S('UI:Button:Add')."\">";
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\">&nbsp;&nbsp;<input id=\"btn_ok_{$this->m_sAttCode}{$this->m_sNameSuffix}\" disabled=\"disabled\" type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
$sHtml .= "</div>\n";
$sHtml .= "</form>\n";
$oPage->add($sHtml);
@@ -476,17 +356,11 @@ EOF
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
*
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of
* m_sRemoteClass
* @param array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of
* the search
*
* @throws \CoreException
* @throws \Exception
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
* @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = array(), $oCurrentObj = null)
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = array())
{
if ($sRemoteClass != '')
{
@@ -500,35 +374,51 @@ EOF
}
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
{
$oFilter->AddCondition('id', $aAlreadyLinkedIds, 'NOTIN');
// Positive IDs correspond to existing link records
// negative IDs correspond to "remote" objects to be linked
$aLinkIds = array();
$aRemoteObjIds = array();
foreach($aAlreadyLinkedIds as $iId)
{
if ($iId > 0)
{
$aLinkIds[] = $iId;
}
else
{
$aRemoteObjIds[] = -$iId;
}
}
if (count($aLinkIds) >0)
{
// Search for the links to find to which "remote" object they are linked
$oLinkFilter = new DBObjectSearch($this->m_sLinkedClass);
$oLinkFilter->AddCondition('id', $aLinkIds, 'IN');
$oLinkSet = new CMDBObjectSet($oLinkFilter);
while($oLink = $oLinkSet->Fetch())
{
$aRemoteObjIds[] = $oLink->Get($this->m_sExtKeyToRemote);
}
}
$oFilter->AddCondition('id', $aRemoteObjIds, 'NOTIN');
}
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$oSet = new CMDBObjectSet($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
}
/**
* @param WebPage $oP
* @param int $iMaxAddedId
* @param $oFullSetFilter
* @param DBObject $oCurrentObj
*
* @throws \ArchivedObjectException
* @throws \CoreException
*/
public function DoAddObjects(WebPage $oP, $iMaxAddedId, $oFullSetFilter, $oCurrentObj)
public function DoAddObjects(WebPage $oP, $oFullSetFilter, $oCurrentObj)
{
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
$iAdditionId = $iMaxAddedId + 1;
foreach($aLinkedObjectIds as $iObjectId)
{
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId);
if (is_object($oLinkedObj))
{
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iAdditionId));
$iAdditionId++;
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId, array(), $oCurrentObj ); // Not yet created link get negative Ids
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId));
}
else
{
@@ -536,15 +426,11 @@ EOF
}
}
}
/**
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
*
* @param DBObject $oSourceObj
* @param DBSearch|DBObjectSearch $oSearch
*
* @throws \CoreException
* @throws \Exception
* @param DBObjectSearch $oSearch
*/
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
{
@@ -563,6 +449,7 @@ EOF
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class
@@ -576,33 +463,10 @@ EOF
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
{
// Add Hierarchical condition if hierarchical key
$oAttDef = MetaModel::GetAttributeDef($sDestClass, $sAttCode);
if (isset($oAttDef) && ($oAttDef->IsExternalKey()))
{
try
{
/** @var AttributeExternalKey $oAttDef */
$sTargetClass = $oAttDef->GetTargetClass();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
if ($sHierarchicalKeyCode !== false)
{
$oFilter = new DBObjectSearch($sTargetClass);
$oFilter->AddCondition('id', $defaultValue);
$oHKFilter = new DBObjectSearch($sTargetClass);
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
}
} catch (Exception $e)
{
}
}
else
{
$oSearch->AddCondition($sAttCode, $defaultValue);
}
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}
}
?>

View File

@@ -1,27 +1,27 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class UIPasswordWidget
* UI wdiget for displaying and editing one-way encrypted passwords
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
@@ -52,21 +52,13 @@ class UIPasswordWidget
{
$sCode = $this->sAttCode.$this->sNameSuffix;
$iWidgetIndex = self::$iWidgetIndex;
$aPasswordValues = utils::ReadPostedParam("attr_{$sCode}", null, 'raw_data');
$sPasswordValue = $aPasswordValues ? $aPasswordValues['value'] : '*****';
$sConfirmPasswordValue = $aPasswordValues ? $aPasswordValues['confirm'] : '*****';
$sPasswordValue = utils::ReadPostedParam("attr_{$sCode}[value]", '*****', 'raw_data');
$sConfirmPasswordValue = utils::ReadPostedParam("attr_{$sCode}[confirm]", '*****', 'raw_data');
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
$sHtmlValue = '';
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword">';
$sHtmlValue .= '<input type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
$sHtmlValue .= '<span>'.Dict::S('UI:PasswordConfirm').'</span>';
$sHtmlValue .= '<input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
$sHtmlValue = '<input type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>&nbsp;<span class="form_validation" id="v_'.$this->iId.'"></span><br/>';
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/> '.Dict::S('UI:PasswordConfirm').' <input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'[changed]" value="'.$sChangedValue.'"/>';
$sHtmlValue .= '</div>';
$sHtmlValue .= '<span class="form_validation" id="v_'.$this->iId.'"></span><span class="field_status" id="fstatus_'.$this->iId.'"></span>';
$oPage->add_ready_script("$('#$this->iId').bind('keyup change', function(evt) { return PasswordFieldChanged('$this->iId') } );"); // Bind to a custom event: validate
$oPage->add_ready_script("$('#$this->iId').bind('keyup change validate', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate

View File

@@ -1,115 +0,0 @@
<?php
/**
*
* Copyright (C) 2010-2018 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*
*/
require_once(APPROOT.'/application/webpage.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UISearchFormForeignKeys
{
public function __construct($sTargetClass, $iInputId = null)
{
$this->m_sRemoteClass = $sTargetClass;
$this->m_iInputId = $iInputId;
}
/**
* @param WebPage $oPage
*
* @param $sTitle
*
* @throws \Exception
*/
public function ShowModalSearchForeignKeys($oPage, $sTitle)
{
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
$oBlock = new DisplayBlock($oFilter, 'search', false);
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_iInputId}",
array(
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$this->m_iInputId}",
'table_id' => "add_{$this->m_iInputId}",
'table_inner_id' => "ResultsToAdd_{$this->m_iInputId}",
'selection_mode' => true,
'cssCount' => "#count_{$this->m_iInputId}",
'query_params' => $oFilter->GetInternalParams(),
));
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_iInputId}\">\n";
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->m_iInputId}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
$sHtml .= "</div>\n";
$sHtml .= "<input type=\"hidden\" id=\"count_{$this->m_iInputId}\" value=\"0\"/>";
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_iInputId}').dialog('close');\">&nbsp;&nbsp;<input id=\"btn_ok_{$this->m_iInputId}\" disabled=\"disabled\" type=\"button\" onclick=\"return oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);\" value=\"".Dict::S('UI:Button:Add')."\">";
$sHtml .= "</div>\n";
$sHtml .= "</form>\n";
$oPage->add($sHtml);
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes });");
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});");
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId} form').bind('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);");
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId}').resize(oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);");
}
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
{
try
{
$aLinkedObjects = utils::ReadMultipleSelectionWithFriendlyname($oFullSetFilter);
$oPage->add(json_encode($aLinkedObjects));
}
catch (CoreException $e)
{
http_response_code(500);
$oPage->add(json_encode(array('error' => $e->GetMessage())));
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
}
}
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
*
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
*
* @throws \Exception
*/
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
{
if ($sRemoteClass != '')
{
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
$oFilter = new DBObjectSearch($sRemoteClass);
}
else
{
// No remote class specified use the one defined in the linkedset
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->m_iInputId}",
array('menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"));
}
}

View File

@@ -0,0 +1,422 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class UILinksWizard
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class UILinksWizard
{
protected $m_sClass;
protected $m_sLinkageAttr;
protected $m_iObjectId;
protected $m_sLinkedClass;
protected $m_sLinkingAttCode;
protected $m_aEditableFields;
protected $m_aTableConfig;
public function __construct($sClass, $sLinkageAttr, $iObjectId, $sLinkedClass = '')
{
$this->m_sClass = $sClass;
$this->m_sLinkageAttr = $sLinkageAttr;
$this->m_iObjectId = $iObjectId;
$this->m_sLinkedClass = $sLinkedClass; // Will try to guess below, if it's empty
$this->m_sLinkingAttCode = ''; // Will be filled once we've found the attribute corresponding to the linked class
$this->m_aEditableFields = array();
$this->m_aTableConfig = array();
$this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onChange=\"var value = this.checked; $('.selection').each( function() { this.checked = value; } );OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+'));
foreach(MetaModel::GetAttributesList($this->m_sClass) as $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oAttDef->IsExternalKey() && ($sAttCode != $this->m_sLinkageAttr))
{
if (empty($this->m_sLinkedClass))
{
// This is a class of objects we can manage !
// Since nothing was specify, any class will do !
$this->m_sLinkedClass = $oAttDef->GetTargetClass();
$this->m_sLinkingAttCode = $sAttCode;
}
else if ($this->m_sLinkedClass == $oAttDef->GetTargetClass())
{
// This is the class of objects we want to manage !
$this->m_sLinkingAttCode = $sAttCode;
}
}
else if ( (!$oAttDef->IsExternalKey()) && (!$oAttDef->IsExternalField()))
{
$this->m_aEditableFields[] = $sAttCode;
$this->m_aTableConfig[$sAttCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
}
}
if (empty($this->m_sLinkedClass))
{
throw( new Exception(Dict::Format('UI:Error:IncorrectLinkDefinition_LinkedClass_Class', $sLinkedClass, $sClass)));
}
foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode)
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$this->m_aTableConfig['static::'.$sFieldCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
}
}
public function Display(WebPage $oP, $aExtraParams = array())
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
$sTargetClass = $oAttDef->GetTargetClass();
$oTargetObj = MetaModel::GetObject($sTargetClass, $this->m_iObjectId);
$oP->set_title("iTop - ".MetaModel::GetName($this->m_sLinkedClass)." objects linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetRawName());
$oP->add("<div class=\"wizContainer\">\n");
$oP->add("<form method=\"post\">\n");
$oP->add("<div class=\"page_header\">\n");
$oP->add("<input type=\"hidden\" id=\"linksToRemove\" name=\"linksToRemove\" value=\"\">\n");
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"do_modify_links\">\n");
$oP->add("<input type=\"hidden\" name=\"class\" value=\"{$this->m_sClass}\">\n");
$oP->add("<input type=\"hidden\" name=\"linkage\" value=\"{$this->m_sLinkageAttr}\">\n");
$oP->add("<input type=\"hidden\" name=\"object_id\" value=\"{$this->m_iObjectId}\">\n");
$oP->add("<input type=\"hidden\" name=\"linking_attcode\" value=\"{$this->m_sLinkingAttCode}\">\n");
$oP->add("<h1>".Dict::Format('UI:ManageObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."</h1>\n");
$oP->add("</div>\n");
$oP->add_script(
<<<EOF
function OnSelectChange()
{
var nbChecked = $('.selection:checked').length;
if (nbChecked > 0)
{
$('#btnRemove').removeAttr('disabled');
}
else
{
$('#btnRemove').attr('disabled','disabled');
}
}
function RemoveSelected()
{
$('.selection:checked').each(
function()
{
$('#linksToRemove').val($('#linksToRemove').val() + ' ' + this.value);
$('#row_'+this.value).remove();
}
);
// Disable the button since all the selected items have been removed
$('#btnRemove').attr('disabled','disabled');
// Re-run the zebra plugin to properly highlight the remaining lines
$('.listResults').trigger('update');
}
function AddObjects()
{
// TO DO: compute the list of objects already linked with the current Object
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { 'operation': 'addObjects',
'class': '{$this->m_sClass}',
'linkageAttr': '{$this->m_sLinkageAttr}',
'linkedClass': '{$this->m_sLinkedClass}',
'objectId': '{$this->m_iObjectId}'
},
function(data)
{
$('#ModalDlg').html(data);
dlgWidth = $(document).width() - 100;
$('#ModalDlg').css('width', dlgWidth);
$('#ModalDlg').css('left', 50);
$('#ModalDlg').css('top', 50);
$('#ModalDlg').dialog( 'open' );
},
'html'
);
}
function SearchObjectsToAdd(currentFormId)
{
var theMap = { 'class': '{$this->m_sClass}',
'linkageAttr': '{$this->m_sLinkageAttr}',
'linkedClass': '{$this->m_sLinkedClass}',
'objectId': '{$this->m_iObjectId}'
}
if ($('#'+currentFormId+' :input[name=class]').val() != undefined)
{
theMap.linkedClass = $('#'+currentFormId+' :input[name=class]').val();
}
// Gather the parameters from the search form
$('#'+currentFormId+' :input').each(
function(i)
{
if (this.name != '')
{
theMap[this.name] = this.value;
}
}
);
theMap['operation'] = 'searchObjectsToAdd';
// Run the query and display the results
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
function(data)
{
$('#SearchResultsToAdd').html(data);
$('#SearchResultsToAdd .listResults').tablesorter( { headers: {0: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
},
'html'
);
return false;
}
function DoAddObjects(currentFormId)
{
var theMap = { 'class': '{$this->m_sClass}',
'linkageAttr': '{$this->m_sLinkageAttr}',
'linkedClass': '{$this->m_sLinkedClass}',
'objectId': '{$this->m_iObjectId}'
}
// Gather the parameters from the search form
$('#'+currentFormId+' :input').each(
function(i)
{
if ( (this.name != '') && ((this.type != 'checkbox') || (this.checked)) )
{
//console.log(this.type);
arrayExpr = /\[\]$/;
if (arrayExpr.test(this.name))
{
// Array
if (theMap[this.name] == undefined)
{
theMap[this.name] = new Array();
}
theMap[this.name].push(this.value);
}
else
{
theMap[this.name] = this.value;
}
}
}
);
theMap['operation'] = 'doAddObjects';
// Run the query and display the results
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
function(data)
{
//console.log('Data: ' + data);
if (data != '')
{
$('#empty_row').remove();
}
$('.listResults tbody').append(data);
$('.listResults').trigger('update');
$('.listResults').tablesorter( { headers: {0: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
},
'html'
);
$('#ModalDlg').dialog('close');
return false;
}
function InitForm()
{
// make sure that the form is clean
$('.selection').each( function() { this.checked = false; });
$('#btnRemove').attr('disabled','disabled');
$('#linksToRemove').val('');
}
function SubmitHook()
{
var the_form = this;
SearchObjectsToAdd(the_form.id);
return false;
}
EOF
);
$oP->add_ready_script("InitForm();");
$oFilter = new DBObjectSearch($this->m_sClass);
$oFilter->AddCondition($this->m_sLinkageAttr, $this->m_iObjectId, '=');
$oSet = new DBObjectSet($oFilter);
$aForm = array();
while($oCurrentLink = $oSet->Fetch())
{
$aRow = array();
$key = $oCurrentLink->GetKey();
$oLinkedObj = MetaModel::GetObject($this->m_sLinkedClass, $oCurrentLink->Get($this->m_sLinkingAttCode));
$aForm[$key] = $this->GetFormRow($oP, $oLinkedObj, $oCurrentLink);
}
//var_dump($aTableLabels);
//var_dump($aForm);
$this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm);
$oP->add("<span style=\"float:left;\">&nbsp;&nbsp;&nbsp;<img src=\"../images/tv-item-last.gif\">&nbsp;&nbsp;<input id=\"btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"RemoveSelected();\" >");
$oP->add("&nbsp;&nbsp;&nbsp;<input id=\"btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sLinkedClass))."\" onClick=\"AddObjects();\"></span>\n");
$oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"BackToDetails('".$sTargetClass."', ".$this->m_iObjectId.");\">");
$oP->add("&nbsp;&nbsp;&nbsp;<input id=\"btnOk\" type=\"submit\" value=\"".Dict::S('UI:Button:Ok')."\"></span>\n");
$oP->add("<span style=\"clear:both;\"><p>&nbsp;</p></span>\n");
$oP->add("</div>\n");
$oP->add("</form>\n");
if (isset($aExtraParams['StartWithAdd']) && ($aExtraParams['StartWithAdd']))
{
$oP->add_ready_script("AddObjects();");
}
}
protected function GetFormRow($oP, $oLinkedObj, $currentLink = null )
{
$aRow = array();
if(is_object($currentLink))
{
$key = $currentLink->GetKey();
$sNameSuffix = "[$key]"; // To make a tabular form
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onChange=\"OnSelectChange();\" value=\"$key\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"linkId[$key]\" value=\"$key\">";
foreach($this->m_aEditableFields as $sFieldCode)
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, $currentLink->Get($sFieldCode), '' /* DisplayValue */, $key, $sNameSuffix);
}
}
else
{
// form for creating a new record
$sNameSuffix = "[$currentLink]"; // To make a tabular form
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onChange=\"OnSelectChange();\" value=\"$currentLink\">";
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"linkId[$currentLink]\" value=\"$currentLink\">";
foreach($this->m_aEditableFields as $sFieldCode)
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode);
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, '' /* id */, $sNameSuffix);
}
}
foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode)
{
$aRow['static::'.$sFieldCode] = $oLinkedObj->GetAsHTML($sFieldCode);
}
return $aRow;
}
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
{
$oP->add("<table class=\"listResults\">\n");
// Header
$oP->add("<thead>\n");
$oP->add("<tr>\n");
foreach($aConfig as $sName=>$aDef)
{
$oP->add("<th title=\"".$aDef['description']."\">".$aDef['label']."</th>\n");
}
$oP->add("</tr>\n");
$oP->add("</thead>\n");
// Content
$oP->add("</tbody>\n");
if (count($aData) == 0)
{
$oP->add("<tr id=\"empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."</td></td>");
}
else
{
foreach($aData as $iRowId => $aRow)
{
$this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
}
}
$oP->add("</tbody>\n");
// Footer
$oP->add("</table>\n");
}
protected function DisplayFormRow(WebPage $oP, $aConfig, $aRow, $iRowId)
{
$oP->add("<tr id=\"row_$iRowId\">\n");
foreach($aConfig as $sName=>$void)
{
$oP->add("<td>".$aRow[$sName]."</td>\n");
}
$oP->add("</tr>\n");
}
public function DisplayAddForm(WebPage $oP)
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
$sTargetClass = $oAttDef->GetTargetClass();
$oTargetObj = MetaModel::GetObject($sTargetClass, $this->m_iObjectId);
$oP->add("<div class=\"wizContainer\">\n");
//$oP->add("<div class=\"page_header\">\n");
//$oP->add("<h1>".Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."</h1>\n");
//$oP->add("</div>\n");
$oFilter = new DBObjectSearch($this->m_sLinkedClass);
$oSet = new CMDBObjectSet($oFilter);
$oBlock = new DisplayBlock($oFilter, 'search', false);
$oBlock->Display($oP, 'SearchFormToAdd', array('open' => true));
$oP->Add("<form id=\"ObjectsAddForm\" OnSubmit=\"return DoAddObjects(this.id);\">\n");
$oP->Add("<div id=\"SearchResultsToAdd\">\n");
$oP->Add("<div style=\"height: 100px; background: #fff;border-color:#F6F6F1 #E6E6E1 #E6E6E1 #F6F6F1; border-style:solid; border-width:3px; text-align: center; vertical-align: center;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n");
$oP->Add("</div>\n");
$oP->add("<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#ModalDlg').dialog('close');\">&nbsp;&nbsp;<input type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">");
$oP->Add("</div>\n");
$oP->Add("</form>\n");
$oP->add_ready_script("$('#ModalDlg').dialog('option', {title:'".Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."'});");
$oP->add_ready_script("$('div#SearchFormToAdd form').bind('submit.uilinksWizard', SubmitHook);");
}
public function SearchObjectsToAdd(WebPage $oP)
{
//$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
$oFilter = new DBObjectSearch($this->m_sLinkedClass);
$oSet = new CMDBObjectSet($oFilter);
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true)); // Don't display the 'Actions' menu on the results
}
public function DoAddObjects(WebPage $oP, $aLinkedObjectIds = array())
{
//$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
//$sTargetClass = $oAttDef->GetTargetClass();
//$oP->Add("<!-- nb of objects to add: ".count($aLinkedObjectIds)." -->\n"); // Just to make sure it's not empty
$aTable = array();
foreach($aLinkedObjectIds as $iObjectId)
{
$oLinkedObj = MetaModel::GetObject($this->m_sLinkedClass, $iObjectId);
if (is_object($oLinkedObj))
{
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids
$this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId);
}
else
{
echo Dict::Format('UI:Error:Object_Class_Id_NotFound', $this->m_sLinkedClass, $iObjectId);
}
}
//var_dump($aTable);
//$oP->Add("<!-- end of added list -->\n"); // Just to make sure it's not empty
}
}
?>

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class UIWizard
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class UIWizard

View File

@@ -1,26 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Store and retrieve user custom dashboards
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/core/dbobject.class.php');

View File

@@ -1,26 +1,26 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Store and retrieve user's preferences (i.e persistent per user settings)
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/core/dbobject.class.php');
require_once(APPROOT.'/core/userrights.class.inc.php');
@@ -50,7 +50,7 @@ class appUserPreferences extends DBObject
self::Load();
}
$aPrefs = self::$oUserPrefs->Get('preferences');
if (array_key_exists($sCode, $aPrefs))
if (isset($aPrefs[$sCode]))
{
return $aPrefs[$sCode];
}
@@ -72,16 +72,9 @@ class appUserPreferences extends DBObject
self::Load();
}
$aPrefs = self::$oUserPrefs->Get('preferences');
if (array_key_exists($sCode, $aPrefs) && ($aPrefs[$sCode] === $sValue))
{
// Do not write it again
}
else
{
$aPrefs[$sCode] = $sValue;
self::$oUserPrefs->Set('preferences', $aPrefs);
self::Save();
}
$aPrefs[$sCode] = $sValue;
self::$oUserPrefs->Set('preferences', $aPrefs);
self::Save();
}
/**
@@ -110,7 +103,7 @@ class appUserPreferences extends DBObject
}
else
{
unset($aPrefs[$sCodeOrPattern]);
unset($aPrefs[$sCode]);
self::$oUserPrefs->Set('preferences', $aPrefs);
}
// Save only if needed
@@ -137,7 +130,7 @@ class appUserPreferences extends DBObject
/**
* Call this function if the user has changed (like when doing a logoff...)
*/
static public function ResetPreferences()
static public function Reset()
{
self::$oUserPrefs = null;
}
@@ -155,9 +148,7 @@ class appUserPreferences extends DBObject
{
if (self::$oUserPrefs->IsModified())
{
utils::PushArchiveMode(false);
self::$oUserPrefs->DBUpdate();
utils::PopArchiveMode();
}
}
}
@@ -181,9 +172,7 @@ class appUserPreferences extends DBObject
$oObj->Set('preferences', array()); // Default preferences: an empty array
try
{
utils::PushArchiveMode(false);
$oObj->DBInsert();
utils::PopArchiveMode();
}
catch(Exception $e)
{
@@ -218,8 +207,7 @@ class appUserPreferences extends DBObject
*/
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
utils::PushArchiveMode(false);
$this->DBDelete($oDeletionPlan);
utils::PopArchiveMode();
}
}
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class WizardHelper
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/application/uiwizard.class.inc.php');
@@ -35,7 +34,7 @@ class WizardHelper
}
/**
* Constructs the PHP target object from the parameters sent to the web page by the wizard
* @param boolean $bReadUploadedFiles True to also read any uploaded file (for blob/document fields)
* @param boolean $bReadUploadedFiles True to also ready any uploaded file (for blob/document fields)
* @return object
*/
public function GetTargetObject($bReadUploadedFiles = false)
@@ -52,7 +51,7 @@ class WizardHelper
{
// Because this is stored in a Javascript array, unused indexes
// are filled with null values and unused keys (stored as strings) contain $$NULL$$
if ( ($sAttCode !='id') && ($value !== '$$NULL$$'))
if ( ($sAttCode !='id') && ($sAttCode !== false) && ($value !== null) && ($value !== '$$NULL$$'))
{
$oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode);
if (($oAttDef->IsLinkSet()) && ($value != '') )
@@ -84,26 +83,6 @@ class WizardHelper
$oTargetObj = MetaModel::GetObject($sLinkedAttDef->GetTargetClass(), $aLinkedObject[$sLinkedAttCode]);
$oLinkedObj->Set($sLinkedAttCode, $oTargetObj);
}
elseif($sLinkedAttDef instanceof AttributeDateTime)
{
$sDateClass = get_class($sLinkedAttDef);
$sDate = $aLinkedObject[$sLinkedAttCode];
if($sDate !== null && $sDate !== '')
{
$oDateTimeFormat = $sDateClass::GetFormat();
$oDate = $oDateTimeFormat->Parse($sDate);
if ($sDateClass == "AttributeDate")
{
$sDate = $oDate->format('Y-m-d');
}
else
{
$sDate = $oDate->format('Y-m-d H:i:s');
}
}
$oLinkedObj->Set($sLinkedAttCode, $sDate);
}
else
{
$oLinkedObj->Set($sLinkedAttCode, $aLinkedObject[$sLinkedAttCode]);
@@ -129,50 +108,12 @@ class WizardHelper
$oObj->Set($sAttCode, $oDocument);
}
}
else if ( $oAttDef->GetEditClass() == 'Image' )
{
if ($bReadUploadedFiles)
{
$oDocument = utils::ReadPostedDocument('attr_'.$sAttCode, 'fcontents');
$oObj->Set($sAttCode, $oDocument);
}
else
{
// Create a new empty document, just for displaying the file name
$oDocument = new ormDocument(null, '', $value);
$oObj->Set($sAttCode, $oDocument);
}
}
else if (($oAttDef->IsExternalKey()) && (!empty($value)) && ($value > 0) )
{
// For external keys: load the target object so that external fields
// get filled too
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value, false);
if ($oTargetObj)
{
$oObj->Set($sAttCode, $oTargetObj);
}
else
{
// May happen for security reasons (portal, see ticket #1074)
$oObj->Set($sAttCode, $value);
}
}
else if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
if ($value != null)
{
$oDate = $oAttDef->GetFormat()->Parse($value);
if ($oDate instanceof DateTime)
{
$value = $oDate->format($oAttDef->GetInternalFormat());
}
else
{
$value = null;
}
}
$oObj->Set($sAttCode, $value);
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value);
$oObj->Set($sAttCode, $oTargetObj);
}
else
{
@@ -290,21 +231,11 @@ class WizardHelper
{
return $this->m_aData['m_sClass'];
}
public function GetFormPrefix()
{
return $this->m_aData['m_sFormPrefix'];
}
public function GetInitialState()
{
return isset($this->m_aData['m_sInitialState']) ? $this->m_aData['m_sInitialState'] : null;
}
public function GetStimulus()
{
return isset($this->m_aData['m_sStimulus']) ? $this->m_aData['m_sStimulus'] : null;
}
public function GetFormPrefix()
{
return $this->m_aData['m_sFormPrefix'];
}
public function GetIdForField($sFieldName)
{
@@ -344,3 +275,4 @@ class WizardHelper
return $oSet;
}
}
?>

View File

@@ -1,467 +0,0 @@
<?php
/* @author Mark Jones
* @license MIT License
* */
if (!class_exists('ZipArchive')) { throw new Exception('ZipArchive not found'); }
Class XLSXWriter
{
//------------------------------------------------------------------
protected $author ='Doc Author';
protected $sheets_meta = array();
protected $shared_strings = array();//unique set
protected $shared_string_count = 0;//count of non-unique references to the unique set
protected $temp_files = array();
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 __destruct()
{
if (!empty($this->temp_files)) {
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("/tmp", "xlsx_writer_");
$this->temp_files[] = $filename;
return $filename;
}
public function writeToStdOut()
{
$temp_file = $this->tempFilename();
self::writeToFile($temp_file);
readfile($temp_file);
}
public function writeToString()
{
$temp_file = $this->tempFilename();
self::writeToFile($temp_file);
$string = file_get_contents($temp_file);
return $string;
}
public function writeToFile($filename)
{
@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; }
$zip->addEmptyDir("docProps/");
$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'] );
}
if (!empty($this->shared_strings)) {
$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->addEmptyDir("xl/_rels/");
$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() )
{
$data = empty($data) ? array( array('') ) : $data;
$sheet_filename = $this->tempFilename();
$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" );
$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 );
$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);
}
$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))
{
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');
}
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]);
}
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>');
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);
$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>');
}
}
protected function writeStylesXML()
{
$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, '<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, '</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, '</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, '<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, '<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, '</styleSheet>');
fclose($fd);
return $tempfile;
}
protected function setSharedString($v)
{
// 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]))
{
$string_value = $this->shared_strings[$v];
}
else
{
$string_value = count($this->shared_strings);
$this->shared_strings[$v] = $string_value;
}
$this->shared_string_count++;//non-unique count
return $string_value;
}
protected function writeSharedStringsXML()
{
$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>');
}
fwrite($fd, '</sst>');
fclose($fd);
return $tempfile;
}
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>';
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>';
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>';
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.='</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']).'"/>';
}
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.="\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"/>';
}
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/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;
}
//------------------------------------------------------------------
/*
* @param $row_number int, zero based
* @param $column_number int, zero based
* @return Cell label/coordinates, ex: A1, C3, AA42
* */
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;
}
return $r . ($row_number+1);
}
//------------------------------------------------------------------
public static function log($string)
{
file_put_contents("php://stderr", date("Y-m-d H:i:s:").rtrim(is_array($string) ? json_encode($string) : $string)."\n");
}
//------------------------------------------------------------------
public static function xmlspecialchars($val)
{
return str_replace("'", "&#39;", htmlspecialchars($val));
}
//------------------------------------------------------------------
public static function array_first_key(array $arr)
{
reset($arr);
$first_key = key($arr);
return $first_key;
}
//------------------------------------------------------------------
public static function convert_date_time($date_input) //thanks to Excel::Writer::XLSX::Worksheet.pm (perl)
{
$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;
$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{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
# 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
# days by normalising the year in relation to the epoch. Thus the year 2000
# becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays.
$epoch = 1900;
$offset = 0;
$norm = 300;
$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 );
# 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;
# 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 += $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 -= $leap; # Already counted above
# Adjust for Excel erroneously treating 1900 as a leap year.
if ($days > 59) { $days++;}
return $days + $seconds;
}
//------------------------------------------------------------------
}

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class XMLPage
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT."/application/webpage.class.inc.php");
@@ -37,70 +36,65 @@ class XMLPage extends WebPage
var $m_bPassThrough;
var $m_bHeaderSent;
function __construct($s_title, $bPassThrough = false)
{
parent::__construct($s_title);
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
$this->add_header("Content-type: text/xml; charset=utf-8");
function __construct($s_title, $bPassThrough = false)
{
parent::__construct($s_title);
$this->m_bPassThrough = $bPassThrough;
$this->m_bHeaderSent = false;
$this->add_header("Content-type: text/xml; charset=utf-8");
$this->add_header("Cache-control: no-cache");
$this->add_header("Content-location: export.xml");
}
}
public function output()
{
if (!$this->m_bPassThrough)
{
// Get the unexpected output but do nothing with it
$sTrash = $this->ob_get_clean_safe();
$this->s_content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n".trim($this->s_content);
$this->add_header("Content-Length: ".strlen($this->s_content));
foreach($this->a_headers as $s_header)
{
header($s_header);
}
echo $this->s_content;
}
if (class_exists('DBSearch'))
{
DBSearch::RecordQueryTrace();
}
}
public function add($sText)
{
if (!$this->m_bPassThrough)
{
parent::add($sText);
}
else
{
if ($this->m_bHeaderSent)
{
echo $sText;
}
else
{
$s_captured_output = $this->ob_get_clean_safe();
foreach($this->a_headers as $s_header)
{
header($s_header);
}
public function output()
{
if (!$this->m_bPassThrough)
{
$this->add("<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n");
$this->add_header("Content-Length: ".strlen(trim($this->s_content)));
foreach($this->a_headers as $s_header)
{
header($s_header);
}
echo trim($this->s_content);
}
}
public function add($sText)
{
if (!$this->m_bPassThrough)
{
parent::add($sText);
}
else
{
if ($this->m_bHeaderSent)
{
echo $sText;
}
else
{
$s_captured_output = ob_get_contents();
ob_end_clean();
foreach($this->a_headers as $s_header)
{
header($s_header);
}
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?".">\n";
echo trim($s_captured_output);
echo trim($this->s_content);
echo $sText;
$this->m_bHeaderSent = true;
}
}
}
echo trim($s_captured_output);
echo trim($this->s_content);
echo $sText;
$this->m_bHeaderSent = true;
}
}
}
public function small_p($sText)
{
public function small_p($sText)
{
}
public function table($aConfig, $aData, $aParams = array())
{
}
}
?>

View File

@@ -1,28 +1,27 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Various dev/debug helpers
* TODO: cleanup or at least re-organize
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
@@ -219,7 +218,7 @@ class MyHelpers
}
}
public static function get_callstack($iLevelsToIgnore = 0, $aCallStack = null)
public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
{
if ($aCallStack == null) $aCallStack = debug_backtrace();
@@ -231,16 +230,6 @@ class MyHelpers
{
$sLine = empty($aCallInfo['line']) ? "" : $aCallInfo['line'];
$sFile = empty($aCallInfo['file']) ? "" : $aCallInfo['file'];
if ($sFile != '')
{
$sFile = str_replace('\\', '/', $sFile);
$sAppRoot = str_replace('\\', '/', APPROOT);
$iPos = strpos($sFile, $sAppRoot);
if ($iPos !== false)
{
$sFile = substr($sFile, strlen($sAppRoot));
}
}
$sClass = empty($aCallInfo['class']) ? "" : $aCallInfo['class'];
$sType = empty($aCallInfo['type']) ? "" : $aCallInfo['type'];
$sFunction = empty($aCallInfo['function']) ? "" : $aCallInfo['function'];
@@ -269,11 +258,11 @@ class MyHelpers
$args .= $a;
break;
case 'string':
$a = Str::pure2html(self::beautifulstr($a, 64, true, false));
$a = Str::pure2html(self::beautifulstr($a, 1024, true, true));
$args .= "\"$a\"";
break;
case 'array':
$args .= 'array('.count($a).')';
$args .= 'Array('.count($a).')';
break;
case 'object':
$args .= 'Object('.get_class($a).')';
@@ -282,25 +271,19 @@ class MyHelpers
$args .= 'Resource('.strstr($a, '#').')';
break;
case 'boolean':
$args .= $a ? 'true' : 'false';
$args .= $a ? 'True' : 'False';
break;
case 'NULL':
$args .= 'null';
$args .= 'Null';
break;
default:
$args .= 'Unknown';
}
}
$sFunctionInfo = "$sClass$sType$sFunction($args)";
$sFunctionInfo = "$sClass $sType $sFunction($args)";
}
$aDigestCallStack[] = array('File'=>$sFile, 'Line'=>$sLine, 'Function'=>$sFunctionInfo);
}
return $aDigestCallStack;
}
public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
{
$aDigestCallStack = self::get_callstack($iLevelsToIgnore, $aCallStack);
return self::make_table_from_assoc_array($aDigestCallStack);
}
@@ -309,17 +292,6 @@ class MyHelpers
return self::get_callstack_html($iLevelsToIgnore, $aCallStack);
}
public static function get_callstack_text($iLevelsToIgnore = 0, $aCallStack = null)
{
$aDigestCallStack = self::get_callstack($iLevelsToIgnore, $aCallStack);
$aRes = array();
foreach ($aDigestCallStack as $aCall)
{
$aRes[] = $aCall['File'].' at '.$aCall['Line'].', '.$aCall['Function'];
}
return implode("\n", $aRes);
}
///////////////////////////////////////////////////////////////////////////////
// Source: New
// Last modif: 2004/12/20 RQU

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Persistent classes (internal): user defined actions
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
@@ -39,7 +38,7 @@ abstract class Action extends cmdbAbstractObject
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -60,7 +59,7 @@ abstract class Action extends cmdbAbstractObject
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status')); // Criteria of the std 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
}
@@ -103,7 +102,7 @@ abstract class ActionNotification extends Action
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -136,7 +135,7 @@ class ActionEmail extends ActionNotification
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb,application",
"category" => "core/cmdb,bizmodel",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -157,7 +156,7 @@ class ActionEmail extends ActionNotification
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 AttributeTemplateText("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())));
// Display lists
@@ -213,12 +212,8 @@ class ActionEmail extends ActionNotification
$aRecipients = array();
while ($oObj = $oSet->Fetch())
{
$sAddress = trim($oObj->Get($sEmailAttCode));
if (strlen($sAddress) > 0)
{
$aRecipients[] = $sAddress;
$this->m_iRecipients++;
}
$aRecipients[] = $oObj->Get($sEmailAttCode);
$this->m_iRecipients++;
}
return implode(', ', $aRecipients);
}
@@ -262,12 +257,7 @@ class ActionEmail extends ActionNotification
{
$sPrefix = '';
}
if ($oLog)
{
$oLog->Set('message', $sPrefix . $sRes);
$oLog->DBUpdate();
}
$oLog->Set('message', $sPrefix.$sRes);
}
catch (Exception $e)
@@ -275,21 +265,12 @@ class ActionEmail extends ActionNotification
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());
$oLog->Set('message', 'Error: more details in the log for email "'.$oLog->GetKey().'"');
$oLog->DBUpdate();
}
}
}
if ($oLog)
{
$oLog->DBUpdate();
}
}
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
@@ -307,8 +288,8 @@ class ActionEmail extends ActionNotification
$sCC = $this->FindRecipients('cc', $aContextArgs);
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$sFrom = $this->Get('from');
$sReplyTo = $this->Get('reply_to');
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
@@ -336,8 +317,6 @@ class ActionEmail extends ActionNotification
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
if (isset($sBody)) $oLog->Set('body', $sBody);
}
$sStyles = file_get_contents(APPROOT.'css/email.css');
$sStyles .= MetaModel::GetConfig()->Get('email_css');
$oEmail = new EMail();
@@ -358,15 +337,16 @@ class ActionEmail extends ActionNotification
$sTestBody .= "</ul>\n";
$sTestBody .= "</p>\n";
$sTestBody .= "</div>\n";
$oEmail->SetBody($sTestBody, 'text/html', $sStyles);
$oEmail->SetBody($sTestBody);
$oEmail->SetRecipientTO($this->Get('test_recipient'));
$oEmail->SetRecipientFrom($this->Get('test_recipient'));
$oEmail->SetReferences($sReference);
$oEmail->SetMessageId($sMessageId);
}
else
{
$oEmail->SetSubject($sSubject);
$oEmail->SetBody($sBody, 'text/html', $sStyles);
$oEmail->SetBody($sBody);
$oEmail->SetRecipientTO($sTo);
$oEmail->SetRecipientCC($sCC);
$oEmail->SetRecipientBCC($sBCC);
@@ -376,17 +356,6 @@ class ActionEmail extends ActionNotification
$oEmail->SetMessageId($sMessageId);
}
if (isset($aContextArgs['attachments']))
{
$aAttachmentReport = array();
foreach($aContextArgs['attachments'] as $oDocument)
{
$oEmail->AddAttachment($oDocument->GetData(), $oDocument->GetFileName(), $oDocument->GetMimeType());
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData()));
}
$oLog->Set('attachments', $aAttachmentReport);
}
if (empty($this->m_aMailErrors))
{
if ($this->m_iRecipients == 0)
@@ -423,4 +392,4 @@ class ActionEmail extends ActionNotification
}
}
}
?>
?>

View File

@@ -1,98 +0,0 @@
<?php
// Copyright (C) 2016-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// Emulate the API of APC, over APCU
// 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'))
{
function apc_add($key, $var, $ttl = 0)
{
return apcu_add($key, $var, $ttl);
}
function apc_cache_info($cache_type = '', $limited = false)
{
return apcu_cache_info($limited);
}
function apc_cas($key, $old, $new)
{
return apcu_cas($key, $old, $new);
}
function apc_clear_cache($cache_type = '')
{
return apcu_clear_cache();
}
function apc_dec($key, $step = 1, &$success = null)
{
apcu_dec($key, $step, $success);
}
function apc_delete($key)
{
return apcu_delete($key);
}
function apc_exists($keys)
{
return apcu_exists($keys);
}
function apc_fetch($key)
{
return apcu_fetch($key);
}
function apc_inc($key, $step = 1, &$success = null)
{
apcu_inc($key, $step, $success);
}
function apc_sma_info($limited = false)
{
return apcu_sma_info($limited);
}
function apc_store($key, $var, $ttl = 0)
{
return apcu_store($key, $var, $ttl);
}
}
/**
* Returns user cache info... beware of the format of the returned structure that may vary (See usages)
* @return array
*/
function apc_cache_info_compat()
{
if (!function_exists('apc_cache_info')) return array();
$oFunction = new ReflectionFunction('apc_cache_info');
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
{
$aCacheUserData = @apc_cache_info('user');
}
return $aCacheUserData;
}
// Cache emulation
if (!function_exists('apc_store'))
{
require_once(APPROOT.'core/apc-emulation.php');
}

View File

@@ -1,333 +0,0 @@
<?php
// Copyright (c) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
/**
* Date: 27/09/2017
*/
/**
* @param string $cache_type
* @param bool $limited
* @return array|bool
*/
function apc_cache_info($cache_type = '', $limited = false)
{
$aInfo = array();
$sRootCacheDir = apcFile::GetCacheFileName();
$aInfo['cache_list'] = apcFile::GetCacheEntries($sRootCacheDir);
return $aInfo;
}
/**
* @param array|string $key
* @param $var
* @param int $ttl
* @return array|bool
*/
function apc_store($key, $var = NULL, $ttl = 0)
{
if (is_array($key))
{
$aResult = array();
foreach($key as $sKey => $value)
{
$aResult[] = apcFile::StoreOneFile($sKey, $value, $ttl);
}
return $aResult;
}
return apcFile::StoreOneFile($key, $var, $ttl);
}
/**
* @param $key string|array
* @return mixed
*/
function apc_fetch($key)
{
if (is_array($key))
{
$aResult = array();
foreach($key as $sKey)
{
$aResult[$sKey] = apcFile::FetchOneFile($sKey);
}
return $aResult;
}
return apcFile::FetchOneFile($key);
}
/**
* @param string $cache_type
* @return bool
*/
function apc_clear_cache($cache_type = '')
{
apcFile::DeleteEntry(utils::GetCachePath());
return true;
}
/**
* @param $key
* @return bool|string[]
*/
function apc_delete($key)
{
if (empty($key))
{
return false;
}
$bRet1 = apcFile::DeleteEntry(apcFile::GetCacheFileName($key));
$bRet2 = apcFile::DeleteEntry(apcFile::GetCacheFileName('-'.$key));
return $bRet1 || $bRet2;
}
class apcFile
{
// Check only once per request
static public $aFilesByTime = null;
static public $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 = '')
{
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey);
return utils::GetCachePath().'apc-emul/'.$sPath;
}
/** Get the list of entries from a starting folder.
* @param $sEntry string starting folder.
* @return array list of entries stored into array of key 'info'
*/
static public function GetCacheEntries($sEntry)
{
$aResult = array();
if (is_dir($sEntry))
{
$aFiles = array_diff(scandir($sEntry), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sEntry.'/'.$sFile;
$aResult = array_merge($aResult, self::GetCacheEntries($sSubFile));
}
}
else
{
$sKey = basename($sEntry);
if (strpos($sKey, '-') === 0)
{
$sKey = substr($sKey, 1);
}
$aResult[] = array('info' => $sKey);
}
return $aResult;
}
/** Delete one cache entry.
* @param $sCache
* @return bool true if the entry was deleted false if error occurs (like entry did not exist).
*/
static public function DeleteEntry($sCache)
{
if (is_dir($sCache))
{
$aFiles = array_diff(scandir($sCache), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sCache.'/'.$sFile;
if (!self::DeleteEntry($sSubFile))
{
return false;
}
}
if (!@rmdir($sCache))
{
return false;
}
}
else
{
if (!@unlink($sCache))
{
return false;
}
}
self::ResetFileCount();
return true;
}
/** Get one cache entry content.
* @param $sKey
* @return bool|mixed
*/
static public function FetchOneFile($sKey)
{
// Try the 'TTLed' version
$sValue = self::ReadCacheLocked(self::GetCacheFileName('-'.$sKey));
if ($sValue === false)
{
$sValue = self::ReadCacheLocked(self::GetCacheFileName($sKey));
if ($sValue === false)
{
return false;
}
}
$oRes = @unserialize($sValue);
return $oRes;
}
/** Add one cache entry.
* @param string $sKey
* @param $value
* @param int $iTTL time to live
* @return bool
*/
static public function StoreOneFile($sKey, $value, $iTTL)
{
if (empty($sKey))
{
return false;
}
@unlink(self::GetCacheFileName($sKey));
@unlink(self::GetCacheFileName('-'.$sKey));
if ($iTTL > 0)
{
// hint for ttl management
$sKey = '-'.$sKey;
}
$sFilename = self::GetCacheFileName($sKey);
// try to create the folder
$sDirname = dirname($sFilename);
if (!file_exists($sDirname))
{
if (!@mkdir($sDirname, 0755, true))
{
return false;
}
}
$bRes = !(@file_put_contents($sFilename, serialize($value), LOCK_EX) === false);
self::AddFile($sFilename);
return $bRes;
}
/** Manage the cache files when adding a new cache entry:
* remove older files if the mamximum is reached.
* @param $sNewFilename
*/
static protected function AddFile($sNewFilename)
{
if (strpos(basename($sNewFilename), '-') !== 0)
{
return;
}
$iMaxFiles = MetaModel::GetConfig()->Get('apc_cache_emulation.max_entries');
if ($iMaxFiles == 0)
{
return;
}
if (!self::$aFilesByTime)
{
self::ListFilesByTime();
self::$iFileCount = count(self::$aFilesByTime);
if ($iMaxFiles !== 0)
{
asort(self::$aFilesByTime);
}
}
else
{
self::$aFilesByTime[$sNewFilename] = time();
self::$iFileCount++;
}
if (self::$iFileCount > $iMaxFiles)
{
$iFileNbToRemove = self::$iFileCount - $iMaxFiles;
foreach(self::$aFilesByTime as $sFileToRemove => $iTime)
{
@unlink($sFileToRemove);
if (--$iFileNbToRemove === 0)
{
break;
}
}
self::$aFilesByTime = array_slice(self::$aFilesByTime, self::$iFileCount - $iMaxFiles, null, true);
self::$iFileCount = $iMaxFiles;
}
}
/** Get the list of files with their associated access time
* @param string $sCheck Directory to scan
*/
static protected function ListFilesByTime($sCheck = null)
{
if (empty($sCheck))
{
$sCheck = self::GetCacheFileName();
}
// Garbage collection
$aFiles = array_diff(@scandir($sCheck), array('.', '..'));
foreach($aFiles as $sFile)
{
$sSubFile = $sCheck.'/'.$sFile;
if (is_dir($sSubFile))
{
self::ListFilesByTime($sSubFile);
}
else
{
if (strpos(basename($sSubFile), '-') === 0)
{
self::$aFilesByTime[$sSubFile] = @fileatime($sSubFile);
}
}
}
}
/** Read the content of one cache file under lock protection
* @param $sFilename
* @return bool|string the content of the cache entry or false if error
*/
static protected function ReadCacheLocked($sFilename)
{
$file = @fopen($sFilename, 'r');
if ($file === false)
{
return false;
}
flock($file, LOCK_SH);
$sContent = @fread($file, @filesize($sFilename));
flock($file, LOCK_UN);
fclose($file);
return $sContent;
}
static protected function ResetFileCount()
{
self::$aFilesByTime = null;
self::$iFileCount = 0;
}
}

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Utility to import/export the DB from/to a ZIP file
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Persistent classes (internal): user defined actions
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
@@ -34,27 +33,27 @@ class ExecAsyncTask implements iBackgroundProcess
public function Process($iTimeLimit)
{
$sNow = date(AttributeDateTime::GetSQLFormat());
// Criteria: planned, and expected to occur... ASAP or in the past
$sOQL = "SELECT AsyncTask WHERE (status = 'planned') AND (ISNULL(planned) OR (planned < '$sNow'))";
$sOQL = "SELECT AsyncTask WHERE ISNULL(started) AND (ISNULL(planned) OR (planned < NOW()))";
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array());
$iProcessed = 0;
while (time() < $iTimeLimit)
while ((time() < $iTimeLimit) && ($oTask = $oSet->Fetch()))
{
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
$oTask = $oSet->Fetch();
if (is_null($oTask))
{
// Nothing to be done
break;
}
$oTask->Set('started', time());
$oTask->DBUpdate();
$oTask->Process();
$iProcessed++;
if ($oTask->Process())
{
$oTask->DBDelete();
}
$oTask->DBDelete();
}
if ($iProcessed == $oSet->Count())
{
return "processed $iProcessed tasks";
}
else
{
return "processed $iProcessed tasks (remaining: ".($oSet->Count() - $iProcessed).")";
}
return "processed $iProcessed tasks";
}
}
@@ -80,101 +79,20 @@ abstract class AsyncTask extends DBObject
"display_template" => "",
);
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_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 AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "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())));
// planned... still not used - reserved for timer management
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 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())));
}
/**
* Every is fine
*/
const OK = 0;
/**
* The task no longer exists
*/
const DELETED = 1;
/**
* The task is already being executed
*/
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.
*/
public function MarkAsRunning()
{
try
{
if ($this->Get('status') == 'running')
{
return self::ALREADY_RUNNING;
}
else
{
$this->Set('status', 'running');
$this->Set('started', time());
$this->DBUpdate();
return self::OK;
}
}
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();
}
$this->DBDelete();
return self::DELETED;
}
}
public function GetRetryDelay($iErrorCode = null)
{
$iRetryDelay = 600;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$iRetryDelay = $aConfig['retry_delay'];
}
return $iRetryDelay;
}
public function GetMaxRetries($iErrorCode = null)
{
$iMaxRetries = 0;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries', array());
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$iMaxRetries = $aConfig['max_retries'];
}
}
/**
* Override to notify people that a task cannot be performed
*/
protected function OnDefinitiveFailure()
{
// Display lists
// MetaModel::Init_SetZListItems('details', array()); // Attributes to be displayed for the complete details
// MetaModel::Init_SetZListItems('list', array()); // 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
}
protected function OnInsert()
@@ -182,118 +100,18 @@ abstract class AsyncTask extends DBObject
$this->Set('created', time());
}
/**
* @return boolean True if the task record can be deleted
*/
public function Process()
{
// By default: consider that the task is not completed
$bRet = false;
// Attempt to take the ownership
$iStatus = $this->MarkAsRunning();
if ($iStatus == self::OK)
{
try
{
$sStatus = $this->DoProcess();
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)
{
$this->HandleError($e->getMessage(), $e->getCode());
}
public function Process()
{
$sStatus = $this->DoProcess();
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', $sStatus);
$oEventLog->DBUpdate();
}
else
{
// Already done or being handled by another process... skip...
$bRet = false;
}
return $bRet;
}
/**
* Overridable to extend the behavior in case of error (logging)
*/
protected function HandleError($sErrorMessage, $iErrorCode)
{
if ($this->Get('last_attempt') == '')
{
// First attempt
$this->Set('remaining_retries', $this->GetMaxRetries($iErrorCode));
}
$this->Set('last_error', $sErrorMessage);
$this->Set('last_error_code', $iErrorCode); // Note: can be ZERO !!!
$this->Set('last_attempt', time());
$iRemaining = $this->Get('remaining_retries');
if ($iRemaining > 0)
{
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
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 '.$iRetryDelay.'s'");
try
{
$oEventLog->DBUpdate();
}
catch (Exception $e)
{
$oEventLog->Set('message', "Failed to process async task. Remaining retries: '.$iRemaining.'. Next retry in '.$iRetryDelay.'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() + $iRetryDelay);
}
else
{
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage);
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();
}
}
$this->Set('status', 'error');
$this->Set('started', null);
$this->Set('planned', null);
$this->OnDefinitiveFailure();
}
$this->DBUpdate();
}
/**
* Throws an exception (message and code)
*/
abstract public function DoProcess();
/**
* Describes the error codes that DoProcess can return by the mean of exceptions
*/
static public function EnumErrorCodes()
{
return array();
}
}
/**
@@ -320,8 +138,7 @@ class AsyncSendEmail extends AsyncTask
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("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>false, "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())));
@@ -343,10 +160,7 @@ class AsyncSendEmail extends AsyncTask
$oNew->Set('to', $oEMail->GetRecipientTO(true /* string */));
$oNew->Set('subject', $oEMail->GetSubject());
// $oNew->Set('version', 1);
// $sMessage = serialize($oEMail);
$oNew->Set('version', 2);
$sMessage = $oEMail->SerializeV2();
$sMessage = serialize($oEMail);
$oNew->Set('message', $sMessage);
$oNew->DBInsert();
}
@@ -354,20 +168,7 @@ class AsyncSendEmail extends AsyncTask
public function DoProcess()
{
$sMessage = $this->Get('message');
$iVersion = (int) $this->Get('version');
switch($iVersion)
{
case Email::FORMAT_V2:
$oEMail = Email::UnSerializeV2($sMessage);
break;
case Email::ORIGINAL_FORMAT:
$oEMail = unserialize($sMessage);
break;
default:
return 'Unknown version of the serialization format: '.$iVersion;
}
$oEMail = unserialize($sMessage);
$iRes = $oEMail->Send($aIssues, true /* force synchro !!!!! */);
switch ($iRes)
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,38 +0,0 @@
<?php
// Copyright (C) 2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
MetaModel::IncludeModule('application/transaction.class.inc.php');
MetaModel::IncludeModule('application/menunode.class.inc.php');
MetaModel::IncludeModule('application/user.preferences.class.inc.php');
MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
MetaModel::IncludeModule('application/audit.rule.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');
MetaModel::IncludeModule('core/bulkexport.class.inc.php');
MetaModel::IncludeModule('core/ownershiplock.class.inc.php');
MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php');
MetaModel::IncludeModule('core/backgroundtask.class.inc.php');
MetaModel::IncludeModule('core/inlineimage.class.inc.php');
MetaModel::IncludeModule('webservices/webservices.basic.php');
//MetaModel::IncludeModule('addons', 'user rights', 'addons/userrights/userrightsprofile.class.inc.php');

View File

@@ -1,55 +0,0 @@
<?php
// Copyright (C) 2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Tasks performed in the background
*
* @copyright Copyright (C) 2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ObsolescenceDateUpdater implements iBackgroundProcess
{
public function GetPeriodicity()
{
return MetaModel::GetConfig()->Get('obsolescence.date_update_interval'); // 10 mn
}
public function Process($iUnixTimeLimit)
{
$iCountSet = 0;
$iCountReset = 0;
$iClasses = 0;
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));
$oObsoletedToday = new DBObjectSearch($sClass);
$oObsoletedToday->AddCondition('obsolescence_flag', 1, '!=');
$oObsoletedToday->AddCondition('obsolescence_date', null, '!=');
$iCountReset += MetaModel::BulkUpdate($oObsoletedToday, array('obsolescence_date' => null));
}
return "Obsolescence date updated (classes: $iClasses ; set: $iCountSet ; reset: $iCountReset)\n";
}
}

View File

@@ -1,88 +1,33 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* interface iProcess
* Something that can be executed
* Class BackgroundProcess
* Any extension that must be called regularly to be executed in the background
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
interface iProcess
interface iBackgroundProcess
{
/**
* @param int $iUnixTimeLimit
*
* @return string status message
* @throws \ProcessException
* @throws \ProcessFatalException
* @throws MySQLHasGoneAwayException
*/
public function GetPeriodicity();
public function Process($iUnixTimeLimit);
}
/**
* interface iBackgroundProcess
* Any extension that must be called regularly to be executed in the background
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iBackgroundProcess extends iProcess
{
/**
* @return int repetition rate in seconds
*/
public function GetPeriodicity();
}
/**
* interface iScheduledProcess
* A variant of process that must be called at specific times
*
* @copyright Copyright (C) 2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iScheduledProcess extends iProcess
{
/**
* @return DateTime exact time at which the process must be run next time
*/
public function GetNextOccurrence();
}
/**
* Class ProcessException
* Exception for iProcess implementations.<br>
* An error happened during the processing but we can go on with the next implementations.
*/
class ProcessException extends CoreException
{
}
/**
* Class ProcessFatalException
* Exception for iProcess implementations.<br>
* A big error occurred, we have to stop the iProcess processing.
*/
class ProcessFatalException extends CoreException
{
}
?>

View File

@@ -1,76 +0,0 @@
<?php
// Copyright (C) 2013 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class BackgroundTask
* A class to record information about the execution of background processes
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class BackgroundTask extends DBObject
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "class_name",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_backgroundtask",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
);
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 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'), "sql"=>"status", "default_value"=>'active', "is_null_allowed"=>false, "depends_on"=>array())));
}
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));
}
if ($fLatestDuration > $this->Get('max_run_duration'))
{
$this->Set('max_run_duration', sprintf('%.3f',$fLatestDuration));
}
$this->Set('latest_run_duration', sprintf('%.3f',$fLatestDuration));
}
}

View File

@@ -1,34 +1,29 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Bulk change facility (common to interactive and batch usages)
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
// The BOM is added at the head of exported UTF-8 CSV data, and removed (if present) from input UTF-8 data.
// This helps MS-Excel (Version > 2007, Windows only) in changing its interpretation of a CSV file (by default Excel reads data as ISO-8859-1 -not 100% sure!)
define('UTF8_BOM', chr(239).chr(187).chr(191)); // 0xEF, 0xBB, 0xBF
/**
* BulkChange
* Interpret a given data set and update the DB accordingly (fake mode avail.)
@@ -89,16 +84,15 @@ class CellStatus_Modify extends CellChangeSpec
{
protected $m_previousValue;
public function __construct($proposedValue, $previousValue = null)
public function __construct($proposedValue, $previousValue)
{
// Unused (could be costly to know -see the case of reconciliation on ext keys)
//$this->m_previousValue = $previousValue;
$this->m_previousValue = $previousValue;
parent::__construct($proposedValue);
}
public function GetDescription()
{
return Dict::S('UI:CSVReport-Value-Modified');
return 'Modified';
}
//public function GetPreviousValue()
@@ -121,9 +115,9 @@ class CellStatus_Issue extends CellStatus_Modify
{
if (is_null($this->m_proposedValue))
{
return Dict::Format('UI:CSVReport-Value-SetIssue', $this->m_sReason);
return 'Could not be changed - reason: '.$this->m_sReason;
}
return Dict::Format('UI:CSVReport-Value-ChangeIssue', $this->m_proposedValue, $this->m_sReason);
return 'Could not be changed to '.$this->m_proposedValue.' - reason: '.$this->m_sReason;
}
}
@@ -136,7 +130,7 @@ class CellStatus_SearchIssue extends CellStatus_Issue
public function GetDescription()
{
return Dict::S('UI:CSVReport-Value-NoMatch');
return 'No match';
}
}
@@ -149,7 +143,7 @@ class CellStatus_NullIssue extends CellStatus_Issue
public function GetDescription()
{
return Dict::S('UI:CSVReport-Value-Missing');
return 'Missing mandatory value';
}
}
@@ -168,7 +162,7 @@ class CellStatus_Ambiguous extends CellStatus_Issue
public function GetDescription()
{
$sCount = $this->m_iCount;
return Dict::Format('UI:CSVReport-Value-Ambiguous', $sCount);
return "Ambiguous: found $sCount objects";
}
}
@@ -192,7 +186,7 @@ class RowStatus_NoChange extends RowStatus
{
public function GetDescription()
{
return Dict::S('UI:CSVReport-Row-Unchanged');
return "unchanged";
}
}
@@ -200,7 +194,7 @@ class RowStatus_NewObj extends RowStatus
{
public function GetDescription()
{
return Dict::S('UI:CSVReport-Row-Created');
return "created";
}
}
@@ -215,7 +209,7 @@ class RowStatus_Modify extends RowStatus
public function GetDescription()
{
return Dict::Format('UI:CSVReport-Row-Updated', $this->m_iChanged);
return "updated ".$this->m_iChanged." cols";
}
}
@@ -223,7 +217,7 @@ class RowStatus_Disappeared extends RowStatus_Modify
{
public function GetDescription()
{
return Dict::Format('UI:CSVReport-Row-Disappeared', $this->m_iChanged);
return "disappeared, changed ".$this->m_iChanged." cols";
}
}
@@ -238,7 +232,7 @@ class RowStatus_Issue extends RowStatus
public function GetDescription()
{
return Dict::Format('UI:CSVReport-Row-Issue', $this->m_sReason);
return 'Issue: '.$this->m_sReason;
}
}
@@ -258,11 +252,9 @@ class BulkChange
protected $m_aReconcilKeys; // attcode (attcode = 'id' for the pkey)
protected $m_sSynchroScope; // OQL - if specified, then the missing items will be reported
protected $m_aOnDisappear; // array of attcode => value, values to be set when an object gets out of scope (ignored if no scope has been defined)
protected $m_sDateFormat; // Date format specification, see DateTime::createFromFormat
protected $m_bLocalizedValues; // Values in the data set are localized (see AttributeEnum)
protected $m_aExtKeysMappingCache; // Cache for resolving external keys based on the given search criterias
protected $m_sDateFormat; // Date format specification, see utils::StringToTime()
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false)
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null)
{
$this->m_sClass = $sClass;
$this->m_aData = $aData;
@@ -272,8 +264,6 @@ class BulkChange
$this->m_sSynchroScope = $sSynchroScope;
$this->m_aOnDisappear = $aOnDisappear;
$this->m_sDateFormat = $sDateFormat;
$this->m_bLocalizedValues = $bLocalize;
$this->m_aExtKeysMappingCache = array();
}
protected $m_bReportHtml = false;
@@ -295,20 +285,11 @@ class BulkChange
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
{
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
{
if ($sForeignAttCode == 'id')
{
$value = (int) $aRowData[$iCol];
}
else
{
// The foreign attribute is one of our reconciliation key
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
// The foreign attribute is one of our reconciliation key
$oReconFilter->AddCondition($sForeignAttCode, $aRowData[$iCol], '=');
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
@@ -320,7 +301,7 @@ class BulkChange
// Returns true if the CSV data specifies that the external key must be left undefined
protected function IsNullExternalKeySpec($aRowData, $sAttCode)
{
//$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
{
// The foreign attribute is one of our reconciliation key
@@ -350,7 +331,6 @@ class BulkChange
{
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
// Default reporting
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
if ($oExtKey->IsNullAllowed())
@@ -360,80 +340,34 @@ class BulkChange
}
else
{
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-Null');
$aResults[$sAttCode]= new CellStatus_Issue(null, $oTargetObj->Get($sAttCode), Dict::S('UI:CSVReport-Value-Issue-Null'));
$aErrors[$sAttCode] = "Null not allowed";
$aResults[$sAttCode]= new CellStatus_Issue(null, $oTargetObj->Get($sAttCode), 'Null not allowed');
}
}
else
{
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
$aCacheKeys = array();
$oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass());
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
// The foreign attribute is one of our reconciliation key
if ($sForeignAttCode == 'id')
{
$value = $aRowData[$iCol];
}
else
{
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
$aCacheKeys[] = $value;
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
$oReconFilter->AddCondition($sForeignAttCode, $aRowData[$iCol], '=');
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
$iForeignKey = null;
$sOQL = '';
// TODO: check if *too long* keys can lead to collisions... and skip the cache in such a case...
if (!array_key_exists($sAttCode, $this->m_aExtKeysMappingCache))
$oExtObjects = new CMDBObjectSet($oReconFilter);
switch($oExtObjects->Count())
{
$this->m_aExtKeysMappingCache[$sAttCode] = array();
}
if (array_key_exists($sCacheKey, $this->m_aExtKeysMappingCache[$sAttCode]))
{
// Cache hit
$iCount = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['c'];
$iForeignKey = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['k'];
$sOQL = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['oql'];
// Record the hit
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['h']++;
}
else
{
// Cache miss, let's initialize it
$oExtObjects = new CMDBObjectSet($oReconFilter);
$iCount = $oExtObjects->Count();
if ($iCount == 1)
{
$oForeignObj = $oExtObjects->Fetch();
$iForeignKey = $oForeignObj->GetKey();
}
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey] = array(
'c' => $iCount,
'k' => $iForeignKey,
'oql' => $oReconFilter->ToOql(),
'h' => 0, // number of hits on this cache entry
);
}
switch($iCount)
{
case 0:
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-NotFound');
case 0:
$aErrors[$sAttCode] = "Object not found";
$aResults[$sAttCode]= new CellStatus_SearchIssue();
break;
case 1:
case 1:
// Do change the external key attribute
$oTargetObj->Set($sAttCode, $iForeignKey);
$oForeignObj = $oExtObjects->Fetch();
$oTargetObj->Set($sAttCode, $oForeignObj->GetKey());
break;
default:
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iCount);
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iCount, $sOQL);
default:
$aErrors[$sAttCode] = "Found ".$oExtObjects->Count()." matches";
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $oExtObjects->Count(), $oReconFilter->ToOql());
}
}
@@ -450,11 +384,6 @@ class BulkChange
else
{
$aResults[$sAttCode]= new CellStatus_Modify($iForeignObj, $oTargetObj->GetOriginal($sAttCode));
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
// Report the change on reconciliation values as well
$aResults[$iCol] = new CellStatus_Modify($aRowData[$iCol]);
}
}
}
else
@@ -472,47 +401,35 @@ class BulkChange
if ($sAttCode == 'id') continue;
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
// skip reconciliation keys
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)){ continue; }
$aReasons = array();
$iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
{
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
$aErrors[$sAttCode] = "the attribute '$sAttCode' is read-only and cannot be modified (current value: ".$oTargetObj->Get($sAttCode).", proposed value: {$aRowData[$iCol]}).";
}
else if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
{
try
{
$oSet = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
$oSet = $oAttDef->MakeValueFromString($aRowData[$iCol]);
$oTargetObj->Set($sAttCode, $oSet);
}
catch(CoreException $e)
{
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Format', $e->getMessage());
$aErrors[$sAttCode] = "Failed to process input: ".$e->getMessage();
}
}
else
{
$value = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
if (is_null($value) && (strlen($aRowData[$iCol]) > 0))
$res = $oTargetObj->CheckValue($sAttCode, $aRowData[$iCol]);
if ($res === true)
{
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-NoMatch', $sAttCode);
$oTargetObj->Set($sAttCode, $aRowData[$iCol]);
}
else
{
$res = $oTargetObj->CheckValue($sAttCode, $value);
if ($res === true)
{
$oTargetObj->Set($sAttCode, $value);
}
else
{
// $res is a string with the error description
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Unknown', $sAttCode, $res);
}
// $res is a string with the error description
$aErrors[$sAttCode] = "Unexpected value for attribute '$sAttCode': $res";
}
}
}
@@ -530,17 +447,17 @@ class BulkChange
{
if ($this->m_bReportHtml)
{
$sCurValue = $oTargetObj->GetAsHTML($sAttCode, $this->m_bLocalizedValues);
$sOrigValue = $oTargetObj->GetOriginalAsHTML($sAttCode, $this->m_bLocalizedValues);
$sCurValue = $oTargetObj->GetAsHTML($sAttCode);
$sOrigValue = $oTargetObj->GetOriginalAsHTML($sAttCode);
}
else
{
$sCurValue = $oTargetObj->GetAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
$sCurValue = $oTargetObj->GetAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter);
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter);
}
if (isset($aErrors[$sAttCode]))
{
$aResults[$iCol]= new CellStatus_Issue($aRowData[$iCol], $sOrigValue, $aErrors[$sAttCode]);
$aResults[$iCol]= new CellStatus_Issue($sCurValue, $sOrigValue, $aErrors[$sAttCode]);
}
elseif (array_key_exists($sAttCode, $aChangedFields))
{
@@ -556,15 +473,7 @@ class BulkChange
else
{
// By default... nothing happens
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oAttDef instanceof AttributeDateTime)
{
$aResults[$iCol]= new CellStatus_Void($oAttDef->GetFormat()->Format($aRowData[$iCol]));
}
else
{
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
}
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
}
}
}
@@ -575,7 +484,7 @@ class BulkChange
if ($res !== true)
{
// $res contains the error description
$aErrors["GLOBAL"] = Dict::Format('UI:CSVReport-Row-Issue-Inconsistent', $res);
$aErrors["GLOBAL"] = "Attributes not consistent with each others: $res";
}
return $aResults;
}
@@ -607,6 +516,10 @@ class BulkChange
throw new BulkChangeException('Invalid attribute code', array('class' => get_class($oTargetObj), 'attcode' => $sAttCode));
}
$oTargetObj->Set($sAttCode, $value);
if (!array_key_exists($sAttCode, $this->m_aAttList))
{
// #@# will be out of the reporting... (counted anyway)
}
}
// Reporting on fields
@@ -635,7 +548,7 @@ class BulkChange
if ($res !== true)
{
// $res contains the error description
$aErrors["GLOBAL"] = Dict::Format('UI:CSVReport-Row-Issue-Inconsistent', $res);
$aErrors["GLOBAL"] = "Attributes not consistent with each others: $res";
}
return $aResults;
}
@@ -644,53 +557,12 @@ class BulkChange
protected function CreateObject(&$aResult, $iRow, $aRowData, CMDBChange $oChange = null)
{
$oTargetObj = MetaModel::NewObject($this->m_sClass);
// Populate the cache for hierarchical keys (only if in verify mode)
if (is_null($oChange))
{
// 1. determine if a hierarchical key exists
foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
{
$oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode);
if (!$this->IsNullExternalKeySpec($aRowData, $sAttCode) && MetaModel::IsParentClass(get_class($oTargetObj), $this->m_sClass))
{
// 2. Populate the cache for further checks
$aCacheKeys = array();
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
{
// The foreign attribute is one of our reconciliation key
if ($sForeignAttCode == 'id')
{
$value = $aRowData[$iCol];
}
else
{
if (!isset($this->m_aAttList[$sForeignAttCode]) || !isset($aRowData[$this->m_aAttList[$sForeignAttCode]]))
{
// the key is not in the import
break 2;
}
$value = $aRowData[$this->m_aAttList[$sForeignAttCode]];
}
$aCacheKeys[] = $value;
}
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey] = array(
'c' => 1,
'k' => -1,
'oql' => '',
'h' => 0, // number of hits on this cache entry
);
}
}
}
$aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors);
if (count($aErrors) > 0)
{
$sErrors = implode(', ', $aErrors);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)");
return $oTargetObj;
}
@@ -709,7 +581,7 @@ class BulkChange
if (count($aMissingKeys) > 0)
{
$sMissingKeys = implode(', ', $aMissingKeys);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-MissingExtKey', $sMissingKeys));
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Could not be created, due to missing external key(s): $sMissingKeys");
return $oTargetObj;
}
@@ -718,7 +590,7 @@ class BulkChange
if ($oChange)
{
$newID = $oTargetObj->DBInsertTrackedNoReload($oChange);
$aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj();
$aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj($this->m_sClass, $newID);
$aResult[$iRow]["finalclass"] = get_class($oTargetObj);
$aResult[$iRow]["id"] = new CellStatus_Void($newID);
}
@@ -743,7 +615,7 @@ class BulkChange
if (count($aErrors) > 0)
{
$sErrors = implode(', ', $aErrors);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)");
return;
}
@@ -784,7 +656,7 @@ class BulkChange
if (count($aErrors) > 0)
{
$sErrors = implode(', ', $aErrors);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)");
return;
}
@@ -842,55 +714,26 @@ class BulkChange
if (!is_null($this->m_sDateFormat) && (strlen($this->m_sDateFormat) > 0))
{
$sDateTimeFormat = $this->m_sDateFormat; // the specified format is actually the date AND time format
$oDateTimeFormat = new DateTimeFormat($sDateTimeFormat);
$sDateFormat = $oDateTimeFormat->ToDateFormat();
AttributeDateTime::SetFormat($oDateTimeFormat);
AttributeDate::SetFormat(new DateTimeFormat($sDateFormat));
// Translate dates from the source data
//
foreach ($this->m_aAttList as $sAttCode => $iCol)
{
if ($sAttCode == 'id') continue;
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
if ($oAttDef instanceof AttributeDateTime)
{
foreach($this->m_aData as $iRow => $aRowData)
{
$sFormat = $sDateTimeFormat;
$sValue = $this->m_aData[$iRow][$iCol];
if (!empty($sValue))
$sNewDate = utils::StringToTime($this->m_aData[$iRow][$iCol], $this->m_sDateFormat);
if ($sNewDate !== false)
{
if ($oAttDef instanceof AttributeDate)
{
$sFormat = $sDateFormat;
}
$oFormat = new DateTimeFormat($sFormat);
$sRegExp = $oFormat->ToRegExpr('/');
if (!preg_match($sRegExp, $this->m_aData[$iRow][$iCol]))
{
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
}
else
{
$oDate = DateTime::createFromFormat($sFormat, $this->m_aData[$iRow][$iCol]);
if ($oDate !== false)
{
$sNewDate = $oDate->format($oAttDef->GetInternalFormat());
$this->m_aData[$iRow][$iCol] = $sNewDate;
}
else
{
// Leave the cell unchanged
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, $this->m_aData[$iRow][$iCol], Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
}
}
// Todo - improve the reporting
$this->m_aData[$iRow][$iCol] = $sNewDate;
}
else
{
$this->m_aData[$iRow][$iCol] = '';
// Leave the cell unchanged
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("wrong date format");
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, $this->m_aData[$iRow][$iCol], 'Wrong date format');
}
}
}
@@ -903,116 +746,98 @@ class BulkChange
{
$aVisited = array();
}
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
foreach($this->m_aData as $iRow => $aRowData)
{
set_time_limit($iLoopTimeLimit);
if (isset($aResult[$iRow]["__STATUS__"]))
{
// An issue at the earlier steps - skip the rest
continue;
}
try
$oReconciliationFilter = new CMDBSearchFilter($this->m_sClass);
$bSkipQuery = false;
foreach($this->m_aReconcilKeys as $sAttCode)
{
$oReconciliationFilter = new DBObjectSearch($this->m_sClass);
$bSkipQuery = false;
foreach($this->m_aReconcilKeys as $sAttCode)
$valuecondition = null;
if (array_key_exists($sAttCode, $this->m_aExtKeys))
{
$valuecondition = null;
if (array_key_exists($sAttCode, $this->m_aExtKeys))
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
{
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oExtKey->IsNullAllowed())
{
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oExtKey->IsNullAllowed())
{
$valuecondition = $oExtKey->GetNullValue();
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
}
else
{
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
}
$valuecondition = $oExtKey->GetNullValue();
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
}
else
{
// The value has to be found or verified
list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
if (count($aMatches) == 1)
{
$oRemoteObj = reset($aMatches); // first item
$valuecondition = $oRemoteObj->GetKey();
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
}
elseif (count($aMatches) == 0)
{
$aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue();
}
else
{
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery);
}
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
}
}
else
{
// The value has to be found or verified
list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
if (count($aMatches) == 1)
{
$oRemoteObj = reset($aMatches); // first item
$valuecondition = $oRemoteObj->GetKey();
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
}
}
else
{
// The value is given in the data row
$iCol = $this->m_aAttList[$sAttCode];
if ($sAttCode == 'id')
elseif (count($aMatches) == 0)
{
$valuecondition = $aRowData[$iCol];
}
$aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue();
}
else
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$valuecondition = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery);
}
}
if (is_null($valuecondition))
{
$bSkipQuery = true;
}
else
{
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
}
}
if ($bSkipQuery)
{
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Reconciliation'));
}
}
else
{
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
switch($oReconciliationSet->Count())
{
case 0:
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
$aVisited[] = $oTargetObj->GetKey();
break;
case 1:
$oTargetObj = $oReconciliationSet->Fetch();
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
if (!is_null($this->m_sSynchroScope))
{
$aVisited[] = $oTargetObj->GetKey();
}
break;
default:
// Found several matches, ambiguous
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Ambiguous'));
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql());
$aResult[$iRow]["finalclass"]= 'n/a';
}
// The value is given in the data row
$iCol = $this->m_aAttList[$sAttCode];
$valuecondition = $aRowData[$iCol];
}
if (is_null($valuecondition))
{
$bSkipQuery = true;
}
else
{
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
}
}
catch (Exception $e)
if ($bSkipQuery)
{
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-Internal', get_class($e), $e->getMessage()));
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("failed to reconcile");
}
else
{
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
switch($oReconciliationSet->Count())
{
case 0:
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
$aVisited[] = $oTargetObj->GetKey();
break;
case 1:
$oTargetObj = $oReconciliationSet->Fetch();
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
if (!is_null($this->m_sSynchroScope))
{
$aVisited[] = $oTargetObj->GetKey();
}
break;
default:
// Found several matches, ambiguous
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("ambiguous reconciliation");
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql());
$aResult[$iRow]["finalclass"]= 'n/a';
}
}
}
@@ -1026,13 +851,11 @@ class BulkChange
$iObj = $oObj->GetKey();
if (!in_array($iObj, $aVisited))
{
set_time_limit($iLoopTimeLimit);
$iRow++;
$this->UpdateMissingObject($aResult, $iRow, $oObj, $oChange);
}
}
}
set_time_limit($iPreviousTimeLimit);
// Fill in the blanks - the result matrix is expected to be 100% complete
//
@@ -1076,17 +899,17 @@ class BulkChange
$oPage->add('<div id="'.$sAjaxDivId.'">');
}
$oPage->p(Dict::S('UI:History:BulkImports+').' <span id="csv_history_reload"></span>');
$oPage->p(Dict::S('UI:History:BulkImports+'));
$oBulkChangeSearch = DBObjectSearch::FromOQL("SELECT CMDBChange WHERE origin IN ('csv-interactive', 'csv-import.php')");
$oBulkChangeSearch = DBObjectSearch::FromOQL("SELECT CMDBChange WHERE userinfo LIKE '%(CSV)'");
$iQueryLimit = $bShowAll ? 0 : appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
$iQueryLimit = $bShowAll ? 0 : MetaModel::GetConfig()->GetMaxDisplayLimit() + 1;
$oBulkChanges = new DBObjectSet($oBulkChangeSearch, array('date' => false), array(), null, $iQueryLimit);
$oAppContext = new ApplicationContext();
$bLimitExceeded = false;
if ($oBulkChanges->Count() > (appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit())))
if ($oBulkChanges->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit())
{
$bLimitExceeded = true;
if (!$bShowAll)
@@ -1144,6 +967,7 @@ class BulkChange
{
$aDetails[] = array('date' => $sDate, 'user' => $sUser, 'class' => $sClass, 'created' => $iCreated, 'modified' => $iModified);
}
}
$aConfig = array( 'date' => array('label' => Dict::S('UI:History:Date'), 'description' => Dict::S('UI:History:Date+')),
@@ -1163,7 +987,7 @@ class BulkChange
else
{
// Truncated list
$iMinDisplayLimit = appUserPreferences::GetPref('default_page_size', MetaModel::GetConfig()->GetMinDisplayLimit());
$iMinDisplayLimit = MetaModel::GetConfig()->GetMinDisplayLimit();
$sCollapsedLabel = Dict::Format('UI:TruncatedResults', $iMinDisplayLimit, $oBulkChanges->Count());
$sLinkLabel = Dict::S('UI:DisplayAll');
$oPage->add('<p>'.$sCollapsedLabel.'&nbsp;&nbsp;<a class="truncated" onclick="OnTruncatedHistoryToggle(true);">'.$sLinkLabel.'</p>');
@@ -1181,7 +1005,6 @@ EOF
<<<EOF
function OnTruncatedHistoryToggle(bShowAll)
{
$('#csv_history_reload').html('<img src="../images/indicator.gif"/>');
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
{
$('#$sAjaxDivId').html(data);
@@ -1210,9 +1033,6 @@ EOF
/**
* Display the details of an import
* @param iTopWebPage $oPage
* @param $iChange
* @throws Exception
*/
static function DisplayImportHistoryDetails(iTopWebPage $oPage, $iChange)
{
@@ -1252,7 +1072,7 @@ EOF
{
$sAttCode = $oOperation->Get('attcode');
if ((get_class($oOperation) == 'CMDBChangeOpSetAttributeScalar') || (get_class($oOperation) == 'CMDBChangeOpSetAttributeURL'))
if (get_class($oOperation) == 'CMDBChangeOpSetAttributeScalar')
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsExternalKey())
@@ -1346,5 +1166,50 @@ EOF
}
$oPage->table($aConfig, $aDetails);
}
/**
* Get the user friendly name for an 'extended' attribute code i.e 'name', becomes 'Name' and 'org_id->name' becomes 'Organization->Name'
* @param string $sClassName The name of the class
* @param string $sAttCodeEx Either an attribute code or ext_key_name->att_code
* @return string A user friendly format of the string: AttributeName or AttributeName->ExtAttributeName
*/
public static function GetFriendlyAttCodeName($sClassName, $sAttCodeEx)
{
$sFriendlyName = '';
if (preg_match('/(.+)->(.+)/', $sAttCodeEx, $aMatches) > 0)
{
$sAttribute = $aMatches[1];
$sField = $aMatches[2];
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttribute);
if ($oAttDef->IsExternalKey())
{
$sTargetClass = $oAttDef->GetTargetClass();
$oTargetAttDef = MetaModel::GetAttributeDef($sTargetClass, $sField);
$sFriendlyName = $oAttDef->GetLabel().'->'.$oTargetAttDef->GetLabel();
}
else
{
// hum, hum... should never happen, we'd better raise an exception
throw(new Exception(Dict::Format('UI:CSVImport:ErrorExtendedAttCode', $sAttCodeEx, $sAttribute, $sClassName)));
}
}
else
{
if ($sAttCodeEx == 'id')
{
$sFriendlyName = Dict::S('UI:CSVImport:idField');
}
else
{
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCodeEx);
$sFriendlyName = $oAttDef->GetLabel();
}
}
return $sFriendlyName;
}
}
?>

View File

@@ -1,482 +0,0 @@
<?php
// Copyright (C) 2015 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
class BulkExportException extends Exception
{
protected $sLocalizedMessage;
public function __construct($message, $sLocalizedMessage, $code = null, $previous = null)
{
parent::__construct($message, $code, $previous);
$this->sLocalizedMessage = $sLocalizedMessage;
}
public function GetLocalizedMessage()
{
return $this->sLocalizedMessage;
}
}
class BulkExportMissingParameterException extends BulkExportException
{
public function __construct($sFieldCode)
{
parent::__construct('Missing parameter: '.$sFieldCode, Dict::Format('Core:BulkExport:MissingParameter_Param', $sFieldCode));
}
}
/**
* Class BulkExport
*
* @copyright Copyright (C) 2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class BulkExportResult extends DBObject
{
public static function Init()
{
$aParams = array
(
"category" => 'core/cmdb',
"key_type" => 'autoincrement',
"name_attcode" => array('created'),
"state_attcode" => '',
"reconc_keys" => array(),
"db_table" => 'priv_bulk_export_result',
"db_key_field" => 'id',
"db_finalclass_field" => '',
"display_template" => '',
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("allowed_values"=>null, "sql"=>"user_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("chunk_size", array("allowed_values"=>null, "sql"=>"chunk_size", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("format", array("allowed_values"=>null, "sql"=>"format", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("temp_file_path", array("allowed_values"=>null, "sql"=>"temp_file_path", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("search", array("allowed_values"=>null, "sql"=>"search", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLongText("status_info", array("allowed_values"=>null, "sql"=>"status_info", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeBoolean("localize_output", array("allowed_values"=>null, "sql"=>"localize_output", "default_value"=>true, "is_null_allowed"=>true, "depends_on"=>array())));
}
/**
* @throws CoreUnexpectedValue
* @throws Exception
*/
public function ComputeValues()
{
$this->Set('user_id', UserRights::GetUserId());
}
}
/**
* Garbage collector for cleaning "old" export results from the database and the disk.
* This background process runs once per day and deletes the results of all exports which
* are older than one day.
*/
class BulkExportResultGC implements iBackgroundProcess
{
public function GetPeriodicity()
{
return 24*3600; // seconds
}
public function Process($iTimeLimit)
{
$sDateLimit = date(AttributeDateTime::GetSQLFormat(), time() - 24*3600); // Every BulkExportResult older than one day will be deleted
$sOQL = "SELECT BulkExportResult WHERE created < '$sDateLimit'";
$iProcessed = 0;
while (time() < $iTimeLimit)
{
// Next one ?
$oSet = new CMDBObjectSet(DBObjectSearch::FromOQL($sOQL), array('created' => true) /* order by*/, array(), null, 1 /* limit count */);
$oSet->OptimizeColumnLoad(array('temp_file_path'));
$oResult = $oSet->Fetch();
if (is_null($oResult))
{
// Nothing to be done
break;
}
$iProcessed++;
@unlink($oResult->Get('temp_file_path'));
utils::PushArchiveMode(false);
$oResult->DBDelete();
utils::PopArchiveMode();
}
return "Cleaned $iProcessed old export results(s).";
}
}
/**
* Class BulkExport
*
* @copyright Copyright (C) 2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class BulkExport
{
protected $oSearch;
protected $iChunkSize;
protected $sFormatCode;
protected $aStatusInfo;
protected $oBulkExportResult;
protected $sTmpFile;
protected $bLocalizeOutput;
public function __construct()
{
$this->oSearch = null;
$this->iChunkSize = 0;
$this->sFormatCode = null;
$this->aStatusInfo = array();
$this->oBulkExportResult = null;
$this->sTmpFile = '';
$this->bLocalizeOutput = false;
}
/**
* Find the first class capable of exporting the data in the given format
*
* @param string $sFormatCode The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
* @param DBSearch $oSearch The search/filter defining the set of objects to export or null when listing the supported formats
*
* @return BulkExport|null
* @throws ReflectionException
*/
static public function FindExporter($sFormatCode, $oSearch = null)
{
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubclassOf('BulkExport') && !$oRefClass->isAbstract())
{
$oBulkExporter = new $sPHPClass();
if ($oBulkExporter->IsFormatSupported($sFormatCode, $oSearch))
{
if ($oSearch)
{
$oBulkExporter->SetObjectList($oSearch);
}
return $oBulkExporter;
}
}
}
return null;
}
/**
* Find the exporter corresponding to the given persistent token
*
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
*
* @return iBulkExport|null
* @throws ArchivedObjectException
* @throws CoreException
* @throws ReflectionException
*/
static public function FindExporterFromToken($iPersistentToken = null)
{
$oBulkExporter = null;
$oInfo = MetaModel::GetObject('BulkExportResult', $iPersistentToken, false);
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
{
$sFormatCode = $oInfo->Get('format');
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
if ($oBulkExporter)
{
$oBulkExporter->SetFormat($sFormatCode);
$oBulkExporter->SetObjectList($oSearch);
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
$oBulkExporter->sTmpFile = $oInfo->Get('temp_file_path');
$oBulkExporter->oBulkExportResult = $oInfo;
}
}
return $oBulkExporter;
}
/**
* @param $data
* @throws Exception
*/
public function AppendToTmpFile($data)
{
if ($this->sTmpFile == '')
{
$this->sTmpFile = $this->MakeTmpFile($this->GetFileExtension());
}
$hFile = fopen($this->sTmpFile, 'ab');
if ($hFile !== false)
{
fwrite($hFile, $data);
fclose($hFile);
}
}
public function GetTmpFilePath()
{
return $this->sTmpFile;
}
/**
* Lists all possible export formats. The output is a hash array in the form: 'format_code' => 'localized format label'
* @return array :string
*/
static public function FindSupportedFormats()
{
$aSupportedFormats = array();
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->isSubClassOf('BulkExport') && !$oRefClass->isAbstract())
{
$oBulkExporter = new $sPHPClass;
$aFormats = $oBulkExporter->GetSupportedFormats();
$aSupportedFormats = array_merge($aSupportedFormats, $aFormats);
}
}
return $aSupportedFormats;
}
/**
* (non-PHPdoc)
* @see iBulkExport::SetChunkSize()
*/
public function SetChunkSize($iChunkSize)
{
$this->iChunkSize = $iChunkSize;
}
/**
* @param $bLocalizeOutput
*/
public function SetLocalizeOutput($bLocalizeOutput)
{
$this->bLocalizeOutput = $bLocalizeOutput;
}
/**
* (non-PHPdoc)
* @see iBulkExport::SetObjectList()
*/
public function SetObjectList(DBSearch $oSearch)
{
$this->oSearch = $oSearch;
}
public function SetFormat($sFormatCode)
{
$this->sFormatCode = $sFormatCode;
}
/**
* (non-PHPdoc)
* @see iBulkExport::IsFormatSupported()
*/
public function IsFormatSupported($sFormatCode, $oSearch = null)
{
return array_key_exists($sFormatCode, $this->GetSupportedFormats());
}
/**
* (non-PHPdoc)
* @see iBulkExport::GetSupportedFormats()
*/
public function GetSupportedFormats()
{
return array(); // return array('csv' => Dict::S('UI:ExportFormatCSV'));
}
public function SetHttpHeaders(WebPage $oPage)
{
}
/**
* @return string
*/
public function GetHeader()
{
return '';
}
abstract public function GetNextChunk(&$aStatus);
/**
* @return string
*/
public function GetFooter()
{
return '';
}
public function SaveState()
{
if ($this->oBulkExportResult === null)
{
$this->oBulkExportResult = new BulkExportResult();
$this->oBulkExportResult->Set('format', $this->sFormatCode);
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
$this->oBulkExportResult->Set('localize_output', $this->bLocalizeOutput);
}
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
utils::PushArchiveMode(false);
$ret = $this->oBulkExportResult->DBWrite();
utils::PopArchiveMode();
return $ret;
}
public function Cleanup()
{
if (($this->oBulkExportResult && (!$this->oBulkExportResult->IsNew())))
{
$sFilename = $this->oBulkExportResult->Get('temp_file_path');
if ($sFilename != '')
{
@unlink($sFilename);
}
utils::PushArchiveMode(false);
$this->oBulkExportResult->DBDelete();
utils::PopArchiveMode();
}
}
public function EnumFormParts()
{
return array();
}
public function DisplayFormPart(WebPage $oP, $sPartId)
{
}
public function DisplayUsage(Page $oP)
{
}
public function ReadParameters()
{
$this->bLocalizeOutput = !((bool)utils::ReadParam('no_localize', 0, true, 'integer'));
}
public function GetResultAsHtml()
{
}
public function GetRawResult()
{
}
/**
* @return string
*/
public function GetMimeType()
{
return '';
}
/**
* @return string
*/
public function GetFileExtension()
{
return '';
}
public function GetCharacterSet()
{
return 'UTF-8';
}
public function GetStatistics()
{
}
public function GetDownloadFileName()
{
return Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())).'.'.$this->GetFileExtension();
}
public function SetStatusInfo($aStatusInfo)
{
$this->aStatusInfo = $aStatusInfo;
}
public function GetStatusInfo()
{
return $this->aStatusInfo;
}
/**
* @param $sExtension
* @return string
* @throws Exception
*/
protected function MakeTmpFile($sExtension)
{
if(!is_dir(APPROOT."data/bulk_export"))
{
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(APPROOT."data/bulk_export"))
{
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
}
$iNum = rand();
do
{
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = APPROOT."data/bulk_export/$sToken.".$sExtension;
$hFile = @fopen($sFileName, 'x');
}
while($hFile === false);
fclose($hFile);
return $sFileName;
}
}
// The built-in exports
require_once(APPROOT.'core/tabularbulkexport.class.inc.php');
require_once(APPROOT.'core/htmlbulkexport.class.inc.php');
if (extension_loaded('gd'))
{
// PDF export - via TCPDF - requires GD
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
}
require_once(APPROOT.'core/csvbulkexport.class.inc.php');
require_once(APPROOT.'core/excelbulkexport.class.inc.php');
require_once(APPROOT.'core/spreadsheetbulkexport.class.inc.php');
require_once(APPROOT.'core/xmlbulkexport.class.inc.php');

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Persistent class (internal) cmdbChange
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
@@ -44,15 +43,11 @@ class CMDBChange extends DBObject
"db_table" => "priv_change",
"db_key_field" => "id",
"db_finalclass_field" => "",
'indexes' => array(
array('origin'),
)
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "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 AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum('interactive,csv-interactive,csv-import.php,webservice-soap,webservice-rest,synchro-data-source,email-processing,custom-extension'), "sql"=>"origin", "default_value"=>"interactive", "is_null_allowed"=>true, "depends_on"=>array())));
}
// Helper to keep track of the author of a given change,

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Persistent classes (internal) : cmdbChangeOp and derived
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
@@ -45,9 +44,6 @@ class CMDBChangeOp extends DBObject
"db_table" => "priv_changeop",
"db_key_field" => "id",
"db_finalclass_field" => "optype",
'indexes' => array(
array('objclass', 'objkey'),
)
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -55,7 +51,7 @@ class CMDBChangeOp extends DBObject
MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date")));
MetaModel::Init_AddAttribute(new AttributeExternalField("userinfo", array("allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"userinfo")));
MetaModel::Init_AddAttribute(new AttributeString("objclass", array("allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeObjectKey("objkey", array("allowed_values"=>null, "class_attcode"=>"objclass", "sql"=>"objkey", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("objkey", array("allowed_values"=>null, "sql"=>"objkey", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_SetZListItems('details', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details
@@ -68,19 +64,6 @@ class CMDBChangeOp extends DBObject
{
return '';
}
/**
* Safety net: in case the change is not given, let's guarantee that it will
* be set to the current ongoing change (or create a new one)
*/
protected function OnInsert()
{
if ($this->Get('change') <= 0)
{
$this->Set('change', CMDBObject::GetCurrentChange());
}
parent::OnInsert();
}
}
@@ -141,11 +124,6 @@ class CMDBChangeOpDelete extends CMDBChangeOp
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Final class of the object (objclass must be set to the root class for efficiency purposes)
MetaModel::Init_AddAttribute(new AttributeString("fclass", array("allowed_values"=>null, "sql"=>"fclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
// Last friendly name of the object
MetaModel::Init_AddAttribute(new AttributeString("fname", array("allowed_values"=>null, "sql"=>"fname", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
}
/**
* Describe (as a text string) the modifications corresponding to this change
@@ -237,62 +215,7 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
$sAttName = $oAttDef->GetLabel();
$sNewValue = $this->Get('newvalue');
$sOldValue = $this->Get('oldvalue');
$sResult = $oAttDef->DescribeChangeAsHTML($sOldValue, $sNewValue);
}
return $sResult;
}
}
/**
* Record the modification of an URL
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeURL extends CMDBChangeOpSetAttribute
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_url",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeURL("oldvalue", array("allowed_values"=>null, "sql"=>"oldvalue", "target" => '_blank', "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeURL("newvalue", array("allowed_values"=>null, "sql"=>"newvalue", "target" => '_blank', "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for a list
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sNewValue = $this->Get('newvalue');
$sOldValue = $this->Get('oldvalue');
$sResult = $oAttDef->DescribeChangeAsHTML($sOldValue, $sNewValue);
$sResult = $oAttDef->GetAsHTMLForHistory($sOldValue, $sNewValue);
}
return $sResult;
}
@@ -344,30 +267,14 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$oPrevDoc = $this->Get('prevdata');
if ($oPrevDoc->IsEmpty())
{
$sPrevious = '';
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevious);
}
else
{
$sDocView = $oPrevDoc->GetAsHtml();
$sDocView .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_', $oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata')).", \n";
$sDocView .= Dict::Format('UI:DownloadDocument_', $oPrevDoc->GetDownloadLink(get_class($this), $this->GetKey(), 'prevdata'))."\n";
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sDocView);
}
$sDocView = $oPrevDoc->GetAsHtml();
$sDocView .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',$oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata')).", \n";
$sDocView .= Dict::Format('UI:DownloadDocument_', $oPrevDoc->GetDownloadLink(get_class($this), $this->GetKey(), 'prevdata'))."\n";
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sDocView);
}
return $sResult;
}
@@ -416,16 +323,8 @@ class CMDBChangeOpSetAttributeOneWayPassword extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sResult = Dict::Format('Change:AttName_Changed', $sAttName);
}
return $sResult;
@@ -476,16 +375,8 @@ class CMDBChangeOpSetAttributeEncrypted extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sPrevString = $this->Get('prevstring');
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevString);
}
@@ -539,16 +430,8 @@ class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sTextView = '<div>'.$this->GetAsHtml('prevdata').'</div>';
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
@@ -558,128 +441,6 @@ class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute
}
}
/**
* Record the modification of a multiline string (text)
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeLongText extends CMDBChangeOpSetAttribute
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_longtext",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeLongText("prevdata", array("allowed_values"=>null, "sql"=>"prevdata", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$sTextView = '<div>'.$this->GetAsHtml('prevdata').'</div>';
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
}
return $sResult;
}
}
/**
* Record the modification of a multiline string (text) containing some HTML markup
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeHTML extends CMDBChangeOpSetAttributeLongText
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_html",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$sTextView = '<div class="history_entry history_entry_truncated"><div class="history_html_content">'.$this->Get('prevdata').'</div></div>';
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
}
return $sResult;
}
}
/**
* Record the modification of a caselog (text)
* since the caselog itself stores the history
@@ -729,30 +490,12 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
}
else
{
// The attribute was renamed or removed from the object ?
$sAttName = $this->Get('attcode');
}
$oObj = $oMonoObjectSet->Fetch();
$oCaseLog = $oObj->Get($this->Get('attcode'));
$iMaxVisibleLength = MetaModel::getConfig()->Get('max_history_case_log_entry_length', 0);
$sTextEntry = '<div class="history_entry history_entry_truncated"><div class="history_html_content">'.$oCaseLog->GetEntryAt($this->Get('lastentry')).'</div></div>';
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName, $sTextEntry);
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sResult = Dict::Format('Change:AttName_EntryAdded', $sAttName);
}
return $sResult;
}
protected function ToHtml($sRawText)
{
return str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sRawText, ENT_QUOTES, 'UTF-8'));
}
}
/**
@@ -792,251 +535,4 @@ class CMDBChangeOpPlugin extends CMDBChangeOp
return $this->Get('description');
}
}
/**
* Record added/removed objects from within a link set
*
* @package iTopORM
*/
abstract class CMDBChangeOpSetAttributeLinks extends CMDBChangeOpSetAttribute
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// Note: item class/id points to the link class itself in case of a direct link set (e.g. Server::interface_list => Interface)
// item class/id points to the remote class in case of a indirect link set (e.g. Server::contract_list => Contract)
MetaModel::Init_AddAttribute(new AttributeString("item_class", array("allowed_values"=>null, "sql"=>"item_class", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("item_id", array("allowed_values"=>null, "sql"=>"item_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
}
}
/**
* Record added/removed objects from within a link set
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeLinksAddRemove extends CMDBChangeOpSetAttributeLinks
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_addremove",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeEnum("type", array("allowed_values"=>new ValueSetEnum('added,removed'), "sql"=>"type", "default_value"=>"added", "is_null_allowed"=>false, "depends_on"=>array())));
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sItemDesc = MetaModel::GetHyperLink($this->Get('item_class'), $this->Get('item_id'));
$sResult = $sAttName.' - ';
switch ($this->Get('type'))
{
case 'added':
$sResult .= Dict::Format('Change:LinkSet:Added', $sItemDesc);
break;
case 'removed':
$sResult .= Dict::Format('Change:LinkSet:Removed', $sItemDesc);
break;
}
}
return $sResult;
}
}
/**
* Record attribute changes from within a link set
* A single record redirects to the modifications made within the same change
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeLinksTune extends CMDBChangeOpSetAttributeLinks
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_tune",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeInteger("link_id", array("allowed_values"=>null, "sql"=>"link_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
$sResult = '';
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
if (!MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) return ''; // Protects against renamed attributes...
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
$sLinkClass = $oAttDef->GetLinkedClass();
$aLinkClasses = MetaModel::EnumChildClasses($sLinkClass, ENUM_CHILD_CLASSES_ALL);
// Search for changes on the corresponding link
//
$oSearch = new DBObjectSearch('CMDBChangeOpSetAttribute');
$oSearch->AddCondition('change', $this->Get('change'), '=');
$oSearch->AddCondition('objkey', $this->Get('link_id'), '=');
if (count($aLinkClasses) == 1)
{
// Faster than the whole building of the expression below for just one value ??
$oSearch->AddCondition('objclass', $sLinkClass, '=');
}
else
{
$oField = new FieldExpression('objclass', $oSearch->GetClassAlias());
$sListExpr = '('.implode(', ', CMDBSource::Quote($aLinkClasses)).')';
$sOQLCondition = $oField->Render()." IN $sListExpr";
$oNewCondition = Expression::FromOQL($sOQLCondition);
$oSearch->AddConditionExpression($oNewCondition);
}
$oSet = new DBObjectSet($oSearch);
$aChanges = array();
while ($oChangeOp = $oSet->Fetch())
{
$aChanges[] = $oChangeOp->GetDescription();
}
if (count($aChanges) == 0)
{
return '';
}
$sItemDesc = MetaModel::GetHyperLink($this->Get('item_class'), $this->Get('item_id'));
$sResult = $sAttName.' - ';
$sResult .= Dict::Format('Change:LinkSet:Modified', $sItemDesc);
$sResult .= ' : '.implode(', ', $aChanges);
}
return $sResult;
}
}
/**
* Record the modification of custom fields
*
* @package iTopORM
*/
class CMDBChangeOpSetAttributeCustomFields extends CMDBChangeOpSetAttribute
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_custfields",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeLongText("prevdata", array("allowed_values"=>null, "sql"=>"prevdata", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list
}
/**
* Describe (as a text string) the modifications corresponding to this change
*/
public function GetDescription()
{
$sResult = '';
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
{
$oTargetObjectClass = $this->Get('objclass');
$oTargetObjectKey = $this->Get('objkey');
$oTargetSearch = new DBObjectSearch($oTargetObjectClass);
$oTargetSearch->AddCondition('id', $oTargetObjectKey, '=');
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
{
$aValues = json_decode($this->Get('prevdata'), true);
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
$sAttName = $oAttDef->GetLabel();
try
{
$oHandler = $oAttDef->GetHandler($aValues);
$sValueDesc = $oHandler->GetAsHTML($aValues);
}
catch (Exception $e)
{
$sValueDesc = 'Custom field error: '.htmlentities($e->getMessage(), ENT_QUOTES, 'UTF-8');
}
$sTextView = '<div>'.$sValueDesc.'</div>';
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
}
}
return $sResult;
}
}
?>

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class cmdbObject
*
* @copyright Copyright (C) 2010-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
@@ -46,20 +45,18 @@ require_once('stimulus.class.inc.php');
require_once('valuesetdef.class.inc.php');
require_once('MyHelpers.class.inc.php');
require_once('oql/expression.class.inc.php');
require_once('expression.class.inc.php');
require_once('cmdbsource.class.inc.php');
require_once('sqlquery.class.inc.php');
require_once('oql/oqlquery.class.inc.php');
require_once('oql/oqlexception.class.inc.php');
require_once('oql/oql-parser.php');
require_once('oql/oql-lexer.php');
require_once('oql/oqlinterpreter.class.inc.php');
require_once('cmdbsource.class.inc.php');
require_once('sqlquery.class.inc.php');
require_once('sqlobjectquery.class.inc.php');
require_once('sqlunionquery.class.inc.php');
require_once('dbobject.class.php');
require_once('dbsearch.class.php');
require_once('dbobjectsearch.class.php');
require_once('dbobjectset.class.php');
require_once('backgroundprocess.inc.php');
@@ -94,12 +91,8 @@ abstract class CMDBObject extends DBObject
protected $m_datUpdated;
// Note: this value is static, but that could be changed because it is sometimes a real issue (see update of interfaces / connected_to
protected static $m_oCurrChange = null;
protected static $m_sInfo = null; // null => the information is built in a standard way
protected static $m_sOrigin = null; // null => the origin is 'interactive'
/**
* Specify another change (this is mainly for backward compatibility)
*/
public static function SetCurrentChange(CMDBChange $oChange)
{
self::$m_oCurrChange = $oChange;
@@ -107,334 +100,42 @@ abstract class CMDBObject extends DBObject
//
// Todo: simplify the APIs and do not pass the current change as an argument anymore
// SetTrackInfo to be invoked in very few cases (UI.php, CSV import, Data synchro)
// SetCurrentChange is an alternative to SetTrackInfo (csv ?)
// SetCurrentChange to be invoked in very few cases (UI.php, CSV import, Data synchro)
// GetCurrentChange to be called ONCE (!) by CMDBChangeOp::OnInsert ($this->Set('change', ..GetCurrentChange())
// GetCurrentChange to create a default change if not already done in the current context
//
/**
* Get a change record (create it if not existing)
*/
public static function GetCurrentChange($bAutoCreate = true)
public static function GetCurrentChange()
{
if ($bAutoCreate && is_null(self::$m_oCurrChange))
{
self::CreateChange();
}
return self::$m_oCurrChange;
}
/**
* Override the additional information (defaulting to user name)
* A call to this verb should replace every occurence of
* $oMyChange = MetaModel::NewObject("CMDBChange");
* $oMyChange->Set("date", time());
* $oMyChange->Set("userinfo", 'this is done by ... for ...');
* $iChangeId = $oMyChange->DBInsert();
*/
public static function SetTrackInfo($sInfo)
{
self::$m_sInfo = $sInfo;
}
/**
* Provides information about the origin of the change
* @param $sOrigin String: one of: interactive, csv-interactive, csv-import.php, webservice-soap, webservice-rest, syncho-data-source, email-processing, custom-extension
*/
public static function SetTrackOrigin($sOrigin)
private function RecordObjCreation(CMDBChange $oChange)
{
self::$m_sOrigin = $sOrigin;
}
/**
* Get the additional information (defaulting to user name)
*/
protected static function GetTrackInfo()
{
if (is_null(self::$m_sInfo))
{
return CMDBChange::GetCurrentUserName();
}
else
{
return self::$m_sInfo;
}
}
/**
* Get the 'origin' information (defaulting to 'interactive')
*/
protected static function GetTrackOrigin()
{
if (is_null(self::$m_sOrigin))
{
return 'interactive';
}
else
{
return self::$m_sOrigin;
}
}
/**
* Create a standard change record (done here 99% of the time, and nearly once per page)
*/
protected static function CreateChange()
{
self::$m_oCurrChange = MetaModel::NewObject("CMDBChange");
self::$m_oCurrChange->Set("date", time());
self::$m_oCurrChange->Set("userinfo", self::GetTrackInfo());
self::$m_oCurrChange->Set("origin", self::GetTrackOrigin());
self::$m_oCurrChange->DBInsert();
}
protected function RecordObjCreation()
{
// Delete any existing change tracking about the current object (IDs can be reused due to InnoDb bug; see TRAC #886)
//
// 1 - remove the deletion record(s)
// Note that objclass contain the ROOT class
$oFilter = new DBObjectSearch('CMDBChangeOpDelete');
$oFilter->AddCondition('objclass', MetaModel::GetRootClass(get_class($this)), '=');
$oFilter->AddCondition('objkey', $this->GetKey(), '=');
MetaModel::PurgeData($oFilter);
// 2 - any other change tracking information left prior to 2.0.3 (when the purge of the history has been implemented in RecordObjDeletion
// In that case, objclass is the final class of the object
$oFilter = new DBObjectSearch('CMDBChangeOp');
$oFilter->AddCondition('objclass', get_class($this), '=');
$oFilter->AddCondition('objkey', $this->GetKey(), '=');
MetaModel::PurgeData($oFilter);
parent::RecordObjCreation();
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$iId = $oMyChangeOp->DBInsertNoReload();
}
protected function RecordObjDeletion($objkey)
private function RecordObjDeletion(CMDBChange $oChange, $objkey)
{
$sRootClass = MetaModel::GetRootClass(get_class($this));
// Delete any existing change tracking about the current object
$oFilter = new DBObjectSearch('CMDBChangeOp');
$oFilter->AddCondition('objclass', get_class($this), '=');
$oFilter->AddCondition('objkey', $objkey, '=');
MetaModel::PurgeData($oFilter);
parent::RecordObjDeletion($objkey);
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpDelete");
$oMyChangeOp->Set("objclass", MetaModel::GetRootClass(get_class($this)));
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $objkey);
$oMyChangeOp->Set("fclass", get_class($this));
$oMyChangeOp->Set("fname", substr($this->GetRawName(), 0, 255)); // Protect against very long friendly names
$iId = $oMyChangeOp->DBInsertNoReload();
}
/**
* @param $sAttCode
* @param $original Original value
* @param $value Current value
*/
protected function RecordAttChange($sAttCode, $original, $value)
private function RecordAttChanges(CMDBChange $oChange, array $aValues, array $aOrigValues)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef->IsExternalField()) return;
if ($oAttDef->IsLinkSet()) return;
if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) return;
if ($oAttDef instanceOf AttributeOneWayPassword)
{
// One Way encrypted passwords' history is stored -one way- encrypted
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (is_null($original))
{
$original = '';
}
$oMyChangeOp->Set("prev_pwd", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeEncryptedString)
{
// Encrypted string history is stored encrypted
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (is_null($original))
{
$original = '';
}
$oMyChangeOp->Set("prevstring", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeBlob)
{
// Data blobs
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (is_null($original))
{
$original = new ormDocument();
}
$oMyChangeOp->Set("prevdata", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeStopWatch)
{
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
//
foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
{
$item_value = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $value, $this);
$item_original = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $original, $this);
if ($item_value != $item_original)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
$oMyChangeOp->Set("oldvalue", $item_original);
$oMyChangeOp->Set("newvalue", $item_value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
}
}
elseif ($oAttDef instanceOf AttributeCaseLog)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeLongText)
{
// Data blobs
if ($oAttDef->GetFormat() == 'html')
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
}
else
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
}
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (!is_null($original) && ($original instanceof ormCaseLog))
{
$original = $original->GetText();
}
$oMyChangeOp->Set("prevdata", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeText)
{
// Data blobs
if ($oAttDef->GetFormat() == 'html')
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
}
else
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
}
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (!is_null($original) && ($original instanceof ormCaseLog))
{
$original = $original->GetText();
}
$oMyChangeOp->Set("prevdata", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeBoolean)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", $original ? 1 : 0);
$oMyChangeOp->Set("newvalue", $value ? 1 : 0);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeHierarchicalKey)
{
// Hierarchical keys
//
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", $original);
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeCustomFields)
{
// Custom fields
//
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCustomFields");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("prevdata", json_encode($original->GetValues()));
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeURL)
{
// URLs
//
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeURL");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", $original);
$oMyChangeOp->Set("newvalue", $value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
else
{
// Scalars
//
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", $original);
$oMyChangeOp->Set("newvalue", $value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
}
/**
* @param array $aValues
* @param array $aOrigValues
*/
protected function RecordAttChanges(array $aValues, array $aOrigValues)
{
parent::RecordAttChanges($aValues, $aOrigValues);
// $aValues is an array of $sAttCode => $value
//
foreach ($aValues as $sAttCode=> $value)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef->IsExternalField()) continue; // #@# temporary
if ($oAttDef->IsLinkSet()) continue; // #@# temporary
if (array_key_exists($sAttCode, $aOrigValues))
{
$original = $aOrigValues[$sAttCode];
@@ -443,7 +144,122 @@ abstract class CMDBObject extends DBObject
{
$original = null;
}
$this->RecordAttChange($sAttCode, $original, $value);
if ($oAttDef instanceOf AttributeOneWayPassword)
{
// One Way encrypted passwords' history is stored -one way- encrypted
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (is_null($original))
{
$original = '';
}
$oMyChangeOp->Set("prev_pwd", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeEncryptedString)
{
// Encrypted string history is stored encrypted
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (is_null($original))
{
$original = '';
}
$oMyChangeOp->Set("prevstring", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeBlob)
{
// Data blobs
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (is_null($original))
{
$original = new ormDocument();
}
$oMyChangeOp->Set("prevdata", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeStopWatch)
{
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
//
if (is_null($original))
{
$original = new OrmStopWatch();
}
foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
{
$item_value = $oSubItemAttDef->GetValue($value);
$item_original = $oSubItemAttDef->GetValue($original);
if ($item_value != $item_original)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
$oMyChangeOp->Set("oldvalue", $item_original);
$oMyChangeOp->Set("newvalue", $item_value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
}
}
elseif ($oAttDef instanceOf AttributeCaseLog)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeText)
{
// Data blobs
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
if (!is_null($original) && ($original instanceof ormCaseLog))
{
$original = $original->GetText();
}
$oMyChangeOp->Set("prevdata", $original);
$iId = $oMyChangeOp->DBInsertNoReload();
}
else
{
// Scalars
//
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($this));
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", $original);
$oMyChangeOp->Set("newvalue", $value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
}
}
@@ -476,51 +292,66 @@ abstract class CMDBObject extends DBObject
}
public function DBInsert()
{
if(!is_object(self::$m_oCurrChange))
{
throw new CoreException("DBInsert() could not be used here, please use DBInsertTracked() instead");
}
return $this->DBInsertTracked_Internal();
}
public function DBInsertTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$ret = $this->DBInsertTracked_Internal();
self::$m_oCurrChange = $oPreviousChange;
return $ret;
}
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$ret = $this->DBInsertTracked_Internal(true);
self::$m_oCurrChange = $oPreviousChange;
return $ret;
}
/**
* To Be Obsoleted: DO NOT rely on an overload of this method since
* DBInsertTracked (resp. DBInsertTrackedNoReload) may call directly
* DBInsert (resp. DBInsertNoReload) in future versions of iTop.
* @param bool $bDoNotReload
* @return integer Identifier of the created object
*/
protected function DBInsertTracked_Internal($bDoNotReload = false)
{
if ($bDoNotReload)
{
$ret = $this->DBInsertNoReload();
$ret = parent::DBInsertNoReload();
}
else
{
$ret = $this->DBInsert();
$ret = parent::DBInsert();
}
$this->RecordObjCreation(self::$m_oCurrChange);
return $ret;
}
public function DBClone($newKey = null)
{
if(!self::$m_oCurrChange)
{
throw new CoreException("DBClone() could not be used here, please use DBCloneTracked() instead");
}
return $this->DBCloneTracked_Internal();
}
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
{
self::SetCurrentChange($oChange);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->DBCloneTracked_Internal($newKey);
self::$m_oCurrChange = $oPreviousChange;
}
protected function DBCloneTracked_Internal($newKey = null)
@@ -528,60 +359,131 @@ abstract class CMDBObject extends DBObject
$newKey = parent::DBClone($newKey);
$oClone = MetaModel::GetObject(get_class($this), $newKey);
$oClone->RecordObjCreation(self::$m_oCurrChange);
return $newKey;
}
public function DBUpdate()
{
// Copy the changes list before the update (the list should be reset afterwards)
$aChanges = $this->ListChanges();
if (count($aChanges) == 0)
if(!self::$m_oCurrChange)
{
return;
throw new CoreException("DBUpdate() could not be used here, please use DBUpdateTracked() instead");
}
$ret = parent::DBUpdate();
return $ret;
return $this->DBUpdateTracked_internal();
}
public function DBUpdateTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
$this->DBUpdate();
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->DBUpdateTracked_Internal();
self::$m_oCurrChange = $oPreviousChange;
}
protected function DBUpdateTracked_Internal()
{
// Copy the changes list before the update (the list should be reset afterwards)
$aChanges = $this->ListChanges();
if (count($aChanges) == 0)
{
//throw new CoreWarning("Attempting to update an unchanged object");
return;
}
// Save the original values (will be reset to the new values when the object get written to the DB)
$aOriginalValues = $this->m_aOrigValues;
$ret = parent::DBUpdate();
$this->RecordAttChanges(self::$m_oCurrChange, $aChanges, $aOriginalValues);
return $ret;
}
public function DBDelete(&$oDeletionPlan = null)
{
if(!self::$m_oCurrChange)
{
throw new CoreException("DBDelete() could not be used here, please use DBDeleteTracked() instead");
}
return $this->DBDeleteTracked_Internal($oDeletionPlan);
}
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
{
self::SetCurrentChange($oChange);
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->DBDeleteTracked_Internal($oDeletionPlan);
self::$m_oCurrChange = $oPreviousChange;
}
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
{
$prevkey = $this->GetKey();
$ret = parent::DBDelete($oDeletionPlan);
$this->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
return $ret;
}
public static function BulkUpdate(DBSearch $oFilter, array $aValues)
public static function BulkDelete(DBObjectSearch $oFilter)
{
return static::BulkUpdateTracked_Internal($oFilter, $aValues);
if(!self::$m_oCurrChange)
{
throw new CoreException("BulkDelete() could not be used here, please use BulkDeleteTracked() instead");
}
return $this->BulkDeleteTracked_Internal($oFilter);
}
public static function BulkUpdateTracked(CMDBChange $oChange, DBSearch $oFilter, array $aValues)
public static function BulkDeleteTracked(CMDBChange $oChange, DBObjectSearch $oFilter)
{
self::SetCurrentChange($oChange);
static::BulkUpdateTracked_Internal($oFilter, $aValues);
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->BulkDeleteTracked_Internal($oFilter);
self::$m_oCurrChange = $oPreviousChange;
}
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter)
{
throw new CoreWarning("Change tracking not tested for bulk operations");
// Get the list of objects to delete (and record data before deleting the DB records)
$oObjSet = new CMDBObjectSet($oFilter);
$aObjAndKeys = array(); // array of id=>object
while ($oItem = $oObjSet->Fetch())
{
$aObjAndKeys[$oItem->GetKey()] = $oItem;
}
$oObjSet->FreeResult();
// Delete in one single efficient query
$ret = parent::BulkDelete($oFilter);
// Record... in many queries !!!
foreach($aObjAndKeys as $prevkey=>$oItem)
{
$oItem->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
}
return $ret;
}
public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues)
{
if(!self::$m_oCurrChange)
{
throw new CoreException("BulkUpdate() could not be used here, please use BulkUpdateTracked() instead");
}
return $this->BulkUpdateTracked_Internal($oFilter, $aValues);
}
public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues)
{
$oPreviousChange = self::$m_oCurrChange;
self::$m_oCurrChange = $oChange;
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
self::$m_oCurrChange = $oPreviousChange;
}
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
{
// $aValues is an array of $sAttCode => $value
@@ -605,38 +507,10 @@ abstract class CMDBObject extends DBObject
while ($oItem = $oObjSet->Fetch())
{
$aChangedValues = $oItem->ListChangedValues($aValues);
$oItem->RecordAttChanges($aChangedValues, $aOriginalValues[$oItem->GetKey()]);
$oItem->RecordAttChanges(self::$m_oCurrChange, $aChangedValues, $aOriginalValues[$oItem->GetKey()]);
}
return $ret;
}
public function DBArchive()
{
// Note: do the job anyway, so as to repair any DB discrepancy
$bOriginal = $this->Get('archive_flag');
parent::DBArchive();
if (!$bOriginal)
{
utils::PushArchiveMode(false);
$this->RecordAttChange('archive_flag', false, true);
utils::PopArchiveMode();
}
}
public function DBUnarchive()
{
// Note: do the job anyway, so as to repair any DB discrepancy
$bOriginal = $this->Get('archive_flag');
parent::DBUnarchive();
if ($bOriginal)
{
utils::PushArchiveMode(false);
$this->RecordAttChange('archive_flag', true, false);
utils::PopArchiveMode();
}
}
}
@@ -656,7 +530,7 @@ class CMDBObjectSet extends DBObjectSet
static public function FromScratch($sClass)
{
$oFilter = new DBObjectSearch($sClass);
$oFilter = new CMDBSearchFilter($sClass);
$oFilter->AddConditionExpression(new FalseExpression());
$oRetSet = new self($oFilter);
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private in the base class (and you will not get any error message)
@@ -680,7 +554,7 @@ class CMDBObjectSet extends DBObjectSet
// let's create one search definition
$sClass = reset($aClasses);
$sAlias = key($aClasses);
$oFilter = new DBObjectSearch($sClass, $sAlias);
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
$oRetSet = new CMDBObjectSet($oFilter);
$oRetSet->m_bLoaded = true; // no DB load
@@ -690,5 +564,18 @@ class CMDBObjectSet extends DBObjectSet
$oRetSet->AddObjectExtended($aObjectsByClassAlias);
}
return $oRetSet;
}
}
}
/**
* TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design!
*
* @package iTopORM
*/
class CMDBSearchFilter extends DBObjectSearch
{
// this is the public interface (?)
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2014 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Any extension to compute things like a stop watch deadline or working hours
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
/**
@@ -96,19 +95,11 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
*/
public function GetDeadline($oObject, $iDuration, DateTime $oStartDate)
{
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";
// Default implementation: 24x7, no holidays: to compute the deadline, just add
// the specified duration to the given date/time
$oResult = clone $oStartDate;
$oResult->modify('+'.$iDuration.' seconds');
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oResult->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_END);
}
return $oResult;
}
@@ -121,17 +112,8 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
*/
public function GetOpenDuration($oObject, DateTime $oStartDate, DateTime $oEndDate)
{
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'))
{
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oEndDate->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_DURATION);
}
return $iDuration;
return abs($oEndDate->format('U') - $oStartDate->format('U'));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,75 +0,0 @@
<?php
// Copyright (C) 2016-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Simple helper class for keeping track of the context inside the call stack
*
* To check (anywhere in the code) if a particular context tag is present
* in the call stack simply do:
*
* if (ContextTag::Check(<the_tag>)) ...
*
* For example to know if the code is being executed in the context of a portal do:
*
* if (ContextTag::Check('GUI:Portal'))
*
* @copyright Copyright (C) 2016-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ContextTag
{
protected static $aStack = array();
/**
* Store a context tag on the stack
* @param string $sTag
*/
public function __construct($sTag)
{
static::$aStack[] = $sTag;
}
/**
* Cleanup the context stack
*/
public function __destruct()
{
array_pop(static::$aStack);
}
/**
* Check if a given tag is present in the stack
* @param string $sTag
* @return bool
*/
public static function Check($sTag)
{
return in_array($sTag, static::$aStack);
}
/**
* Get the whole stack as an array
* @return hash
*/
public static function GetStack()
{
return static::$aStack;
}
}

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Exception management
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
@@ -69,13 +68,6 @@ class CoreException extends Exception
parent::__construct($sMessage, 0);
}
/**
* @return string code and message for log purposes
*/
public function getInfoLog()
{
return 'error_code='.$this->getCode().', message="'.$this->getMessage().'"';
}
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
{
return $this->getMessage();
@@ -119,13 +111,4 @@ class SecurityException extends CoreException
{
}
/**
* Throwned when querying on an object that exists in the database but is archived
*
* @see N.1108
*/
class ArchivedObjectException extends CoreException
{
}
?>

View File

@@ -1,381 +0,0 @@
<?php
// Copyright (C) 2015-2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Bulk export: CSV export
*
* @copyright Copyright (C) 2015-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class CSVBulkExport extends TabularBulkExport
{
public function DisplayUsage(Page $oP)
{
$oP->p(" * csv format options:");
$oP->p(" *\tfields: (mandatory) the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
$oP->p(" *\tseparator: (optional) character to be used as the separator (default is ',').");
$oP->p(" *\tcharset: (optional) character set for encoding the result (default is 'UTF-8').");
$oP->p(" *\ttext-qualifier: (optional) character to be used around text strings (default is '\"').");
$oP->p(" *\tno_localize: set to 1 to retrieve non-localized values (for instance for ENUM values). Default is 0 (= localized values)");
$oP->p(" *\tformatted_text: set to 1 to export case logs and formatted text fields with their HTML markup. Default is 0 (= plain text)");
$oP->p(" *\tdate_format: the format to use when exporting date and time fields (default = the SQL format used in the user interface). e.g. 'Y-m-d H:i:s'");
}
public function ReadParameters()
{
parent::ReadParameters();
$this->aStatusInfo['separator'] = utils::ReadParam('separator', ',', true, 'raw_data');
if (strtolower($this->aStatusInfo['separator']) == 'tab')
{
$this->aStatusInfo['separator'] = "\t";
}
else if (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')
{
$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)
{
case 'default':
// 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;
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');
}
}
protected function SuggestField($sClass, $sAttCode)
{
switch($sAttCode)
{
case 'id': // replace 'id' by 'friendlyname'
$sAttCode = 'friendlyname';
break;
default:
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef instanceof AttributeExternalKey)
{
$sAttCode .= '_friendlyname';
}
}
return parent::SuggestField($sClass, $sAttCode);
}
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')));
}
public function DisplayFormPart(WebPage $oP, $sPartId)
{
switch($sPartId)
{
case 'interactive_fields_csv':
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_csv');
break;
case 'csv_options':
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:CSVOptions').'</legend>');
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
$oP->add('<h3>'.Dict::S('UI:CSVImport:SeparatorCharacter').'</h3>');
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');
$sCustomDateTimeFormat = utils::ReadParam('', ',', true, 'raw_data');
$aSep = array(
';' => 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;
$sRawSeparator = 'other';
}
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.htmlentities($sOtherSeparator, ENT_QUOTES, 'UTF-8').'"/>';
foreach($aSep as $sVal => $sLabel)
{
$sChecked = ($sVal == $sRawSeparator) ? 'checked' : '';
$oP->add('<input type="radio" name="separator" value="'.htmlentities($sVal, ENT_QUOTES, 'UTF-8').'" '.$sChecked.'/>&nbsp;'.$sLabel.'<br/>');
}
$oP->add('</td><td style="vertical-align:top">');
$oP->add('<h3>'.Dict::S('UI:CSVImport:TextQualifierCharacter').'</h3>');
$sRawQualifier = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
$aQualifiers = array(
'"' => Dict::S('UI:CSVImport:QualifierDoubleQuote+'),
'\'' => Dict::S('UI:CSVImport:QualifierSimpleQuote+'),
);
$sOtherQualifier = '';
if (!array_key_exists($sRawQualifier, $aQualifiers))
{
$sOtherQualifier = $sRawQualifier;
$sRawQualifier = 'other';
}
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.htmlentities($sOtherQualifier, ENT_QUOTES, 'UTF-8').'"/>';
foreach($aQualifiers as $sVal => $sLabel)
{
$sChecked = ($sVal == $sRawQualifier) ? 'checked' : '';
$oP->add('<input type="radio" name="text-qualifier" value="'.htmlentities($sVal, ENT_QUOTES, 'UTF-8').'" '.$sChecked.'/>&nbsp;'.$sLabel.'<br/>');
}
$sChecked = (utils::ReadParam('no_localize', 0) == 1) ? ' checked ' : '';
$oP->add('</td><td style="vertical-align:top">');
$oP->add('<h3>'.Dict::S('Core:BulkExport:CSVLocalization').'</h3>');
$oP->add('<input type="checkbox" id="csv_no_localize" name="no_localize" value="1"'.$sChecked.'><label for="csv_no_localize"> '.Dict::S('Core:BulkExport:OptionNoLocalize').'</label>');
$oP->add('<br/>');
$oP->add('<br/>');
$oP->add(Dict::S('UI:CSVImport:Encoding').': <select name="charset" style="font-family:Arial,Helvetica,Sans-serif">'); // IE 8 has some troubles if the font is different
$aPossibleEncodings = utils::GetPossibleEncodings(MetaModel::GetConfig()->GetCSVImportCharsets());
$sDefaultEncoding = MetaModel::GetConfig()->Get('csv_file_default_charset');
foreach($aPossibleEncodings as $sIconvCode => $sDisplayName )
{
$sSelected = '';
if ($sIconvCode == $sDefaultEncoding)
{
$sSelected = ' selected';
}
$oP->add('<option value="'.$sIconvCode.'"'.$sSelected.'>'.$sDisplayName.'</option>');
}
$oP->add('</select>');
$sChecked = (utils::ReadParam('formatted_text', 0) == 1) ? ' checked ' : '';
$oP->add('<h3>'.Dict::S('Core:BulkExport:TextFormat').'</h3>');
$oP->add('<input type="checkbox" id="csv_formatted_text" name="formatted_text" value="1"'.$sChecked.'><label for="csv_formatted_text"> '.Dict::S('Core:BulkExport:OptionFormattedText').'</label>');
$oP->add('</td><td style="vertical-align:top">');
$sDateTimeFormat = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
$sDefaultChecked = ($sDateTimeFormat == (string)AttributeDateTime::GetFormat()) ? ' checked' : '';
$sCustomChecked = ($sDateTimeFormat !== (string)AttributeDateTime::GetFormat()) ? ' checked' : '';
$oP->add('<h3>'.Dict::S('Core:BulkExport:DateTimeFormat').'</h3>');
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
$oP->add('<input type="radio" id="csv_date_time_format_default" name="csv_date_format_radio" value="default"'.$sDefaultChecked.'><label for="csv_date_time_format_default"> '.Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample).'</label><br/>');
$sFormatInput = '<input type="text" size="15" name="date_format" id="csv_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
$oP->add('<input type="radio" id="csv_date_time_format_custom" name="csv_date_format_radio" value="custom"'.$sCustomChecked.'><label for="csv_date_time_format_custom"> '.Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput).'</label>');
$oP->add('</td></tr></table>');
$oP->add('</fieldset>');
$sJSTooltip = json_encode('<div class="date_format_tooltip">'.Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip').'</div>');
$oP->add_ready_script(
<<<EOF
$('#csv_custom_date_time_format').tooltip({content: function() { return $sJSTooltip; } });
$('#form_part_csv_options').on('preview_updated', function() { FormatDatesInPreview('csv', 'csv'); });
$('#csv_date_time_format_default').on('click', function() { FormatDatesInPreview('csv', 'csv'); });
$('#csv_date_time_format_custom').on('click', function() { FormatDatesInPreview('csv', 'csv'); });
$('#csv_custom_date_time_format').on('click', function() { $('#csv_date_time_format_custom').prop('checked', true); FormatDatesInPreview('csv', 'csv'); }).on('keyup', function() { FormatDatesInPreview('csv', 'csv'); });
EOF
);
break;
default:
return parent:: DisplayFormPart($oP, $sPartId);
}
}
protected function GetSampleData($oObj, $sAttCode)
{
if ($sAttCode != 'id')
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
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).'">'.htmlentities($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj), ENT_QUOTES, 'UTF-8').'</div>';
}
}
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
case 'id':
$sRet = $oObj->GetKey();
break;
default:
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
}
return $sRet;
}
public function GetHeader()
{
$oSet = new DBObjectSet($this->oSearch);
$this->aStatusInfo['status'] = 'running';
$this->aStatusInfo['position'] = 0;
$this->aStatusInfo['total'] = $oSet->Count();
$aData = array();
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)
{
// 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')
{
// 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 #991)
$aData[$idx] = @iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $aData[$idx]);
}
}
$sData = implode($this->aStatusInfo['separator'], $aData)."\n";
return $sData;
}
public function GetNextChunk(&$aStatus)
{
$sRetCode = 'run';
$iPercentage = 0;
$oSet = new DBObjectSet($this->oSearch);
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
$this->OptimizeColumnLoad($oSet);
$iCount = 0;
$sData = '';
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
$sExportDateTimeFormat = $this->aStatusInfo['date_format'];
$oPrevDateTimeFormat = AttributeDateTime::GetFormat();
$oPrevDateFormat = AttributeDate::GetFormat();
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())
{
set_time_limit($iLoopTimeLimit);
$aData = array();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$sAlias = $aFieldSpec['sAlias'];
$sAttCode = $aFieldSpec['sAttCode'];
$sField = '';
$oObj = $aRow[$sAlias];
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')
{
// 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 #991)
$aData[] = @iconv('UTF-8', $this->aStatusInfo['charset'].'//IGNORE//TRANSLIT', $sField);
}
else
{
$aData[] = $sField;
}
}
$sData .= implode($this->aStatusInfo['separator'], $aData)."\n";
$iCount++;
}
// Restore original date & time formats
AttributeDateTime::SetFormat($oPrevDateTimeFormat);
AttributeDate::SetFormat($oPrevDateFormat);
set_time_limit($iPreviousTimeLimit);
$this->aStatusInfo['position'] += $this->iChunkSize;
if ($this->aStatusInfo['total'] == 0)
{
$iPercentage = 100;
}
else
{
$iPercentage = floor(min(100.0, 100.0*$this->aStatusInfo['position']/$this->aStatusInfo['total']));
}
if ($iCount < $this->iChunkSize)
{
$sRetCode = 'done';
}
$aStatus = array('code' => $sRetCode, 'message' => Dict::S('Core:BulkExport:RetrievingData'), 'percentage' => $iPercentage);
return $sData;
}
public function GetSupportedFormats()
{
return array('csv' => Dict::S('Core:BulkExport:CSVFormat'));
}
public function GetMimeType()
{
return 'text/csv';
}
public function GetFileExtension()
{
return 'csv';
}
public function GetCharacterSet()
{
return $this->aStatusInfo['charset'];
}
}

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* CSV parser
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
@@ -54,14 +53,12 @@ class CSVParser
private $m_sCSVData;
private $m_sSep;
private $m_sTextQualifier;
private $m_iTimeLimitPerRow;
public function __construct($sTxt, $sSep = ',', $sTextQualifier = '"', $iTimeLimitPerRow = null)
public function __construct($sTxt, $sSep = ',', $sTextQualifier = '"')
{
$this->m_sCSVData = str_replace("\r\n", "\n", $sTxt);
$this->m_sSep = $sSep;
$this->m_sTextQualifier = $sTextQualifier;
$this->m_iTimeLimitPerRow = $iTimeLimitPerRow;
}
protected $m_sCurrCell = '';
@@ -131,12 +128,6 @@ class CSVParser
// blank line, skip silently
}
$this->m_aCurrRow = array();
// More time for the next row
if ($this->m_iTimeLimitPerRow !== null)
{
set_time_limit($this->m_iTimeLimitPerRow);
}
}
protected function __AddCellTrimmed($c = null, $aFieldMap = null)
{
@@ -189,13 +180,6 @@ class CSVParser
$iDataLength = strlen($this->m_sCSVData);
$iState = stSTARTING;
$iTimeLimit = null;
if ($this->m_iTimeLimitPerRow !== null)
{
// Give some time for the first row
$iTimeLimit = ini_get('max_execution_time');
set_time_limit($this->m_iTimeLimitPerRow);
}
for($i = 0; $i <= $iDataLength ; $i++)
{
if ($i == $iDataLength)
@@ -252,11 +236,6 @@ class CSVParser
$iLineCount = count($this->m_aDataSet);
if (($iMax > 0) && ($iLineCount >= $iMax)) break;
}
if ($iTimeLimit !== null)
{
// Restore the previous time limit
set_time_limit($iTimeLimit);
}
return $this->m_aDataSet;
}

View File

@@ -1,140 +0,0 @@
<?php
// Copyright (C) 2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Form\Form;
use Combodo\iTop\Form\FormManager;
/**
* Base class to implement a handler for AttributeCustomFields
*
* @copyright Copyright (C) 2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class CustomFieldsHandler
{
protected $sAttCode;
protected $aValues;
protected $oForm;
/**
* This constructor's prototype must be frozen.
* Any specific behavior must be implemented in BuildForm()
*
* @param $sAttCode
*/
final public function __construct($sAttCode)
{
$this->sAttCode = $sAttCode;
$this->aValues = null;
}
abstract public function BuildForm(DBObject $oHostObject, $sFormId);
/**
*
* @return \Combodo\iTop\Form\Form
*/
public function GetForm()
{
return $this->oForm;
}
public function SetCurrentValues($aValues)
{
$this->aValues = $aValues;
}
static public function GetPrerequisiteAttributes($sClass = null)
{
return array();
}
/**
* List the available verbs for 'GetForTemplate'
*/
static public function EnumTemplateVerbs()
{
return array();
}
/**
* Get various representations of the value, for insertion into a template (e.g. in Notifications)
* @param $aValues array The current values
* @param $sVerb string The verb specifying the representation of the value
* @param $bLocalize bool Whether or not to localize the value
* @return string
*/
abstract public function GetForTemplate($aValues, $sVerb, $bLocalize = true);
/**
* @param $aValues
* @param bool|true $bLocalize
* @return mixed
*/
abstract public function GetAsHTML($aValues, $bLocalize = true);
/**
* @param $aValues
* @param bool|true $bLocalize
* @return mixed
*/
abstract public function GetAsXML($aValues, $bLocalize = true);
/**
* @param $aValues
* @param string $sSeparator
* @param string $sTextQualifier
* @param bool|true $bLocalize
* @return mixed
*/
abstract public function GetAsCSV($aValues, $sSeparator = ',', $sTextQualifier = '"', $bLocalize = true);
/**
* @param DBObject $oHostObject
* @return array Associative array id => value
*/
abstract public function ReadValues(DBObject $oHostObject);
/**
* Record the data (currently in the processing of recording the host object)
* It is assumed that the data has been checked prior to calling Write()
* @param DBObject $oHostObject
* @param array Associative array id => value
*/
abstract public function WriteValues(DBObject $oHostObject, $aValues);
/**
* Cleanup data upon object deletion (object id still available here)
* @param DBObject $oHostObject
*/
abstract public function DeleteValues(DBObject $oHostObject);
/**
* @param $aValuesA
* @param $aValuesB
* @return bool
*/
abstract public function CompareValues($aValuesA, $aValuesB);
/**
* String representation of the value, must depend solely on the semantics
* @return string
*/
abstract public function GetValueFingerprint();
}

View File

@@ -1,28 +1,27 @@
<?php
// Copyright (C) 2010-2015 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* data generator
* helps the consultants in creating dummy data sets, for various test purposes (validation, usability, scalability)
*
* @copyright Copyright (C) 2010-2015 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
/**
@@ -267,7 +266,7 @@ class cmdbDataGenerator
function GenerateKey($sClass, $aFilterCriteria)
{
$retKey = null;
$oFilter = new DBObjectSearch($sClass);
$oFilter = new CMDBSearchFilter($sClass);
foreach($aFilterCriteria as $sFilterCode => $filterValue)
{
$oFilter->AddCondition($sFilterCode, $filterValue, '=');
@@ -346,7 +345,7 @@ class cmdbDataGenerator
*/
protected function OrganizationExists($sCode)
{
$oFilter = new DBObjectSearch('Organization');
$oFilter = new CMDBSearchFilter('bizOrganization');
$oFilter->AddCondition('code', $sCode, '=');
$oSet = new CMDBObjectSet($oFilter);
return ($oSet->Count() > 0);
@@ -361,7 +360,7 @@ class cmdbDataGenerator
protected function GetOrganization($sId)
{
$oOrg = null;
$oFilter = new DBObjectSearch('Organization');
$oFilter = new CMDBSearchFilter('bizOrganization');
$oFilter->AddCondition('id', $sId, '=');
$oSet = new CMDBObjectSet($oFilter);
if ($oSet->Count() > 0)

View File

@@ -1,258 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
<user_rights>
<profiles>
<profile id="1024" _delta="define">
<name>REST Services User</name>
<description>Only users having this profile are allowed to use the REST Web Services (unless 'secure_rest_services' is set to false in the configuration file).</description>
<groups />
</profile>
</profiles>
</user_rights>
<meta>
<classes>
<class id="User" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>core,grant_by_profile</category>
</properties>
<fields>
<field id="contactid" xsi:type="AttributeExternalKey">
<target_class>Person</target_class>
</field>
<field id="last_name" xsi:type="AttributeExternalField"/>
<field id="first_name" xsi:type="AttributeExternalField"/>
<field id="email" xsi:type="AttributeExternalField"/>
<field id="org_id" xsi:type="AttributeExternalField"/>
<field id="login" xsi:type="AttributeString"/>
<field id="language" xsi:type="AttributeApplicationLanguage"/>
<field id="status" xsi:type="AttributeEnum"/>
<field id="profile_list" xsi:type="AttributeLinkedSetIndirect"/>
<field id="allowed_org_list" xsi:type="AttributeLinkedSetIndirect"/>
<field id="finalclass" xsi:type="AttributeFinalClass"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
<field id="contactid_friendlyname" xsi:type="AttributeExternalField"/>
<field id="contactid_obsolescence_flag" xsi:type="AttributeExternalField"/>
<field id="org_id_friendlyname" xsi:type="AttributeExternalField"/>
<field id="org_id_obsolescence_flag" xsi:type="AttributeExternalField"/>
</fields>
</class>
<class id="URP_Profiles" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>addon/userrights,grant_by_profile</category>
</properties>
<fields>
<field id="name" xsi:type="AttributeString"/>
<field id="description" xsi:type="AttributeString"/>
<field id="user_list" xsi:type="AttributeLinkedSetIndirect"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="URP_UserProfile" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>addon/userrights,grant_by_profile</category>
</properties>
<fields>
<field id="userid" xsi:type="AttributeExternalKey">
<target_class>User</target_class>
</field>
<field id="userlogin" xsi:type="AttributeExternalField"/>
<field id="profileid" xsi:type="AttributeExternalKey">
<target_class>URP_Profiles</target_class>
</field>
<field id="profile" xsi:type="AttributeExternalField"/>
<field id="reason" xsi:type="AttributeString"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
<field id="userid_friendlyname" xsi:type="AttributeExternalField"/>
<field id="userid_finalclass_recall" xsi:type="AttributeExternalField"/>
<field id="profileid_friendlyname" xsi:type="AttributeExternalField"/>
</fields>
</class>
<class id="URP_UserOrg" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>addon/userrights,grant_by_profile</category>
</properties>
<fields>
<field id="userid" xsi:type="AttributeExternalKey">
<target_class>User</target_class>
</field>
<field id="userlogin" xsi:type="AttributeExternalField"/>
<field id="allowed_org_id" xsi:type="AttributeExternalKey">
<target_class>Organization</target_class>
</field>
<field id="allowed_org_name" xsi:type="AttributeExternalField"/>
<field id="reason" xsi:type="AttributeString"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
<field id="userid_friendlyname" xsi:type="AttributeExternalField"/>
<field id="userid_finalclass_recall" xsi:type="AttributeExternalField"/>
<field id="allowed_org_id_friendlyname" xsi:type="AttributeExternalField"/>
<field id="allowed_org_id_obsolescence_flag" xsi:type="AttributeExternalField"/>
</fields>
</class>
<class id="Action" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>grant_by_profile,core/cmdb</category>
</properties>
<fields>
<field id="name" xsi:type="AttributeString"/>
<field id="description" xsi:type="AttributeString"/>
<field id="status" xsi:type="AttributeEnum"/>
<field id="trigger_list" xsi:type="AttributeLinkedSetIndirect"/>
<field id="finalclass" xsi:type="AttributeFinalClass"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="Trigger" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>grant_by_profile,core/cmdb</category>
</properties>
<fields>
<field id="description" xsi:type="AttributeString"/>
<field id="action_list" xsi:type="AttributeLinkedSetIndirect"/>
<field id="finalclass" xsi:type="AttributeFinalClass"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="SynchroDataSource" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>core/cmdb,view_in_gui,grant_by_profile</category>
</properties>
<fields>
<field id="name" xsi:type="AttributeString"/>
<field id="description" xsi:type="AttributeText"/>
<field id="status" xsi:type="AttributeEnum"/>
<field id="user_id" xsi:type="AttributeExternalKey">
<target_class>User</target_class>
</field>
<field id="notify_contact_id" xsi:type="AttributeExternalKey">
<target_class>Contact</target_class>
</field>
<field id="scope_class" xsi:type="AttributeClass"/>
<field id="database_table_name" xsi:type="AttributeString"/>
<field id="scope_restriction" xsi:type="AttributeString"/>
<field id="full_load_periodicity" xsi:type="AttributeDuration"/>
<field id="reconciliation_policy" xsi:type="AttributeEnum"/>
<field id="action_on_zero" xsi:type="AttributeEnum"/>
<field id="action_on_one" xsi:type="AttributeEnum"/>
<field id="action_on_multiple" xsi:type="AttributeEnum"/>
<field id="delete_policy" xsi:type="AttributeEnum"/>
<field id="delete_policy_update" xsi:type="AttributeString"/>
<field id="delete_policy_retention" xsi:type="AttributeDuration"/>
<field id="attribute_list" xsi:type="AttributeLinkedSet"/>
<field id="user_delete_policy" xsi:type="AttributeEnum"/>
<field id="url_icon" xsi:type="AttributeURL"/>
<field id="url_application" xsi:type="AttributeString"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
<field id="user_id_friendlyname" xsi:type="AttributeExternalField"/>
<field id="user_id_finalclass_recall" xsi:type="AttributeExternalField"/>
<field id="notify_contact_id_friendlyname" xsi:type="AttributeExternalField"/>
<field id="notify_contact_id_finalclass_recall" xsi:type="AttributeExternalField"/>
<field id="notify_contact_id_obsolescence_flag" xsi:type="AttributeExternalField"/>
</fields>
</class>
<class id="SynchroAttribute" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>core/cmdb,view_in_gui,grant_by_profile</category>
</properties>
<fields>
<field id="sync_source_id" xsi:type="AttributeExternalKey">
<target_class>SynchroDataSource</target_class>
</field>
<field id="sync_source_name" xsi:type="AttributeExternalField"/>
<field id="attcode" xsi:type="AttributeString"/>
<field id="update" xsi:type="AttributeBoolean"/>
<field id="reconcile" xsi:type="AttributeBoolean"/>
<field id="update_policy" xsi:type="AttributeEnum"/>
<field id="finalclass" xsi:type="AttributeFinalClass"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
<field id="sync_source_id_friendlyname" xsi:type="AttributeExternalField"/>
</fields>
</class>
<class id="AuditRule" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>application, grant_by_profile</category>
</properties>
<fields>
<field id="name" xsi:type="AttributeString"/>
<field id="description" xsi:type="AttributeString"/>
<field id="query" xsi:type="AttributeOQL"/>
<field id="valid_flag" xsi:type="AttributeEnum"/>
<field id="category_id" xsi:type="AttributeExternalKey">
<target_class>AuditCategory</target_class>
</field>
<field id="category_name" xsi:type="AttributeExternalField"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
<field id="category_id_friendlyname" xsi:type="AttributeExternalField"/>
</fields>
</class>
<class id="AuditCategory" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>application, grant_by_profile</category>
</properties>
<fields>
<field id="name" xsi:type="AttributeString"/>
<field id="description" xsi:type="AttributeString"/>
<field id="definition_set" xsi:type="AttributeOQL"/>
<field id="rules_list" xsi:type="AttributeLinkedSet"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="Query" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>core/cmdb,view_in_gui,application,grant_by_profile</category>
</properties>
<fields>
<field id="name" xsi:type="AttributeString"/>
<field id="description" xsi:type="AttributeText"/>
<field id="fields" xsi:type="AttributeText"/>
<field id="finalclass" xsi:type="AttributeFinalClass"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="lnkTriggerAction" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
<properties>
<category>grant_by_profile,core/cmdb,application</category>
</properties>
<fields>
<field id="action_id" xsi:type="AttributeExternalKey">
<target_class>Action</target_class>
</field>
<field id="action_name" xsi:type="AttributeExternalField"/>
<field id="trigger_id" xsi:type="AttributeExternalKey">
<target_class>Trigger</target_class>
</field>
<field id="trigger_name" xsi:type="AttributeExternalField"/>
<field id="order" xsi:type="AttributeInteger"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
<field id="action_id_friendlyname" xsi:type="AttributeExternalField"/>
<field id="action_id_finalclass_recall" xsi:type="AttributeExternalField"/>
<field id="trigger_id_friendlyname" xsi:type="AttributeExternalField"/>
<field id="trigger_id_finalclass_recall" xsi:type="AttributeExternalField"/>
</fields>
</class>
</classes>
</meta>
</itop_design>

View File

@@ -1,428 +0,0 @@
<?php
// Copyright (C) 2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* 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
*/
public function __construct($sPHPFormat)
{
$this->sPHPFormat = (string)$sPHPFormat;
}
/**
* @return string
*/
public function __toString()
{
return $this->sPHPFormat;
}
/**
* Return the mapping table for converting between various conventions for date/time formats
*/
protected static function GetFormatMapping()
{
return array(
// 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)
// 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
// 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
// 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' => '([1-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'),
// Minutes
'i' => array('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'),
);
}
/**
* Transform the PHP format into the specified format, taking care of escaping the litteral characters
* using the supplied escaping expression
* @param string $sOutputFormatCode THe target format code: regexpr|datepicker|excel|moment
* @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
*/
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] == '\\'))
{
$bEscaping = true;
continue;
}
if ($bEscaping)
{
if (($sSpecialChars === '') || (strpos($sSpecialChars, $this->sPHPFormat[$i]) !== false))
{
$sResult .= sprintf($sEscapePattern, $this->sPHPFormat[$i]);
}
else
{
$sResult .= $this->sPHPFormat[$i];
}
$bEscaping = false;
}
else if(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))
{
$sResult .= sprintf($sEscapePattern, $this->sPHPFormat[$i]);
}
else
{
// Normal char with no special meaning, no need to escape it
$sResult .= $this->sPHPFormat[$i];
}
}
}
return $sResult;
}
/**
* Format a date into the supplied format string
* @param mixed $date An int, string, DateTime object or null !!
* @throws Exception
* @return string The formatted date
*/
public function Format($date)
{
if ($date == null)
{
$sDate = '';
}
else if (($date === '0000-00-00') || ($date === '0000-00-00 00:00:00'))
{
$sDate = '';
}
else if ($date instanceof DateTime)
{
// Parameter is a DateTime
$sDate = $date->format($this->sPHPFormat);
}
else if (is_int($date))
{
// Parameter is a Unix timestamp
$oDate = new DateTime();
$oDate->setTimestamp($date);
$sDate = $oDate->format($this->sPHPFormat);
}
else if (is_string($date))
{
$oDate = new DateTime($date);
$sDate = $oDate->format($this->sPHPFormat);
}
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
* @param string $sFormat The format, in PHP createFromFormat convention
* @throws Exception
* @return DateTime|null
*/
public function Parse($sDate)
{
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)
{
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
*/
public function ToDatePicker()
{
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
*/
public function ToExcel()
{
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
*/
public function ToMomentJS()
{
return $this->Transform('moment', "[%s]", true /* escape all */);
}
public static function GetJSSQLToCustomFormat()
{
$aPHPToMoment = array();
foreach(self::GetFormatMapping() as $sPHPCode => $aMapping)
{
$aPHPToMoment[$sPHPCode] = $aMapping['moment'];
}
$sJSMapping = json_encode($aPHPToMoment);
$sFunction =
<<<EOF
function PHPDateTimeFormatToSubFormat(sPHPFormat, sPlaceholders)
{
var iMax = 0;
var iMin = 999;
var bEscaping = false;
for(var i=0; i<sPHPFormat.length; i++)
{
var c = sPHPFormat[i];
if (c == '\\\\')
{
bEscaping = true;
continue;
}
if (bEscaping)
{
bEscaping = false;
continue;
}
else
{
if (sPlaceholders.search(c) != -1)
{
iMax = Math.max(iMax, i);
iMin = Math.min(iMin, i);
}
}
}
return sPHPFormat.substr(iMin, iMax - iMin + 1);
}
function PHPDateTimeFormatToMomentFormat(sPHPFormat)
{
var aFormatMapping = $sJSMapping;
var sMomentFormat = '';
var bEscaping = false;
for(var i=0; i<sPHPFormat.length; i++)
{
var c = sPHPFormat[i];
if (c == '\\\\')
{
bEscaping = true;
continue;
}
if (bEscaping)
{
sMomentFormat += '['+c+']';
bEscaping = false;
}
else
{
if (aFormatMapping[c] !== undefined)
{
sMomentFormat += aFormatMapping[c];
}
else
{
sMomentFormat += '['+c+']';
}
}
}
return sMomentFormat;
}
function DateFormatFromPHP(sSQLDate, sPHPFormat)
{
if (sSQLDate === '') return '';
var sPHPDateFormat = PHPDateTimeFormatToSubFormat(sPHPFormat, 'Yydjmn');
var sMomentFormat = PHPDateTimeFormatToMomentFormat(sPHPDateFormat);
return moment(sSQLDate).format(sMomentFormat);
}
function DateTimeFormatFromPHP(sSQLDate, sPHPFormat)
{
if (sSQLDate === '') return '';
var sMomentFormat = PHPDateTimeFormatToMomentFormat(sPHPFormat);
return moment(sSQLDate).format(sMomentFormat);
}
EOF
;
return $sFunction;
}
/**
* Get a placeholder text for a date or datetime format string
* @return string The placeholder text (localized)
*/
public function ToPlaceholder()
{
$aMappings = static::GetFormatMapping();
$sResult = '';
$bEscaping = false;
for($i=0; $i < strlen($this->sPHPFormat); $i++)
{
if (($this->sPHPFormat[$i] == '\\'))
{
$bEscaping = true;
continue;
}
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))
{
// Not a litteral value, must be replaced by Dict equivalent
$sResult .= Dict::S('Core:DateTime:Placeholder_'.$this->sPHPFormat[$i]);
}
else
{
// Normal char with no special meaning
$sResult .= $this->sPHPFormat[$i];
}
}
return $sResult;
}
/**
* Produces a subformat (Date or Time) by extracting the part of the whole DateTime format containing only the given placeholders
* @return string
*/
protected function ToSubFormat($aPlaceholders)
{
$iStart = 999;
$iEnd = 0;
foreach($aPlaceholders as $sChar)
{
$iPos = strpos($this->sPHPFormat, $sChar);
if ($iPos !== false)
{
if (($iPos > 0) && ($this->sPHPFormat[$iPos-1] == '\\'))
{
// The placeholder is actually escaped, it's a litteral character, ignore it
continue;
}
$iStart = min($iStart, $iPos);
$iEnd = max($iEnd, $iPos);
}
}
$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'));
}
/**
* 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'));
}
/**
* 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!)
* @param string $sDelimiter Surround the regexp (and escape) if needed
* @return string The regular expression in PCRE syntax
*/
public function ToRegExpr($sDelimiter = null)
{
$sRet = '^'.$this->Transform('regexpr', "\\%s", false /* escape all */, '.?*$^()[]:').'$';
if ($sDelimiter !== null)
{
$sRet = $sDelimiter.str_replace($sDelimiter, '\\'.$sDelimiter, $sRet).$sDelimiter;
}
return $sRet;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,63 +0,0 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* 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-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
interface iDBObjectSetIterator extends Countable
{
/**
* The class of the objects of the collection (at least a common ancestor)
*
* @return string
*/
public function GetClass();
/**
* The total number of objects in the collection
*
* @return int
*/
public function Count();
/**
* Reset the cursor to the first item in the collection. Equivalent to Seek(0)
*
* @return DBObject The fetched object or null when at the end
*/
public function Rewind();
/**
* Position the cursor to the given 0-based position
*
* @param int $iRow
*/
public function Seek($iPosition);
/**
* Fetch the object at the current position in the collection and move the cursor to the next position.
*
* @return DBObject The fetched object or null when at the end
*/
public function Fetch();
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Database properties - manage database instances in a complex installation
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/

View File

@@ -1,922 +0,0 @@
<?php
// Copyright (C) 2015-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
require_once('dbobjectsearch.class.php');
require_once('dbunionsearch.class.php');
/**
* An object search
*
* Note: in the ancient times of iTop, a search was named after DBObjectSearch.
* When the UNION has been introduced, it has been decided to:
* - declare a hierarchy of search classes, with two leafs :
* - one class to cope with a single query (A JOIN B... WHERE...)
* - and the other to cope with several queries (query1 UNION query2)
* - in order to preserve forward/backward compatibility of the existing modules
* - keep the name of DBObjectSearch even if it a little bit confusing
* - do not provide a type-hint for function parameters defined in the modules
* - leave the statements DBObjectSearch::FromOQL in the modules, though DBSearch is more relevant
*
* @copyright Copyright (C) 2015-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
abstract class DBSearch
{
const JOIN_POINTING_TO = 0;
const JOIN_REFERENCED_BY = 1;
protected $m_bNoContextParameters = false;
protected $m_aModifierProperties = array();
protected $m_bArchiveMode = false;
protected $m_bShowObsoleteData = true;
public function __construct()
{
$this->Init();
}
protected function Init()
{
// Set the obsolete and archive modes to the default ones
$this->m_bArchiveMode = utils::IsArchiveMode();
$this->m_bShowObsoleteData = true;
}
/**
* Perform a deep clone (as opposed to "clone" which does copy a reference to the underlying objects)
*
* @return \DBSearch
**/
public function DeepClone()
{
return unserialize(serialize($this)); // Beware this serializes/unserializes the search and its parameters as well
}
abstract public function AllowAllData();
abstract public function IsAllDataAllowed();
public function SetArchiveMode($bEnable)
{
$this->m_bArchiveMode = $bEnable;
}
public function GetArchiveMode()
{
return $this->m_bArchiveMode;
}
public function SetShowObsoleteData($bShow)
{
$this->m_bShowObsoleteData = $bShow;
}
public function GetShowObsoleteData()
{
if ($this->m_bArchiveMode || $this->IsAllDataAllowed())
{
// Enable obsolete data too!
$bRet = true;
}
else
{
$bRet = $this->m_bShowObsoleteData;
}
return $bRet;
}
public function NoContextParameters() {$this->m_bNoContextParameters = true;}
public function HasContextParameters() {return $this->m_bNoContextParameters;}
public function SetModifierProperty($sPluginClass, $sProperty, $value)
{
$this->m_aModifierProperties[$sPluginClass][$sProperty] = $value;
}
public function GetModifierProperties($sPluginClass)
{
if (array_key_exists($sPluginClass, $this->m_aModifierProperties))
{
return $this->m_aModifierProperties[$sPluginClass];
}
else
{
return array();
}
}
abstract public function GetClassName($sAlias);
abstract public function GetClass();
abstract public function GetClassAlias();
/**
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
* Defaults to the first selected class (most of the time it is also the first joined class
*/
abstract public function ChangeClass($sNewClass, $sAlias = null);
abstract public function GetSelectedClasses();
/**
* @param array $aSelectedClasses array of aliases
* @throws CoreException
*/
abstract public function SetSelectedClasses($aSelectedClasses);
/**
* Change any alias of the query tree
*
* @param $sOldName
* @param $sNewName
* @return bool True if the alias has been found and changed
*/
abstract public function RenameAlias($sOldName, $sNewName);
abstract public function IsAny();
public function Describe(){return 'deprecated - use ToOQL() instead';}
public function DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo){return 'deprecated - use ToOQL() instead';}
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode){return 'deprecated - use ToOQL() instead';}
public function DescribeConditionRelTo($aRelInfo){return 'deprecated - use ToOQL() instead';}
public function DescribeConditions(){return 'deprecated - use ToOQL() instead';}
public function __DescribeHTML(){return 'deprecated - use ToOQL() instead';}
abstract public function ResetCondition();
abstract public function MergeConditionExpression($oExpression);
abstract public function AddConditionExpression($oExpression);
abstract public function AddNameCondition($sName);
abstract public function AddCondition($sFilterCode, $value, $sOpCode = null);
/**
* Specify a condition on external keys or link sets
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
* Example: infra_list->ci_id->location_id->country
* @param value The value to match (can be an array => IN(val1, val2...)
* @return void
*/
abstract public function AddConditionAdvanced($sAttSpec, $value);
abstract public function AddCondition_FullText($sFullText);
/**
* @param DBObjectSearch $oFilter
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @throws CoreException
* @throws CoreWarning
*/
abstract public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null);
/**
* @param DBObjectSearch $oFilter
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
*/
abstract public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null);
abstract public function Intersect(DBSearch $oFilter);
/**
* @param DBSearch $oFilter
* @param integer $iDirection
* @param string $sExtKeyAttCode
* @param integer $iOperatorCode
* @param array &$RealisasingMap Map of aliases from the attached query, that could have been renamed by the optimization process
* @return DBSearch
*/
public function Join(DBSearch $oFilter, $iDirection, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
$oSourceFilter = $this->DeepClone();
$oRet = null;
if ($oFilter instanceof DBUnionSearch)
{
$aSearches = array();
foreach ($oFilter->GetSearches() as $oSearch)
{
$aSearches[] = $oSourceFilter->Join($oSearch, $iDirection, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
$oRet = new DBUnionSearch($aSearches);
}
else
{
if ($iDirection === static::JOIN_POINTING_TO)
{
$oSourceFilter->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
else
{
if ($iOperatorCode !== TREE_OPERATOR_EQUALS)
{
throw new Exception('Only TREE_OPERATOR_EQUALS operator code is supported yet for AddCondition_ReferencedBy.');
}
$oSourceFilter->AddCondition_ReferencedBy($oFilter, $sExtKeyAttCode, TREE_OPERATOR_EQUALS, $aRealiasingMap);
}
$oRet = $oSourceFilter;
}
return $oRet;
}
abstract public function SetInternalParams($aParams);
abstract public function GetInternalParams();
abstract public function GetQueryParams($bExcludeMagicParams = true);
abstract public function ListConstantFields();
/**
* Turn the parameters (:xxx) into scalar values in order to easily
* serialize a search
*/
abstract public function ApplyParameters($aArgs);
public function serialize($bDevelopParams = false, $aContextParams = null)
{
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
return rawurlencode(base64_encode(serialize(array($sOql, $this->GetInternalParams(), $this->m_aModifierProperties))));
}
/**
* @param string $sValue Serialized OQL query
*
* @return \DBSearch
*/
static public function unserialize($sValue)
{
$aData = unserialize(base64_decode(rawurldecode($sValue)));
$sOql = $aData[0];
$aParams = $aData[1];
// We've tried to use gzcompress/gzuncompress, but for some specific queries
// it was not working at all (See Trac #193)
// gzuncompress was issuing a warning "data error" and the return object was null
$oRetFilter = self::FromOQL($sOql, $aParams);
$oRetFilter->m_aModifierProperties = $aData[2];
return $oRetFilter;
}
/**
* Create a new DBObjectSearch from $oSearch with a new alias $sAlias
*
* Note : This has not be tested with UNION queries.
*
* @param DBSearch $oSearch
* @param string $sAlias
* @return DBObjectSearch
*/
static public function CloneWithAlias(DBSearch $oSearch, $sAlias)
{
$oSearchWithAlias = new DBObjectSearch($oSearch->GetClass(), $sAlias);
$oSearchWithAlias = $oSearchWithAlias->Intersect($oSearch);
return $oSearchWithAlias;
}
abstract public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false);
static protected $m_aOQLQueries = array();
// Do not filter out depending on user rights
// In particular when we are currently in the process of evaluating the user rights...
static public function FromOQL_AllData($sQuery, $aParams = null)
{
$oRes = self::FromOQL($sQuery, $aParams);
$oRes->AllowAllData();
return $oRes;
}
/**
* @param string $sQuery
* @param array $aParams
* @return self
* @throws OQLException
*/
static public function FromOQL($sQuery, $aParams = null)
{
if (empty($sQuery))
{
return null;
}
// Query caching
$sQueryId = md5($sQuery);
$bOQLCacheEnabled = true;
if ($bOQLCacheEnabled)
{
if (array_key_exists($sQueryId, self::$m_aOQLQueries))
{
// hit!
$oResultFilter = self::$m_aOQLQueries[$sQueryId]->DeepClone();
}
elseif (self::$m_bUseAPCCache)
{
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
$sAPCCacheId = 'itop-'.MetaModel::GetEnvironmentId().'-dbsearch-cache-'.$sQueryId;
$oKPI = new ExecutionKPI();
$result = apc_fetch($sAPCCacheId);
$oKPI->ComputeStats('Search APC (fetch)', $sQuery);
if (is_object($result))
{
$oResultFilter = $result;
self::$m_aOQLQueries[$sQueryId] = $oResultFilter->DeepClone();
}
}
}
if (!isset($oResultFilter))
{
$oKPI = new ExecutionKPI();
$oOql = new OqlInterpreter($sQuery);
$oOqlQuery = $oOql->ParseQuery();
$oMetaModel = new ModelReflectionRuntime();
$oOqlQuery->Check($oMetaModel, $sQuery); // Exceptions thrown in case of issue
$oResultFilter = $oOqlQuery->ToDBSearch($sQuery);
$oKPI->ComputeStats('Parse OQL', $sQuery);
if ($bOQLCacheEnabled)
{
self::$m_aOQLQueries[$sQueryId] = $oResultFilter->DeepClone();
if (self::$m_bUseAPCCache)
{
$oKPI = new ExecutionKPI();
apc_store($sAPCCacheId, $oResultFilter, self::$m_iQueryCacheTTL);
$oKPI->ComputeStats('Search APC (store)', $sQueryId);
}
}
}
if (!is_null($aParams))
{
$oResultFilter->SetInternalParams($aParams);
}
// Set the default fields
$oResultFilter->Init();
return $oResultFilter;
}
// Alternative to object mapping: the data are transfered directly into an array
// This is 10 times faster than creating a set of objects, and makes sense when optimization is required
/**
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
*/
public function ToDataArray($aColumns = array(), $aOrderBy = array(), $aArgs = array())
{
$sSQL = $this->MakeSelectQuery($aOrderBy, $aArgs);
$resQuery = CMDBSource::Query($sSQL);
if (!$resQuery)
{
return;
}
if (count($aColumns) == 0)
{
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
// Add the standard id (as first column)
array_unshift($aColumns, 'id');
}
$aQueryCols = CMDBSource::GetColumns($resQuery, $sSQL);
$sClassAlias = $this->GetClassAlias();
$aColMap = array();
foreach ($aColumns as $sAttCode)
{
$sColName = $sClassAlias.$sAttCode;
if (in_array($sColName, $aQueryCols))
{
$aColMap[$sAttCode] = $sColName;
}
}
$aRes = array();
while ($aRow = CMDBSource::FetchArray($resQuery))
{
$aMappedRow = array();
foreach ($aColMap as $sAttCode => $sColName)
{
$aMappedRow[$sAttCode] = $aRow[$sColName];
}
$aRes[] = $aMappedRow;
}
CMDBSource::FreeResult($resQuery);
return $aRes;
}
////////////////////////////////////////////////////////////////////////////
//
// Construction of the SQL queries
//
////////////////////////////////////////////////////////////////////////////
protected static $m_aQueryStructCache = array();
/** Generate a Group By SQL request from a search
* @param array $aArgs
* @param array $aGroupByExpr array('alias' => Expression)
* @param bool $bExcludeNullValues
* @param array $aSelectExpr array('alias' => Expression) Additional expressions added to the request
* @param array $aOrderBy array('alias' => bool) true = ASC false = DESC
* @param int $iLimitCount
* @param int $iLimitStart
* @return string SQL query generated
* @throws Exception
*/
public function MakeGroupByQuery($aArgs, $aGroupByExpr, $bExcludeNullValues = false, $aSelectExpr = array(), $aOrderBy = array(), $iLimitCount = 0, $iLimitStart = 0)
{
// Sanity check
foreach($aGroupByExpr as $sAlias => $oExpr)
{
if (!($oExpr instanceof Expression))
{
throw new CoreException("Wrong parameter for 'Group By' for [$sAlias] (an array('alias' => Expression) is awaited)");
}
}
foreach($aSelectExpr as $sAlias => $oExpr)
{
if (array_key_exists($sAlias, $aGroupByExpr))
{
throw new CoreException("Alias collision between 'Group By' and 'Select Expressions' [$sAlias]");
}
if (!($oExpr instanceof Expression))
{
throw new CoreException("Wrong parameter for 'Select Expressions' for [$sAlias] (an array('alias' => Expression) is awaited)");
}
}
foreach($aOrderBy as $sAlias => $bAscending)
{
if (!array_key_exists($sAlias, $aGroupByExpr) && !array_key_exists($sAlias, $aSelectExpr) && ($sAlias != '_itop_count_'))
{
$aAllowedAliases = array_keys($aSelectExpr);
$aAllowedAliases = array_merge($aAllowedAliases, array_keys($aGroupByExpr));
$aAllowedAliases[] = '_itop_count_';
throw new CoreException("Wrong alias [$sAlias] for 'Order By'. Allowed values are: ", null, implode(", ", $aAllowedAliases));
}
if (!is_bool($bAscending))
{
throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value for '$sAlias''");
}
}
if ($bExcludeNullValues)
{
// Null values are not handled (though external keys set to 0 are allowed)
$oQueryFilter = $this->DeepClone();
foreach ($aGroupByExpr as $oGroupByExp)
{
$oNull = new FunctionExpression('ISNULL', array($oGroupByExp));
$oNotNull = new BinaryExpression($oNull, '!=', new TrueExpression());
$oQueryFilter->AddConditionExpression($oNotNull);
}
}
else
{
$oQueryFilter = $this;
}
$aAttToLoad = array();
$oSQLQuery = $oQueryFilter->GetSQLQuery(array(), $aArgs, $aAttToLoad, null, 0, 0, false, $aGroupByExpr, $aSelectExpr);
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
try
{
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
$sRes = $oSQLQuery->RenderGroupBy($aScalarArgs, $bBeautifulSQL, $aOrderBy, $iLimitCount, $iLimitStart);
}
catch (Exception $e)
{
// Add some information...
$e->addInfo('OQL', $this->ToOQL());
throw $e;
}
$this->AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $sRes);
return $sRes;
}
/**
* @param array|hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
* @param array $aArgs
* @param null $aAttToLoad
* @param null $aExtendedDataSpec
* @param int $iLimitCount
* @param int $iLimitStart
* @param bool $bGetCount
* @return string
* @throws CoreException
* @throws Exception
* @throws MissingQueryArgument
*/
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
{
// Check the order by specification, and prefix with the class alias
// and make sure that the ordering columns are going to be selected
//
$sClass = $this->GetClass();
$sClassAlias = $this->GetClassAlias();
$aOrderSpec = array();
foreach ($aOrderBy as $sFieldAlias => $bAscending)
{
if (!is_bool($bAscending))
{
throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value");
}
$iDotPos = strpos($sFieldAlias, '.');
if ($iDotPos === false)
{
$sAttClass = $sClass;
$sAttClassAlias = $sClassAlias;
$sAttCode = $sFieldAlias;
}
else
{
$sAttClassAlias = substr($sFieldAlias, 0, $iDotPos);
$sAttClass = $this->GetClassName($sAttClassAlias);
$sAttCode = substr($sFieldAlias, $iDotPos + 1);
}
if ($sAttCode != 'id')
{
MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sAttCode, MetaModel::GetAttributesList($sAttClass));
$oAttDef = MetaModel::GetAttributeDef($sAttClass, $sAttCode);
foreach($oAttDef->GetOrderBySQLExpressions($sAttClassAlias) as $sSQLExpression)
{
$aOrderSpec[$sSQLExpression] = $bAscending;
}
}
else
{
$aOrderSpec['`'.$sAttClassAlias.$sAttCode.'`'] = $bAscending;
}
// Make sure that the columns used for sorting are present in the loaded columns
if (!is_null($aAttToLoad) && !isset($aAttToLoad[$sAttClassAlias][$sAttCode]))
{
$aAttToLoad[$sAttClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sAttClass, $sAttCode);
}
}
$oSQLQuery = $this->GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount);
if ($this->m_bNoContextParameters)
{
// Only internal parameters
$aScalarArgs = $this->GetInternalParams();
}
else
{
// The complete list of arguments will include magic arguments (e.g. current_user->attcode)
$aScalarArgs = MetaModel::PrepareQueryArguments($aArgs, $this->GetInternalParams());
}
try
{
$bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
if ($sClassAlias == '_itop_')
{
IssueLog::Info('SQL Query (_itop_): '.$sRes);
}
}
catch (MissingQueryArgument $e)
{
// Add some information...
$e->addInfo('OQL', $this->ToOQL());
throw $e;
}
$this->AddQueryTraceSelect($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sRes);
return $sRes;
}
protected function GetSQLQuery($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $aGroupByExpr = null, $aSelectExpr = null)
{
$oSQLQuery = $this->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, null, $aSelectExpr);
$oSQLQuery->SetSourceOQL($this->ToOQL());
// Join to an additional table, if required...
//
if ($aExtendedDataSpec != null)
{
$sTableAlias = '_extended_data_';
$aExtendedFields = array();
foreach($aExtendedDataSpec['fields'] as $sColumn)
{
$sColRef = $this->GetClassAlias().'_extdata_'.$sColumn;
$aExtendedFields[$sColRef] = new FieldExpressionResolved($sColumn, $sTableAlias);
}
$oSQLQueryExt = new SQLObjectQuery($aExtendedDataSpec['table'], $sTableAlias, $aExtendedFields);
$oSQLQuery->AddInnerJoin($oSQLQueryExt, 'id', $aExtendedDataSpec['join_key'] /*, $sTableAlias*/);
}
return $oSQLQuery;
}
public abstract function GetSQLQueryStructure(
$aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null
);
////////////////////////////////////////////////////////////////////////////
//
// Cache/Trace/Log queries
//
////////////////////////////////////////////////////////////////////////////
protected static $m_bDebugQuery = false;
protected static $m_aQueriesLog = array();
protected static $m_bQueryCacheEnabled = false;
protected static $m_bUseAPCCache = false;
protected static $m_iQueryCacheTTL = 3600;
protected static $m_bTraceQueries = false;
protected static $m_bIndentQueries = false;
protected static $m_bOptimizeQueries = false;
public static function StartDebugQuery()
{
$aBacktrace = debug_backtrace();
self::$m_bDebugQuery = true;
}
public static function StopDebugQuery()
{
self::$m_bDebugQuery = false;
}
public static function EnableQueryCache($bEnabled, $bUseAPC, $iTimeToLive = 3600)
{
self::$m_bQueryCacheEnabled = $bEnabled;
self::$m_bUseAPCCache = $bUseAPC;
self::$m_iQueryCacheTTL = $iTimeToLive;
}
public static function EnableQueryTrace($bEnabled)
{
self::$m_bTraceQueries = $bEnabled;
}
public static function EnableQueryIndentation($bEnabled)
{
self::$m_bIndentQueries = $bEnabled;
}
public static function EnableOptimizeQuery($bEnabled)
{
self::$m_bOptimizeQueries = $bEnabled;
}
protected function AddQueryTraceSelect($aOrderBy, $aArgs, $aAttToLoad, $aExtendedDataSpec, $iLimitCount, $iLimitStart, $bGetCount, $sSql)
{
if (self::$m_bTraceQueries)
{
$aQueryData = array(
'type' => 'select',
'filter' => $this,
'order_by' => $aOrderBy,
'args' => $aArgs,
'att_to_load' => $aAttToLoad,
'extended_data_spec' => $aExtendedDataSpec,
'limit_count' => $iLimitCount,
'limit_start' => $iLimitStart,
'is_count' => $bGetCount
);
$sOql = $this->ToOQL(true, $aArgs);
self::AddQueryTrace($aQueryData, $sOql, $sSql);
}
}
protected function AddQueryTraceGroupBy($aArgs, $aGroupByExpr, $sSql)
{
if (self::$m_bTraceQueries)
{
$aQueryData = array(
'type' => 'group_by',
'filter' => $this,
'args' => $aArgs,
'group_by_expr' => $aGroupByExpr
);
$sOql = $this->ToOQL(true, $aArgs);
self::AddQueryTrace($aQueryData, $sOql, $sSql);
}
}
protected static function AddQueryTrace($aQueryData, $sOql, $sSql)
{
if (self::$m_bTraceQueries)
{
$sQueryId = md5(serialize($aQueryData));
$sMySQLQueryId = md5($sSql);
if(!isset(self::$m_aQueriesLog[$sQueryId]))
{
self::$m_aQueriesLog[$sQueryId]['data'] = serialize($aQueryData);
self::$m_aQueriesLog[$sQueryId]['oql'] = $sOql;
self::$m_aQueriesLog[$sQueryId]['hits'] = 1;
}
else
{
self::$m_aQueriesLog[$sQueryId]['hits']++;
}
if(!isset(self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]))
{
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['sql'] = $sSql;
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['count'] = 1;
$iTableCount = count(CMDBSource::ExplainQuery($sSql));
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['table_count'] = $iTableCount;
}
else
{
self::$m_aQueriesLog[$sQueryId]['queries'][$sMySQLQueryId]['count']++;
}
}
}
public static function RecordQueryTrace()
{
if (!self::$m_bTraceQueries)
{
return;
}
$iOqlCount = count(self::$m_aQueriesLog);
$iSqlCount = 0;
foreach (self::$m_aQueriesLog as $sQueryId => $aOqlData)
{
$iSqlCount += $aOqlData['hits'];
}
$sHtml = "<h2>Stats on SELECT queries: OQL=$iOqlCount, SQL=$iSqlCount</h2>\n";
foreach (self::$m_aQueriesLog as $sQueryId => $aOqlData)
{
$sOql = $aOqlData['oql'];
$sHits = $aOqlData['hits'];
$sHtml .= "<p><b>$sHits</b> hits for OQL query: $sOql</p>\n";
$sHtml .= "<ul id=\"ClassesRelationships\" class=\"treeview\">\n";
foreach($aOqlData['queries'] as $aSqlData)
{
$sQuery = $aSqlData['sql'];
$sSqlHits = $aSqlData['count'];
$iTableCount = $aSqlData['table_count'];
$sHtml .= "<li><b>$sSqlHits</b> hits for SQL ($iTableCount tables): <pre style=\"font-size:60%\">$sQuery</pre></li>\n";
}
$sHtml .= "</ul>\n";
}
$sLogFile = 'queries.latest';
file_put_contents(APPROOT.'data/'.$sLogFile.'.html', $sHtml);
$sLog = "<?php\n\$aQueriesLog = ".var_export(self::$m_aQueriesLog, true).";";
file_put_contents(APPROOT.'data/'.$sLogFile.'.log', $sLog);
// Cumulate the queries
$sAllQueries = APPROOT.'data/queries.log';
if (file_exists($sAllQueries))
{
// Merge the new queries into the existing log
include($sAllQueries);
$aQueriesLog = array();
foreach (self::$m_aQueriesLog as $sQueryId => $aOqlData)
{
if (!array_key_exists($sQueryId, $aQueriesLog))
{
$aQueriesLog[$sQueryId] = $aOqlData;
}
}
}
else
{
$aQueriesLog = self::$m_aQueriesLog;
}
$sLog = "<?php\n\$aQueriesLog = ".var_export($aQueriesLog, true).";";
file_put_contents($sAllQueries, $sLog);
}
protected static function DbgTrace($value)
{
if (!self::$m_bDebugQuery)
{
return;
}
$aBacktrace = debug_backtrace();
$iCallStackPos = count($aBacktrace) - self::$m_bDebugQuery;
$sIndent = "";
for ($i = 0 ; $i < $iCallStackPos ; $i++)
{
$sIndent .= " .-=^=-. ";
}
$aCallers = array();
foreach($aBacktrace as $aStackInfo)
{
$aCallers[] = $aStackInfo["function"];
}
$sCallers = "Callstack: ".implode(', ', $aCallers);
$sFunction = "<b title=\"$sCallers\">".$aBacktrace[1]["function"]."</b>";
if (is_object($value))
{
echo "$sIndent$sFunction:\n<pre>\n";
print_r($value);
echo "</pre>\n";
}
else
{
echo "$sIndent$sFunction: $value<br/>\n";
}
}
/**
* Experimental!
* todo: implement the change tracking
*
* @param $bArchive
* @throws Exception
*/
function DBBulkWriteArchiveFlag($bArchive)
{
$sClass = $this->GetClass();
if (!MetaModel::IsArchivable($sClass))
{
throw new Exception($sClass.' is not an archivable class');
}
$iFlag = $bArchive ? 1 : 0;
$oSet = new DBObjectSet($this);
if (MetaModel::IsStandaloneClass($sClass))
{
$oSet->OptimizeColumnLoad(array($this->GetClassAlias() => array('')));
$aIds = array($sClass => $oSet->GetColumnAsArray('id'));
}
else
{
$oSet->OptimizeColumnLoad(array($this->GetClassAlias() => array('finalclass')));
$aTemp = $oSet->GetColumnAsArray('finalclass');
$aIds = array();
foreach ($aTemp as $iObjectId => $sObjectClass)
{
$aIds[$sObjectClass][$iObjectId] = $iObjectId;
}
}
foreach ($aIds as $sFinalClass => $aObjectIds)
{
$sIds = implode(', ', $aObjectIds);
$sArchiveRoot = MetaModel::GetAttributeOrigin($sFinalClass, 'archive_flag');
$sRootTable = MetaModel::DBGetTable($sArchiveRoot);
$sRootKey = MetaModel::DBGetKey($sArchiveRoot);
$aJoins = array("`$sRootTable`");
$aUpdates = array();
foreach (MetaModel::EnumParentClasses($sFinalClass, ENUM_PARENT_CLASSES_ALL) as $sParentClass)
{
if (!MetaModel::IsValidAttCode($sParentClass, 'archive_flag'))
{
continue;
}
$sTable = MetaModel::DBGetTable($sParentClass);
$aUpdates[] = "`$sTable`.`archive_flag` = $iFlag";
if ($sParentClass == $sArchiveRoot)
{
if ($bArchive)
{
// Set the date (do not change it)
$sDate = '"'.date(AttributeDate::GetSQLFormat()).'"';
$aUpdates[] = "`$sTable`.`archive_date` = coalesce(`$sTable`.`archive_date`, $sDate)";
}
else
{
// Reset the date
$aUpdates[] = "`$sTable`.`archive_date` = null";
}
}
else
{
$sKey = MetaModel::DBGetKey($sParentClass);
$aJoins[] = "`$sTable` ON `$sTable`.`$sKey` = `$sRootTable`.`$sRootKey`";
}
}
$sJoins = implode(' INNER JOIN ', $aJoins);
$sValues = implode(', ', $aUpdates);
$sUpdateQuery = "UPDATE $sJoins SET $sValues WHERE `$sRootTable`.`$sRootKey` IN ($sIds)";
CMDBSource::Query($sUpdateQuery);
}
}
public function UpdateContextFromUser()
{
$this->SetShowObsoleteData(utils::ShowObsoleteData());
}
}

View File

@@ -1,601 +0,0 @@
<?php
// Copyright (C) 2015-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* A union of DBObjectSearches
*
* @copyright Copyright (C) 2015-2017 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class DBUnionSearch extends DBSearch
{
protected $aSearches; // source queries
protected $aSelectedClasses; // alias => classes (lowest common ancestors) computed at construction
public function __construct($aSearches)
{
if (count ($aSearches) == 0)
{
throw new CoreException('A DBUnionSearch must be made of at least one search');
}
$this->aSearches = array();
foreach ($aSearches as $oSearch)
{
if ($oSearch instanceof DBUnionSearch)
{
foreach ($oSearch->aSearches as $oSubSearch)
{
$this->aSearches[] = $oSubSearch->DeepClone();
}
}
else
{
$this->aSearches[] = $oSearch->DeepClone();
}
}
$this->ComputeSelectedClasses();
}
public function AllowAllData()
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AllowAllData();
}
}
public function IsAllDataAllowed()
{
foreach ($this->aSearches as $oSearch)
{
if ($oSearch->IsAllDataAllowed() === false) return false;
}
return true;
}
public function SetArchiveMode($bEnable)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->SetArchiveMode($bEnable);
}
parent::SetArchiveMode($bEnable);
}
public function SetShowObsoleteData($bShow)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->SetShowObsoleteData($bShow);
}
parent::SetShowObsoleteData($bShow);
}
/**
* Find the lowest common ancestor for each of the selected class
*/
protected function ComputeSelectedClasses()
{
// 1 - Collect all the column/classes
$aColumnToClasses = array();
foreach ($this->aSearches as $iPos => $oSearch)
{
$aSelected = array_values($oSearch->GetSelectedClasses());
if ($iPos != 0)
{
if (count($aSelected) < count($aColumnToClasses))
{
throw new Exception('Too few selected classes in the subquery #'.($iPos+1));
}
if (count($aSelected) > count($aColumnToClasses))
{
throw new Exception('Too many selected classes in the subquery #'.($iPos+1));
}
}
foreach ($aSelected as $iColumn => $sClass)
{
$aColumnToClasses[$iColumn][] = $sClass;
}
}
// 2 - Build the index column => alias
$oFirstSearch = $this->aSearches[0];
$aColumnToAlias = array_keys($oFirstSearch->GetSelectedClasses());
// 3 - Compute alias => lowest common ancestor
$this->aSelectedClasses = array();
foreach ($aColumnToClasses as $iColumn => $aClasses)
{
$sAlias = $aColumnToAlias[$iColumn];
$sAncestor = MetaModel::GetLowestCommonAncestor($aClasses);
if (is_null($sAncestor))
{
throw new Exception('Could not find a common ancestor for the column '.($iColumn+1).' (Classes: '.implode(', ', $aClasses).')');
}
$this->aSelectedClasses[$sAlias] = $sAncestor;
}
}
public function GetSearches()
{
return $this->aSearches;
}
/**
* Limited to the selected classes
*/
public function GetClassName($sAlias)
{
if (array_key_exists($sAlias, $this->aSelectedClasses))
{
return $this->aSelectedClasses[$sAlias];
}
else
{
throw new CoreException("Invalid class alias '$sAlias'");
}
}
public function GetClass()
{
return reset($this->aSelectedClasses);
}
public function GetClassAlias()
{
reset($this->aSelectedClasses);
return key($this->aSelectedClasses);
}
/**
* Change the class (only subclasses are supported as of now, because the conditions must fit the new class)
* Defaults to the first selected class
* Only the selected classes can be changed
*/
public function ChangeClass($sNewClass, $sAlias = null)
{
if (is_null($sAlias))
{
$sAlias = $this->GetClassAlias();
}
elseif (!array_key_exists($sAlias, $this->aSelectedClasses))
{
// discard silently - necessary when recursing (??? copied from DBObjectSearch)
return;
}
// 1 - identify the impacted column
$iColumn = array_search($sAlias, array_keys($this->aSelectedClasses));
// 2 - change for each search
foreach ($this->aSearches as $oSearch)
{
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
$sSearchAlias = $aSearchAliases[$iColumn];
$oSearch->ChangeClass($sNewClass, $sSearchAlias);
}
// 3 - record the change
$this->aSelectedClasses[$sAlias] = $sNewClass;
}
public function GetSelectedClasses()
{
return $this->aSelectedClasses;
}
/**
* @param array $aSelectedClasses array of aliases
* @throws CoreException
*/
public function SetSelectedClasses($aSelectedClasses)
{
// 1 - change for each search
foreach ($this->aSearches as $oSearch)
{
// Throws an exception if not valid
$oSearch->SetSelectedClasses($aSelectedClasses);
}
// 2 - update the lowest common ancestors
$this->ComputeSelectedClasses();
}
/**
* Change any alias of the query tree
*
* @param $sOldName
* @param $sNewName
* @return bool True if the alias has been found and changed
*/
public function RenameAlias($sOldName, $sNewName)
{
$bRet = false;
foreach ($this->aSearches as $oSearch)
{
$bRet = $oSearch->RenameAlias($sOldName, $sNewName) || $bRet;
}
return $bRet;
}
public function IsAny()
{
$bIsAny = true;
foreach ($this->aSearches as $oSearch)
{
if (!$oSearch->IsAny())
{
$bIsAny = false;
break;
}
}
return $bIsAny;
}
public function ResetCondition()
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->ResetCondition();
}
}
public function MergeConditionExpression($oExpression)
{
$aAliases = array_keys($this->aSelectedClasses);
foreach ($this->aSearches as $iSearchIndex => $oSearch)
{
$oClonedExpression = $oExpression->DeepClone();
if ($iSearchIndex != 0)
{
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
{
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
}
}
$oSearch->MergeConditionExpression($oClonedExpression);
}
}
public function AddConditionExpression($oExpression)
{
$aAliases = array_keys($this->aSelectedClasses);
foreach ($this->aSearches as $iSearchIndex => $oSearch)
{
$oClonedExpression = $oExpression->DeepClone();
if ($iSearchIndex != 0)
{
foreach (array_keys($oSearch->GetSelectedClasses()) as $iColumn => $sSearchAlias)
{
$oClonedExpression->RenameAlias($aAliases[$iColumn], $sSearchAlias);
}
}
$oSearch->AddConditionExpression($oClonedExpression);
}
}
public function AddNameCondition($sName)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddNameCondition($sName);
}
}
public function AddCondition($sFilterCode, $value, $sOpCode = null)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition($sFilterCode, $value, $sOpCode);
}
}
/**
* Specify a condition on external keys or link sets
* @param sAttSpec Can be either an attribute code or extkey->[sAttSpec] or linkset->[sAttSpec] and so on, recursively
* Example: infra_list->ci_id->location_id->country
* @param value The value to match (can be an array => IN(val1, val2...)
* @return void
*/
public function AddConditionAdvanced($sAttSpec, $value)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddConditionAdvanced($sAttSpec, $value);
}
}
public function AddCondition_FullText($sFullText)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_FullText($sFullText);
}
}
/**
* @param DBObjectSearch $oFilter
* @param $sExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
* @throws CoreException
* @throws CoreWarning
*/
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
}
/**
* @param DBObjectSearch $oFilter
* @param $sForeignExtKeyAttCode
* @param int $iOperatorCode
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
*/
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
}
}
public function Intersect(DBSearch $oFilter)
{
$aSearches = array();
foreach ($this->aSearches as $oSearch)
{
$aSearches[] = $oSearch->Intersect($oFilter);
}
return new DBUnionSearch($aSearches);
}
public function SetInternalParams($aParams)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->SetInternalParams($aParams);
}
}
public function GetInternalParams()
{
$aParams = array();
foreach ($this->aSearches as $oSearch)
{
$aParams = array_merge($oSearch->GetInternalParams(), $aParams);
}
return $aParams;
}
public function GetQueryParams($bExcludeMagicParams = true)
{
$aParams = array();
foreach ($this->aSearches as $oSearch)
{
$aParams = array_merge($oSearch->GetQueryParams($bExcludeMagicParams), $aParams);
}
return $aParams;
}
public function ListConstantFields()
{
// Somewhat complex to implement for unions, for a poor benefit
return array();
}
/**
* Turn the parameters (:xxx) into scalar values in order to easily
* serialize a search
*/
public function ApplyParameters($aArgs)
{
foreach ($this->aSearches as $oSearch)
{
$oSearch->ApplyParameters($aArgs);
}
}
/**
* Overloads for query building
*/
public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false)
{
$aSubQueries = array();
foreach ($this->aSearches as $oSearch)
{
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams, $bWithAllowAllFlag);
}
$sRet = implode(' UNION ', $aSubQueries);
return $sRet;
}
/**
* Returns a new DBUnionSearch object where duplicates queries have been removed based on their OQLs
*
* @return \DBUnionSearch
*/
public function RemoveDuplicateQueries()
{
$aQueries = array();
$aSearches = array();
foreach ($this->GetSearches() as $oTmpSearch)
{
$sQuery = $oTmpSearch->ToOQL(true);
if (!in_array($sQuery, $aQueries))
{
$aQueries[] = $sQuery;
$aSearches[] = $oTmpSearch;
}
}
$oNewSearch = new DBUnionSearch($aSearches);
return $oNewSearch;
}
////////////////////////////////////////////////////////////////////////////
//
// Construction of the SQL queries
//
////////////////////////////////////////////////////////////////////////////
public function MakeDeleteQuery($aArgs = array())
{
throw new Exception('MakeDeleteQuery is not implemented for the unions!');
}
public function MakeUpdateQuery($aValues, $aArgs = array())
{
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
}
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
{
if (count($this->aSearches) == 1)
{
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectExpr);
}
$aSQLQueries = array();
$aAliases = array_keys($this->aSelectedClasses);
$aQueryAttToLoad = null;
$aUnionQuerySelectExpr = array();
foreach ($this->aSearches as $iSearch => $oSearch)
{
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
// The selected classes from the query build perspective are the lowest common ancestors amongst the various queries
// (used when it comes to determine which attributes must be selected)
$aSearchSelectedClasses = array();
foreach ($aSearchAliases as $iColumn => $sSearchAlias)
{
$sAlias = $aAliases[$iColumn];
$aSearchSelectedClasses[$sSearchAlias] = $this->aSelectedClasses[$sAlias];
}
if ($bGetCount)
{
// Select only ids for the count to allow optimization of joins
foreach($aSearchAliases as $sSearchAlias)
{
$aQueryAttToLoad[$sSearchAlias] = array();
}
}
else
{
if (is_null($aAttToLoad))
{
$aQueryAttToLoad = null;
}
else
{
// (Eventually) Transform the aliases
$aQueryAttToLoad = array();
foreach($aAttToLoad as $sAlias => $aAttributes)
{
$iColumn = array_search($sAlias, $aAliases);
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
}
}
}
if (is_null($aGroupByExpr))
{
$aQueryGroupByExpr = null;
}
else
{
// Clone (and eventually transform) the group by expressions
$aQueryGroupByExpr = array();
$aTranslationData = array();
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
foreach ($aAliases as $iColumn => $sAlias)
{
$sQueryAlias = $aQueryColumns[$iColumn];
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
$aQueryGroupByExpr[$sAlias.'id'] = new FieldExpression('id', $sQueryAlias);
}
foreach ($aGroupByExpr as $sExpressionAlias => $oExpression)
{
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
}
}
if (is_null($aSelectExpr))
{
$aQuerySelectExpr = null;
}
else
{
$aQuerySelectExpr = array();
$aTranslationData = array();
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
foreach($aAliases as $iColumn => $sAlias)
{
$sQueryAlias = $aQueryColumns[$iColumn];
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
}
foreach($aSelectExpr as $sExpressionAlias => $oExpression)
{
$oExpression->Browse(function ($oNode) use (&$aQuerySelectExpr, &$aTranslationData)
{
if ($oNode instanceof FieldExpression)
{
$sAlias = $oNode->GetParent()."__".$oNode->GetName();
if (!key_exists($sAlias, $aQuerySelectExpr))
{
$aQuerySelectExpr[$sAlias] = $oNode->Translate($aTranslationData, false, false);
}
$aTranslationData[$oNode->GetParent()][$oNode->GetName()] = new FieldExpression($sAlias);
}
});
// Only done for the first select as aliases are named after the first query
if (!array_key_exists($sExpressionAlias, $aUnionQuerySelectExpr))
{
$aUnionQuerySelectExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
}
}
}
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses, $aQuerySelectExpr);
if (count($aSearchAliases) > 1)
{
// Necessary to make sure that selected columns will match throughout all the queries
// (default order of selected fields depending on the order of JOINS)
$oSubQuery->SortSelectedFields();
}
$aSQLQueries[] = $oSubQuery;
}
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr, $aUnionQuerySelectExpr);
//MyHelpers::var_dump_html($oSQLQuery, true);
//MyHelpers::var_dump_html($oSQLQuery->RenderSelect(), true);
if (self::$m_bDebugQuery) $oSQLQuery->DisplayHtml();
return $oSQLQuery;
}
}

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2013 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Algorithm to delete object(s) and maintain data integrity
* Class dbObject: the root of persistent classes
*
* @copyright Copyright (C) 2010-2013 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class DeleteException extends CoreException
@@ -82,9 +81,6 @@ class DeletionPlan
public function ComputeResults()
{
$this->m_iToDelete = 0;
$this->m_iToUpdate = 0;
foreach($this->m_aToDelete as $sClass => $aToDelete)
{
foreach($aToDelete as $iId => $aData)
@@ -101,22 +97,16 @@ class DeletionPlan
}
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;
}
}
}
// Getting and setting time limit are not symetric:
// 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)
{
set_time_limit($iLoopTimeLimit);
$this->m_iToUpdate++;
$oObject = $aData['to_reset'];
@@ -140,9 +130,9 @@ class DeletionPlan
$this->m_bFoundSecurityIssue = true;
}
}
}
}
set_time_limit($iPreviousTimeLimit);
}
public function GetIssues()

View File

@@ -1,282 +0,0 @@
<?php
/**
* Copyright (c) 2010-2018 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*
*/
/**
* Design document and associated nodes
* @package Core
*/
namespace Combodo\iTop;
use \DOMDocument;
use \DOMFormatException;
/**
* Class \Combodo\iTop\DesignDocument
*
* A design document is the DOM tree that modelize behaviors. One of its
* characteristics is that it can be altered by the mean of the same kind of document.
*
*/
class DesignDocument extends DOMDocument
{
/**
* @throws \Exception
*/
public function __construct()
{
parent::__construct('1.0', 'UTF-8');
$this->Init();
}
/**
* Overloadable. Called prior to data loading.
*/
protected function Init()
{
$this->registerNodeClass('DOMElement', '\Combodo\iTop\DesignElement');
$this->formatOutput = true; // indent (must be loaded with option LIBXML_NOBLANKS)
$this->preserveWhiteSpace = true; // otherwise the formatOutput option would have no effect
}
/**
* Overload of the standard API
*
* @param $filename
* @param int $options
*/
public function load($filename, $options = 0)
{
parent::load($filename, LIBXML_NOBLANKS);
}
/**
* Overload of the standard API
*
* @param $filename
* @param int $options
*
* @return int
*/
public function save($filename, $options = 0)
{
$this->documentElement->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
return parent::save($filename, LIBXML_NOBLANKS);
}
/**
* Create an HTML representation of the DOM, for debugging purposes
* @param bool|false $bReturnRes Echoes or returns the HTML representation
* @return mixed void or the HTML representation of the DOM
*/
public function Dump($bReturnRes = false)
{
$sXml = $this->saveXML();
if ($bReturnRes)
{
return $sXml;
}
echo "<pre>\n";
echo htmlentities($sXml);
echo "</pre>\n";
return '';
}
/**
* Quote and escape strings for use within an XPath expression
* Usage: DesignDocument::GetNodes('class[@id='.DesignDocument::XPathQuote($sId).']');
* @param string $sValue The value to be quoted
* @return string to be used within an XPath
*/
public static function XPathQuote($sValue)
{
if (strpos($sValue, '"') !== false)
{
$aParts = explode('"', $sValue);
$sRet = 'concat("'.implode('", \'"\', "', $aParts).'")';
}
else
{
$sRet = '"'.$sValue.'"';
}
return $sRet;
}
/**
* Extracts some nodes from the DOM
* @param string $sXPath A XPath expression
* @param DesignElement $oContextNode The node to start the search from
* @return \DOMNodeList
*/
public function GetNodes($sXPath, $oContextNode = null)
{
$oXPath = new \DOMXPath($this);
if (is_null($oContextNode))
{
$oResult = $oXPath->query($sXPath);
}
else
{
$oResult = $oXPath->query($sXPath, $oContextNode);
}
return $oResult;
}
/**
* An alternative to getNodePath, that gives the id of nodes instead of the position within the children
* @param DesignElement $oNode The node to describe
* @return string
*/
public static function GetItopNodePath($oNode)
{
if ($oNode instanceof \DOMDocument) return '';
if (is_null($oNode)) return '';
$sId = $oNode->getAttribute('id');
$sNodeDesc = ($sId != '') ? $oNode->nodeName.'['.$sId.']' : $oNode->nodeName;
return self::GetItopNodePath($oNode->parentNode).'/'.$sNodeDesc;
}
}
/**
* DesignElement: helper to read/change the DOM
* @package ModelFactory
*/
class DesignElement extends \DOMElement
{
/**
* Extracts some nodes from the DOM
* @param string $sXPath A XPath expression
* @return \DOMNodeList
*/
public function GetNodes($sXPath)
{
return $this->ownerDocument->GetNodes($sXPath, $this);
}
/**
* Create an HTML representation of the DOM, for debugging purposes
*
* @param bool|false $bReturnRes Echoes or returns the HTML representation
*
* @return mixed void or the HTML representation of the DOM
* @throws \Exception
*/
public function Dump($bReturnRes = false)
{
$oDoc = new DesignDocument();
$oClone = $oDoc->importNode($this->cloneNode(true), true);
$oDoc->appendChild($oClone);
$sXml = $oDoc->saveXML($oClone);
if ($bReturnRes)
{
return $sXml;
}
echo "<pre>\n";
echo htmlentities($sXml);
echo "</pre>\n";
return '';
}
/**
* Returns the node directly under the given node
* @param $sTagName
* @param bool|true $bMustExist
* @return \MFElement
* @throws DOMFormatException
*/
public function GetUniqueElement($sTagName, $bMustExist = true)
{
$oNode = null;
foreach($this->childNodes as $oChildNode)
{
if ($oChildNode->nodeName == $sTagName)
{
$oNode = $oChildNode;
break;
}
}
if ($bMustExist && is_null($oNode))
{
throw new DOMFormatException('Missing unique tag: '.$sTagName);
}
return $oNode;
}
/**
* Returns the node directly under the current node, or null if missing
* @param $sTagName
* @return \MFElement
* @throws DOMFormatException
*/
public function GetOptionalElement($sTagName)
{
return $this->GetUniqueElement($sTagName, false);
}
/**
* Returns the TEXT of the current node (possibly from several child nodes)
* @param null $sDefault
* @return null|string
*/
public function GetText($sDefault = null)
{
$sText = null;
foreach($this->childNodes as $oChildNode)
{
if ($oChildNode instanceof \DOMText)
{
if (is_null($sText)) $sText = '';
$sText .= $oChildNode->wholeText;
}
}
if (is_null($sText))
{
return $sDefault;
}
else
{
return $sText;
}
}
/**
* Get the TEXT value from a child node
*
* @param string $sTagName
* @param string|null $sDefault
*
* @return string
* @throws \DOMFormatException
*/
public function GetChildText($sTagName, $sDefault = null)
{
$sRet = $sDefault;
if ($oChild = $this->GetOptionalElement($sTagName))
{
$sRet = $oChild->GetText($sDefault);
}
return $sRet;
}
}

View File

@@ -1,27 +1,27 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Class Dict
* Management of localizable strings
*
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class DictException extends CoreException
@@ -57,19 +57,27 @@ define('DICT_ERR_EXCEPTION', 2); // when a string is missing, throw an exception
class Dict
{
protected static $m_bTraceFiles = false;
protected static $m_aEntryFiles = array();
protected static $m_iErrorMode = DICT_ERR_STRING;
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_sApplicationPrefix = null;
/**
* @param $sLanguageCode
*
* @throws \DictExceptionUnknownLanguage
*/
public static function EnableTraceFiles()
{
self::$m_bTraceFiles = true;
}
public static function GetEntryFiles()
{
return self::$m_aEntryFiles;
}
public static function SetDefaultLanguage($sLanguageCode)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
@@ -79,11 +87,6 @@ class Dict
self::$m_sDefaultLanguage = $sLanguageCode;
}
/**
* @param $sLanguageCode
*
* @throws \DictExceptionUnknownLanguage
*/
public static function SetUserLanguage($sLanguageCode)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
@@ -116,24 +119,14 @@ class Dict
self::$m_iErrorMode = $iErrorMode;
}
/**
* Returns a localised string from the dictonary
*
* @param string $sStringCode The code identifying the dictionary entry
* @param string $sDefault Default value if there is no match in the dictionary
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
*
* @return string
*/
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
public static function S($sStringCode, $sDefault = null)
{
// Attempt to find the string in the user language
//
self::InitLangIfNeeded(self::GetUserLanguage());
if (!array_key_exists(self::GetUserLanguage(), self::$m_aData))
{
// It may happen, when something happens before the dictionaries get loaded
// It may happen, when something happens before the dictionnaries get loaded
return $sStringCode;
}
$aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
@@ -141,44 +134,44 @@ class Dict
{
return $aCurrentDictionary[$sStringCode];
}
if (!$bUserLanguageOnly)
// Attempt to find the string in the default language
//
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
if (array_key_exists($sStringCode, $aDefaultDictionary))
{
// Attempt to find the string in the default language
//
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
if (array_key_exists($sStringCode, $aDefaultDictionary))
{
return $aDefaultDictionary[$sStringCode];
}
// Attempt to find the string in english
//
self::InitLangIfNeeded('EN US');
$aDefaultDictionary = self::$m_aData['EN US'];
if (array_key_exists($sStringCode, $aDefaultDictionary))
{
return $aDefaultDictionary[$sStringCode];
}
return $aDefaultDictionary[$sStringCode];
}
// Attempt to find the string in english
//
$aDefaultDictionary = self::$m_aData['EN US'];
if (array_key_exists($sStringCode, $aDefaultDictionary))
{
return $aDefaultDictionary[$sStringCode];
}
// Could not find the string...
//
if (is_null($sDefault))
switch (self::$m_iErrorMode)
{
return $sStringCode;
}
case DICT_ERR_STRING:
if (is_null($sDefault))
{
return $sStringCode;
}
else
{
return $sDefault;
}
break;
return $sDefault;
case DICT_ERR_EXCEPTION:
default:
throw new DictExceptionMissingString(self::$m_sCurrentLanguage, $sStringCode);
break;
}
return 'bug!';
}
/**
* Formats a localized string with numbered placeholders (%1$s...) for the additional arguments
* See vsprintf for more information about the syntax of the placeholders
* @param string $sFormatCode
* @return string
*/
public static function Format($sFormatCode /*, ... arguments ....*/)
{
$sLocalizedFormat = self::S($sFormatCode);
@@ -193,98 +186,43 @@ class Dict
return vsprintf($sLocalizedFormat, $aArguments);
}
/**
* Initialize a the entries for a given language (replaces the former Add() method)
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
* @param hash $aEntries Hash array of dictionnary entries
*/
public static function SetEntries($sLanguageCode, $aEntries)
// sLanguageCode: Code identifying the language i.e. FR-FR
// sEnglishLanguageDesc: Description of the language code, in English. i.e. French (France)
// sLocalizedLanguageDesc: Description of the language code, in its own language. i.e. Français (France)
// aEntries: Hash array of dictionnary entries
// ~~ or ~* can be used to indicate entries still to be translated.
public static function Add($sLanguageCode, $sEnglishLanguageDesc, $sLocalizedLanguageDesc, $aEntries)
{
self::$m_aData[$sLanguageCode] = $aEntries;
}
/**
* Set the list of available languages
* @param hash $aLanguagesList
*/
public static function SetLanguagesList($aLanguagesList)
{
self::$m_aLanguages = $aLanguagesList;
}
/**
* Load a language from the language dictionary, if not already loaded
* @param string $sLangCode Language code
* @return boolean
*/
public static function InitLangIfNeeded($sLangCode)
{
if (array_key_exists($sLangCode, self::$m_aData)) return true;
$bResult = false;
if (function_exists('apc_fetch') && (self::$m_sApplicationPrefix !== null))
if (self::$m_bTraceFiles)
{
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
self::$m_aData[$sLangCode] = apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode);
if (self::$m_aData[$sLangCode] === false)
$aBacktrace = debug_backtrace();
$sFile = $aBacktrace[0]["file"];
foreach($aEntries as $sKey => $sValue)
{
unset(self::$m_aData[$sLangCode]);
}
else
{
$bResult = true;
self::$m_aEntryFiles[$sLanguageCode][$sKey] = array(
'file' => $sFile,
'value' => $sValue
);
}
}
if (!$bResult)
{
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
require_once($sDictFile);
if (function_exists('apc_store') && (self::$m_sApplicationPrefix !== null))
{
apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]);
}
$bResult = true;
}
return $bResult;
}
/**
* Enable caching (cached using APC)
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
*/
public static function EnableCache($sApplicationPrefix)
{
self::$m_sApplicationPrefix = $sApplicationPrefix;
}
/**
* Reset the cached entries (cached using APC)
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
*/
public static function ResetCache($sApplicationPrefix)
{
if (function_exists('apc_delete'))
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
foreach(self::$m_aLanguages as $sLang => $void)
{
apc_delete($sApplicationPrefix.'-dict-'.$sLang);
}
self::$m_aLanguages[$sLanguageCode] = array('description' => $sEnglishLanguageDesc, 'localized_description' => $sLocalizedLanguageDesc);
self::$m_aData[$sLanguageCode] = array();
}
foreach($aEntries as $sCode => $sValue)
{
self::$m_aData[$sLanguageCode][$sCode] = self::FilterString($sValue);
}
}
/////////////////////////////////////////////////////////////////////////
/**
* Clone a string in every language (if it exists in that language)
*
* @param $sSourceCode
* @param $sDestCode
*/
*/
public static function CloneString($sSourceCode, $sDestCode)
{
foreach(self::$m_aLanguages as $sLanguageCode => $foo)
@@ -295,14 +233,14 @@ class Dict
}
}
}
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
{
$aMissing = array(); // Strings missing for the target language
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
$aNotTranslated = array(); // Strings having the same value in both dictionaries
$aOK = array(); // Strings having different values in both dictionaries
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
@@ -310,7 +248,7 @@ class Dict
$aMissing[$sStringCode] = $sValue;
}
}
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
@@ -338,56 +276,57 @@ class Dict
{
MyHelpers::var_dump_html(self::$m_aData);
}
// Only used by the setup to determine the list of languages to display in the initial setup screen
// otherwise replaced by LoadModule by its own handler
// sLanguageCode: Code identifying the language i.e. FR-FR
// sEnglishLanguageDesc: Description of the language code, in English. i.e. French (France)
// sLocalizedLanguageDesc: Description of the language code, in its own language. i.e. Français (France)
// aEntries: Hash array of dictionnary entries
// ~~ or ~* can be used to indicate entries still to be translated.
public static function Add($sLanguageCode, $sEnglishLanguageDesc, $sLocalizedLanguageDesc, $aEntries)
public static function InCache($sApplicationPrefix)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
if (function_exists('apc_fetch'))
{
self::$m_aLanguages[$sLanguageCode] = array('description' => $sEnglishLanguageDesc, 'localized_description' => $sLocalizedLanguageDesc);
self::$m_aData[$sLanguageCode] = array();
$bResult = false;
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
self::$m_aData = apc_fetch($sApplicationPrefix.'-dict');
if (is_bool(self::$m_aData) && (self::$m_aData === false))
{
self::$m_aData = array();
}
else
{
self::$m_aLanguages = apc_fetch($sApplicationPrefix.'-languages');
if (is_bool(self::$m_aLanguages) && (self::$m_aLanguages === false))
{
self::$m_aLanguages = array();
}
else
{
$bResult = true;
}
}
return $bResult;
}
// No need to actually load the strings since it's only used to know the list of languages
// at setup time !!
return false;
}
/**
* Export all the dictionary entries - of the given language - whose code matches the given prefix
* missing entries in the current language will be replaced by entries in the default language
* @param string $sStartingWith
* @return string[]
*/
public static function ExportEntries($sStartingWith)
public static function InitCache($sApplicationPrefix)
{
self::InitLangIfNeeded(self::GetUserLanguage());
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aEntries = array();
$iLength = strlen($sStartingWith);
// First prefill the array with entries from the default language
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
if (function_exists('apc_store'))
{
if (substr($sCode, 0, $iLength) == $sStartingWith)
{
$aEntries[$sCode] = $sEntry;
}
apc_store($sApplicationPrefix.'-languages', self::$m_aLanguages);
apc_store($sApplicationPrefix.'-dict', self::$m_aData);
}
// Now put (overwrite) the entries for the user language
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
}
public static function ResetCache($sApplicationPrefix)
{
if (function_exists('apc_delete'))
{
if (substr($sCode, 0, $iLength) == $sStartingWith)
{
$aEntries[$sCode] = $sEntry;
}
apc_delete($sApplicationPrefix.'-languages');
apc_delete($sApplicationPrefix.'-dict');
}
return $aEntries;
}
protected static function FilterString($s)
{
return str_replace(array('~~', '~*'), '', $s);
}
}
?>

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,26 @@
<?php
// Copyright (C) 2010-2016 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Send an email (abstraction for synchronous/asynchronous modes)
*
* @copyright Copyright (C) 2010-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
require_once(APPROOT.'/lib/swiftmailer/lib/swift_required.php');
@@ -33,15 +32,10 @@ define ('EMAIL_SEND_OK', 0);
define ('EMAIL_SEND_PENDING', 1);
define ('EMAIL_SEND_ERROR', 2);
class EMail
{
// Serialization formats
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)
protected static $m_oConfig = null;
protected $m_aData; // For storing data to serialize
public function LoadConfig($sConfigFile = ITOP_DEFAULT_CONFIG_FILE)
{
@@ -51,93 +45,17 @@ class EMail
}
}
protected $m_oMessage;
public function __construct()
{
$this->m_aData = array();
$this->m_oMessage = Swift_Message::newInstance();
$this->SetRecipientFrom(MetaModel::GetConfig()->Get('email_default_sender_address'), MetaModel::GetConfig()->Get('email_default_sender_label'));
$oEncoder = new Swift_Mime_ContentEncoder_PlainContentEncoder('8bit');
$this->m_oMessage->setEncoder($oEncoder);
}
/**
* Custom serialization method
* No longer use the brute force "serialize" method since
* 1) It does not work with binary attachments (since they cannot be stored in a UTF-8 text field)
* 2) The size tends to be quite big (sometimes ten times the size of the email)
*/
public function SerializeV2()
{
return serialize($this->m_aData);
}
/**
* Custom de-serialization method
* @param string $sSerializedMessage The serialized representation of the message
*/
static public function UnSerializeV2($sSerializedMessage)
{
$aData = unserialize($sSerializedMessage);
$oMessage = new Email();
if (array_key_exists('body', $aData))
{
$oMessage->SetBody($aData['body']['body'], $aData['body']['mimeType']);
}
if (array_key_exists('message_id', $aData))
{
$oMessage->SetMessageId($aData['message_id']);
}
if (array_key_exists('bcc', $aData))
{
$oMessage->SetRecipientBCC($aData['bcc']);
}
if (array_key_exists('cc', $aData))
{
$oMessage->SetRecipientCC($aData['cc']);
}
if (array_key_exists('from', $aData))
{
$oMessage->SetRecipientFrom($aData['from']['address'], $aData['from']['label']);
}
if (array_key_exists('reply_to', $aData))
{
$oMessage->SetRecipientReplyTo($aData['reply_to']);
}
if (array_key_exists('to', $aData))
{
$oMessage->SetRecipientTO($aData['to']);
}
if (array_key_exists('subject', $aData))
{
$oMessage->SetSubject($aData['subject']);
}
if (array_key_exists('headers', $aData))
{
foreach($aData['headers'] as $sKey => $sValue)
{
$oMessage->AddToHeader($sKey, $sValue);
}
}
if (array_key_exists('parts', $aData))
{
foreach($aData['parts'] as $aPart)
{
$oMessage->AddPart($aPart['text'], $aPart['mimeType']);
}
}
if (array_key_exists('attachments', $aData))
{
foreach($aData['attachments'] as $aAttachment)
{
$oMessage->AddAttachment(base64_decode($aAttachment['data']), $aAttachment['filename'], $aAttachment['mimeType']);
}
}
return $oMessage;
}
protected function SendAsynchronous(&$aIssues, $oLog = null)
{
try
@@ -155,9 +73,6 @@ class EMail
protected function SendSynchronous(&$aIssues, $oLog = null)
{
// If the body of the message is in HTML, embed all images based on attachments
$this->EmbedInlineImages();
$this->LoadConfig();
$sTransport = self::$m_oConfig->Get('email_transport');
@@ -178,15 +93,6 @@ class EMail
}
break;
case 'Null':
$oTransport = Swift_NullTransport::newInstance();
break;
case 'LogFile':
$oTransport = Swift_LogFileTransport::newInstance();
$oTransport->setLogFile(APPROOT.'log/mail.log');
break;
case 'PHPMail':
default:
$oTransport = Swift_MailTransport::newInstance();
@@ -194,78 +100,21 @@ class EMail
$oMailer = Swift_Mailer::newInstance($oTransport);
$aFailedRecipients = array();
$this->m_oMessage->setMaxLineLength(0);
$oKPI = new ExecutionKPI();
try
$iSent = $oMailer->send($this->m_oMessage);
if ($iSent === false)
{
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
if ($iSent === 0)
{
// Beware: it seems that $aFailedRecipients sometimes contains the recipients that actually received the message !!!
IssueLog::Warning('Email sending failed: Some recipients were invalid, aFailedRecipients contains: '.implode(', ', $aFailedRecipients));
$aIssues = array('Some recipients were invalid.');
$oKPI->ComputeStats('Email Sent', 'Error received');
return EMAIL_SEND_ERROR;
}
else
{
$aIssues = array();
$oKPI->ComputeStats('Email Sent', 'Succeded');
return EMAIL_SEND_OK;
}
$aIssues = 'une erreur s\'est produite... mais quoi !!!';
return EMAIL_SEND_ERROR;
}
catch (Exception $e)
else
{
$oKPI->ComputeStats('Email Sent', 'Error received');
throw $e;
}
}
/**
* Reprocess the body of the message (if it is an HTML message)
* to replace the URL of images based on attachments by a link
* to an embedded image (i.e. cid:....)
*/
protected function EmbedInlineImages()
{
if ($this->m_aData['body']['mimeType'] == 'text/html')
{
$oDOMDoc = new DOMDocument();
$oDOMDoc->preserveWhitespace = true;
@$oDOMDoc->loadHTML('<?xml encoding="UTF-8"?>'.$this->m_aData['body']['body']); // For loading HTML chunks where the character set is not specified
$oXPath = new DOMXPath($oDOMDoc);
$sXPath = "//img[@data-img-id]";
$oImagesList = $oXPath->query($sXPath);
if ($oImagesList->length != 0)
{
foreach($oImagesList as $oImg)
{
$iAttId = $oImg->getAttribute('data-img-id');
$oAttachment = MetaModel::GetObject('InlineImage', $iAttId, false, true /* Allow All Data */);
if ($oAttachment)
{
$oDoc = $oAttachment->Get('contents');
$oSwiftImage = new Swift_Image($oDoc->GetData(), $oDoc->GetFileName(), $oDoc->GetMimeType());
$sCid = $this->m_oMessage->embed($oSwiftImage);
$oImg->setAttribute('src', $sCid);
}
}
}
$sHtmlBody = $oDOMDoc->saveHTML();
$this->m_oMessage->setBody($sHtmlBody, 'text/html', 'UTF-8');
$aIssues = array();
return EMAIL_SEND_OK;
}
}
public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null)
{
//select a default sender if none is provided.
if(empty($this->m_aData['from']['address']) && !empty($this->m_aData['to'])){
$this->SetRecipientFrom($this->m_aData['to']);
}
{
if ($bForceSynchronous)
{
return $this->SendSynchronous($aIssues, $oLog);
@@ -286,17 +135,27 @@ class EMail
public function AddToHeader($sKey, $sValue)
{
if (!array_key_exists('headers', $this->m_aData))
{
$this->m_aData['headers'] = array();
}
$this->m_aData['headers'][$sKey] = $sValue;
if (strlen($sValue) > 0)
{
$oHeaders = $this->m_oMessage->getHeaders();
switch(strtolower($sKey))
{
case 'from':
case 'cc':
case 'bcc':
$aMatches = array();
// Header may be in the form: John Doe <jd@company.com>
if (preg_match('/^([^<]+) <([^>]+)>$/', $sValue, $aMatches))
{
$aHeader = array($aMatches[2] => $aMatches[1]);
}
else
{
$aHeader = array($sValue);
}
$oHeaders->addMailboxHeader($sKey, $aHeader);
break;
default:
$oHeaders->addTextHeader($sKey, $sValue);
}
@@ -305,8 +164,6 @@ class EMail
public function SetMessageId($sId)
{
$this->m_aData['message_id'] = $sId;
// Note: Swift will add the angle brackets for you
// so let's remove the angle brackets if present, for historical reasons
$sId = str_replace(array('<', '>'), '', $sId);
@@ -320,42 +177,24 @@ class EMail
$this->AddToHeader('References', $sReferences);
}
public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null)
public function SetBody($sBody, $sMimeType = 'text/html')
{
if (($sMimeType === 'text/html') && ($sCustomStyles !== null))
{
require_once(APPROOT.'lib/emogrifier/Classes/Emogrifier.php');
$emogrifier = new \Pelago\Emogrifier($sBody, $sCustomStyles);
$sBody = $emogrifier->emogrify(); // Adds html/body tags if not already present
}
$this->m_aData['body'] = array('body' => $sBody, 'mimeType' => $sMimeType);
$this->m_oMessage->setBody($sBody, $sMimeType);
}
public function AddPart($sText, $sMimeType = 'text/html')
{
if (!array_key_exists('parts', $this->m_aData))
{
$this->m_aData['parts'] = array();
}
$this->m_aData['parts'][] = array('text' => $sText, 'mimeType' => $sMimeType);
$this->m_oMessage->addPart($sText, $sMimeType);
}
public function AddAttachment($data, $sFileName, $sMimeType)
{
if (!array_key_exists('attachments', $this->m_aData))
{
$this->m_aData['attachments'] = array();
}
$this->m_aData['attachments'][] = array('data' => base64_encode($data), 'filename' => $sFileName, 'mimeType' => $sMimeType);
$this->m_oMessage->attach(Swift_Attachment::newInstance($data, $sFileName, $sMimeType));
}
public function SetSubject($sSubject)
public function SetSubject($aSubject)
{
$this->m_aData['subject'] = $sSubject;
$this->m_oMessage->setSubject($sSubject);
$this->m_oMessage->setSubject($aSubject);
}
public function GetSubject()
@@ -363,42 +202,14 @@ class EMail
return $this->m_oMessage->getSubject();
}
/**
* Helper to transform and sanitize addresses
* - get rid of empty addresses
*/
protected function AddressStringToArray($sAddressCSVList)
{
$aAddresses = array();
foreach(explode(',', $sAddressCSVList) as $sAddress)
{
$sAddress = trim($sAddress);
if (strlen($sAddress) > 0)
{
$aAddresses[] = $sAddress;
}
}
return $aAddresses;
}
public function SetRecipientTO($sAddress)
{
$this->m_aData['to'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setTo($aAddresses);
}
$this->m_oMessage->setTo($sAddress);
}
public function GetRecipientTO($bAsString = false)
{
$aRes = $this->m_oMessage->getTo();
if ($aRes === null)
{
// There is no "To" header field
$aRes = array();
}
if ($bAsString)
{
$aStrings = array();
@@ -424,32 +235,21 @@ class EMail
public function SetRecipientCC($sAddress)
{
$this->m_aData['cc'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setCc($aAddresses);
}
$this->AddToHeader('Cc', $sAddress);
}
public function SetRecipientBCC($sAddress)
{
$this->m_aData['bcc'] = $sAddress;
if (!empty($sAddress))
{
$aAddresses = $this->AddressStringToArray($sAddress);
$this->m_oMessage->setBcc($aAddresses);
}
$this->AddToHeader('Bcc', $sAddress);
}
public function SetRecipientFrom($sAddress, $sLabel = '')
{
$this->m_aData['from'] = array('address' => $sAddress, 'label' => $sLabel);
if ($sLabel != '')
{
$this->m_oMessage->setFrom(array($sAddress => $sLabel));
}
else if (!empty($sAddress))
else
{
$this->m_oMessage->setFrom($sAddress);
}
@@ -457,84 +257,9 @@ class EMail
public function SetRecipientReplyTo($sAddress)
{
$this->m_aData['reply_to'] = $sAddress;
if (!empty($sAddress))
{
$this->m_oMessage->setReplyTo($sAddress);
}
$this->AddToHeader('Reply-To', $sAddress);
}
}
/////////////////////////////////////////////////////////////////////////////////////
/**
* Extension to SwiftMailer: "debug" transport that pretends messages have been sent,
* but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_Transport_LogFileTransport extends Swift_Transport_NullTransport
{
protected $sLogFile;
/**
* Sends the given message.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent emails
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$hFile = @fopen($this->sLogFile, 'a');
if ($hFile)
{
$sTxt = "================== ".date('Y-m-d H:i:s')." ==================\n";
$sTxt .= $message->toString()."\n";
@fwrite($hFile, $sTxt);
@fclose($hFile);
}
return parent::send($message, $failedRecipients);
}
public function setLogFile($sFilename)
{
$this->sLogFile = $sFilename;
}
}
/**
* Pretends messages have been sent, but just log them to a file.
*
* @package Swift
* @author Denis Flaven
*/
class Swift_LogFileTransport extends Swift_Transport_LogFileTransport
{
/**
* Create a new LogFileTransport.
*/
public function __construct()
{
call_user_func_array(
array($this, 'Swift_Transport_LogFileTransport::__construct'),
Swift_DependencyContainer::getInstance()
->createDependenciesFor('transport.null')
);
}
/**
* Create a new LogFileTransport instance.
*
* @return Swift_LogFileTransport
*/
public static function newInstance()
{
return new self();
}
}
?>

View File

@@ -1,29 +1,28 @@
<?php
// Copyright (C) 2010-2017 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Persistent class Event and derived
* Application internal events
* There is also a file log
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class Event extends DBObject implements iDisplay
@@ -41,7 +40,6 @@ class Event extends DBObject implements iDisplay
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -107,7 +105,7 @@ class Event extends DBObject implements iDisplay
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
if ($bEditMode) return array(); // Not editable
if ($bEditMode) return; // Not editable
$aDetails = array();
$sClass = get_class($this);
@@ -118,7 +116,6 @@ class Event extends DBObject implements iDisplay
$aDetails[] = array('label' => '<span title="'.MetaModel::GetDescription($sClass, $sAttCode).'">'.MetaModel::GetLabel($sClass, $sAttCode).'</span>', 'value' => $sDisplayValue);
}
$oPage->Details($aDetails);
return array();
}
}
@@ -137,10 +134,6 @@ class EventNotification extends Event
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false),
'indexes' => array(
array('object_id'),
)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -173,7 +166,6 @@ class EventNotificationEmail extends EventNotification
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -183,11 +175,10 @@ class EventNotificationEmail extends EventNotification
MetaModel::Init_AddAttribute(new AttributeText("from", array("allowed_values"=>null, "sql"=>"from", "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"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeHTML("body", array("allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeTable("attachments", array("allowed_values"=>null, "sql"=>"attachments", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body', 'attachments')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message', 'to', 'subject', 'attachments')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message', 'to', 'subject')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
@@ -211,7 +202,6 @@ class EventIssue extends Event
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -264,8 +254,8 @@ class EventIssue extends Event
}
else
{
// Not a string (avoid warnings in case the value cannot be easily casted into a string)
$aPost[$sKey] = @(string) $sValue;
// Not a string
$aPost[$sKey] = (string) $sValue;
}
}
$this->Set('arguments_post', $aPost);
@@ -311,7 +301,6 @@ class EventWebService extends Event
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -332,42 +321,6 @@ class EventWebService extends Event
}
}
class EventRestService extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_restservice",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("operation", array("allowed_values"=>null, "sql"=>"operation", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("version", array("allowed_values"=>null, "sql"=>"version", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("json_input", array("allowed_values"=>null, "sql"=>"json_input", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("code", array("allowed_values"=>null, "sql"=>"code", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("json_output", array("allowed_values"=>null, "sql"=>"json_output", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("provider", array("allowed_values"=>null, "sql"=>"provider", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'operation', 'version', 'json_input', 'message', 'code', 'json_output', 'provider')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'operation', 'message')); // 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
}
}
class EventLoginUsage extends Event
{
public static function Init()
@@ -382,12 +335,11 @@ class EventLoginUsage extends Event
"db_table" => "priv_event_loginusage",
"db_key_field" => "id",
"db_finalclass_field" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
$aZList = array('date', 'user_id');
if (MetaModel::IsValidAttCode('Contact', 'name'))
{
@@ -408,30 +360,4 @@ class EventLoginUsage extends Event
}
}
class EventOnObject extends Event
{
public static function Init()
{
$aParams = array
(
"category" => "core/cmdb,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_event_onobject",
"db_key_field" => "id",
"db_finalclass_field" => "",
"display_template" => "",
"order_by_default" => array('date' => false)
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values"=>null, "sql"=>"obj_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values"=>null, "sql"=>"obj_key", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for a list
}
}
?>

View File

@@ -1,384 +0,0 @@
<?php
// Copyright (C) 2015-2016 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Bulk export: Excel (xlsx) export
*
* @copyright Copyright (C) 2015-2016 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'application/xlsxwriter.class.php');
class ExcelBulkExport extends TabularBulkExport
{
protected $sData;
public function __construct()
{
parent::__construct();
$this->aStatusInfo['status'] = 'not_started';
$this->aStatusInfo['position'] = 0;
}
public function Cleanup()
{
@unlink($this->aStatusInfo['tmp_file']);
parent::Cleanup();
}
public function DisplayUsage(Page $oP)
{
$oP->p(" * xlsx format options:");
$oP->p(" *\tfields: the comma separated list of field codes to export (e.g: name,org_id,service_name...).");
$oP->p(" *\tformatted_text: set to 1 to export case logs and formatted text fields with their HTML markup. Default is 0 (= plain text)");
$oP->p(" *\tdate_format: the format to use when exporting date and time fields (default = the SQL format). e.g. 'Y-m-d H:i:s'");
}
public function ReadParameters()
{
parent::ReadParameters();
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 0, true);
$sDateFormatRadio = utils::ReadParam('excel_date_format_radio', '');
switch($sDateFormatRadio)
{
case 'default':
// 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;
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');
}
}
public function EnumFormParts()
{
return array_merge(parent::EnumFormParts(), array('xlsx_options' => array('formatted_text') ,'interactive_fields_xlsx' => array('interactive_fields_xlsx')));
}
public function DisplayFormPart(WebPage $oP, $sPartId)
{
switch($sPartId)
{
case 'interactive_fields_xlsx':
$this->GetInteractiveFieldsWidget($oP, 'interactive_fields_xlsx');
break;
case 'xlsx_options':
$oP->add('<fieldset><legend>'.Dict::S('Core:BulkExport:XLSXOptions').'</legend>');
$oP->add('<table class="export_parameters"><tr><td style="vertical-align:top">');
$sChecked = (utils::ReadParam('formatted_text', 0) == 1) ? ' checked ' : '';
$oP->add('<h3>'.Dict::S('Core:BulkExport:TextFormat').'</h3>');
$oP->add('<input type="checkbox" id="xlsx_formatted_text" name="formatted_text" value="1"'.$sChecked.'><label for="xlsx_formatted_text"> '.Dict::S('Core:BulkExport:OptionFormattedText').'</label>');
$oP->add('</td><td style="vertical-align:top">');
$sDateTimeFormat = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
$sDefaultChecked = ($sDateTimeFormat == (string)AttributeDateTime::GetFormat()) ? ' checked' : '';
$sCustomChecked = ($sDateTimeFormat !== (string)AttributeDateTime::GetFormat()) ? ' checked' : '';
$oP->add('<h3>'.Dict::S('Core:BulkExport:DateTimeFormat').'</h3>');
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
$oP->add('<input type="radio" id="excel_date_time_format_default" name="excel_date_format_radio" value="default"'.$sDefaultChecked.'><label for="excel_date_time_format_default"> '.Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample).'</label><br/>');
$sFormatInput = '<input type="text" size="15" name="date_format" id="excel_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
$oP->add('<input type="radio" id="excel_date_time_format_custom" name="excel_date_format_radio" value="custom"'.$sCustomChecked.'><label for="excel_date_time_format_custom"> '.Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput).'</label>');
$oP->add('</td></tr></table>');
$oP->add('</fieldset>');
$sJSTooltip = json_encode('<div class="date_format_tooltip">'.Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip').'</div>');
$oP->add_ready_script(
<<<EOF
$('#excel_custom_date_time_format').tooltip({content: function() { return $sJSTooltip; } });
$('#form_part_xlsx_options').on('preview_updated', function() { FormatDatesInPreview('excel', 'xlsx'); });
$('#excel_date_time_format_default').on('click', function() { FormatDatesInPreview('excel', 'xlsx'); });
$('#excel_date_time_format_custom').on('click', function() { FormatDatesInPreview('excel', 'xlsx'); });
$('#excel_custom_date_time_format').on('click', function() { $('#excel_date_time_format_custom').prop('checked', true); FormatDatesInPreview('excel', 'xlsx'); }).on('keyup', function() { FormatDatesInPreview('excel', 'xlsx'); });
EOF
);
break;
default:
return parent:: DisplayFormPart($oP, $sPartId);
}
}
protected function SuggestField($sClass, $sAttCode)
{
switch($sAttCode)
{
case 'id': // replace 'id' by 'friendlyname'
$sAttCode = 'friendlyname';
break;
default:
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef instanceof AttributeExternalKey)
{
$sAttCode .= '_friendlyname';
}
}
return parent::SuggestField($sClass, $sAttCode);
}
protected function GetSampleData($oObj, $sAttCode)
{
if ($sAttCode != 'id')
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
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).'">'.htmlentities($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj), ENT_QUOTES, 'UTF-8').'</div>';
}
}
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
}
protected function GetValue($oObj, $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
{
$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;
}
}
else if (array_key_exists('formatted_text', $this->aStatusInfo) && $this->aStatusInfo['formatted_text'])
{
$sRet = $oAttDef->GetEditValue($value, $oObj);
}
else
{
$sRet = $oAttDef->GetAsPlainText($value, $oObj);
}
}
}
return $sRet;
}
public function GetHeader()
{
$oSet = new DBObjectSet($this->oSearch);
$this->aStatusInfo['status'] = 'retrieving';
$this->aStatusInfo['tmp_file'] = $this->MakeTmpFile('data');
$this->aStatusInfo['position'] = 0;
$this->aStatusInfo['total'] = $oSet->Count();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$sExtendedAttCode = $aFieldSpec['sFieldSpec'];
$sAttCode = $aFieldSpec['sAttCode'];
$sColLabel = $aFieldSpec['sColLabel'];
switch($sAttCode)
{
case 'id':
$sType = '0';
break;
default:
$oAttDef = MetaModel::GetAttributeDef($aFieldSpec['sClass'], $aFieldSpec['sAttCode']);
$sType = 'string';
if($oAttDef instanceof AttributeDate)
{
$sType = 'date';
}
else if($oAttDef instanceof AttributeDateTime)
{
$sType = 'datetime';
}
}
$aTableHeaders[] = array('label' => $sColLabel, 'type' => $sType);
}
$sRow = json_encode($aTableHeaders);
$hFile = @fopen($this->aStatusInfo['tmp_file'], 'ab');
if ($hFile === false)
{
throw new Exception('ExcelBulkExport: Failed to open temporary data file: "'.$this->aStatusInfo['tmp_file'].'" for writing.');
}
fwrite($hFile, $sRow."\n");
fclose($hFile);
return '';
}
public function GetNextChunk(&$aStatus)
{
$sRetCode = 'run';
$iPercentage = 0;
$hFile = fopen($this->aStatusInfo['tmp_file'], 'ab');
$oSet = new DBObjectSet($this->oSearch);
$oSet->SetLimit($this->iChunkSize, $this->aStatusInfo['position']);
$this->OptimizeColumnLoad($oSet);
$iCount = 0;
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
while($aRow = $oSet->FetchAssoc())
{
set_time_limit($iLoopTimeLimit);
$aData = array();
foreach($this->aStatusInfo['fields'] as $iCol => $aFieldSpec)
{
$sAlias = $aFieldSpec['sAlias'];
$sAttCode = $aFieldSpec['sAttCode'];
$oObj = $aRow[$sAlias];
$sField = '';
if ($oObj)
{
$sField = $this->GetValue($oObj, $sAttCode);
}
$aData[] = $sField;
}
fwrite($hFile, json_encode($aData)."\n");
$iCount++;
}
set_time_limit($iPreviousTimeLimit);
$this->aStatusInfo['position'] += $this->iChunkSize;
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']));
}
if ($iCount < $this->iChunkSize)
{
$sRetCode = 'done';
}
$aStatus = array('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)
{
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))
{
$aRow = json_decode($sLine);
$aData[] = $aRow;
}
fclose($hFile);
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$oDateTimeFormat = new DateTimeFormat($this->aStatusInfo['date_format']);
$writer->setDateTimeFormat($oDateTimeFormat->ToExcel());
$oDateFormat = new DateTimeFormat($oDateTimeFormat->ToDateFormat());
$writer->setDateFormat($oDateFormat->ToExcel());
$writer->setAuthor(UserRights::GetUserFriendlyName());
$aHeaderTypes = array();
$aHeaderNames = array();
foreach($aHeaders as $Header)
{
$aHeaderNames[] = $Header['label'];
$aHeaderTypes[] = $Header['type'];
}
$writer->writeSheet($aData,'Sheet1', $aHeaderTypes, $aHeaderNames);
$fExcelTime = microtime(true) - $fStartExcel;
//$this->aStatistics['excel_build_duration'] = $fExcelTime;
$fTime = microtime(true);
$data = $writer->writeToString();
$fExcelSaveTime = microtime(true) - $fTime;
//$this->aStatistics['excel_write_duration'] = $fExcelSaveTime;
@unlink($this->aStatusInfo['tmp_file']);
return $data;
}
public function GetMimeType()
{
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
}
public function GetFileExtension()
{
return 'xlsx';
}
public function GetSupportedFormats()
{
return array('xlsx' => Dict::S('Core:BulkExport:XLSXFormat'));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,110 +0,0 @@
<?php
// Copyright (c) 2010-2017 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
class ExpressionCache
{
static private $aCache = array();
static public function GetCachedExpression($sClass, $sAttCode)
{
// read current cache
@include_once (static::GetCacheFileName());
$oExpr = null;
$sKey = static::GetKey($sClass, $sAttCode);
if (array_key_exists($sKey, static::$aCache))
{
$oExpr = static::$aCache[$sKey];
}
else
{
if (class_exists('ExpressionCacheData'))
{
if (array_key_exists($sKey, ExpressionCacheData::$aCache))
{
$sVal = ExpressionCacheData::$aCache[$sKey];
$oExpr = unserialize($sVal);
static::$aCache[$sKey] = $oExpr;
}
}
}
return $oExpr;
}
static public function Warmup()
{
$sFilePath = static::GetCacheFileName();
if (!is_file($sFilePath))
{
$content = <<<EOF
<?php
// Copyright (c) 2010-2017 Combodo SARL
// Generated Expression Cache file
class ExpressionCacheData
{
static \$aCache = array(
EOF;
foreach(MetaModel::GetClasses() as $sClass)
{
$content .= static::GetSerializedExpression($sClass, 'friendlyname');
if (MetaModel::IsObsoletable($sClass))
{
$content .= static::GetSerializedExpression($sClass, 'obsolescence_flag');
}
}
$content .= <<<EOF
);
}
EOF;
file_put_contents($sFilePath, $content);
}
}
static private function GetSerializedExpression($sClass, $sAttCode)
{
$sKey = static::GetKey($sClass, $sAttCode);
$oExpr = DBObjectSearch::GetPolymorphicExpression($sClass, $sAttCode);
return "'".$sKey."' => '".serialize($oExpr)."',\n";
}
/**
* @param $sClass
* @param $sAttCode
* @return string
*/
static private function GetKey($sClass, $sAttCode)
{
return $sClass.'::'.$sAttCode;
}
public static function GetCacheFileName()
{
return utils::GetCachePath().'expressioncache.php';
}
}

View File

@@ -1,28 +1,27 @@
<?php
// Copyright (C) 2010-2012 Combodo SARL
// Copyright (C) 2010 Combodo SARL
//
// This file is part of iTop.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Definition of a filter
* Most of the time, a filter corresponds to an attribute, but we could imagine other search criteria
*
* @copyright Copyright (C) 2010-2012 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/

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