Created branch 1.0.2
SVN:1.0.2[1071]
368
addons/userrights/userrightsmatrix.class.inc.php
Normal file
@@ -0,0 +1,368 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* UserRightsMatrix (User management Module)
|
||||
*
|
||||
* @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 UserRightsMatrixClassGrant extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_ur_matrixclasses",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("class", array("allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
}
|
||||
|
||||
class UserRightsMatrixClassStimulusGrant extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_ur_matrixclassesstimulus",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("class", array("allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
}
|
||||
|
||||
class UserRightsMatrixAttributeGrant extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_ur_matrixattributes",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("class", array("allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class UserRightsMatrix extends UserRightsAddOnAPI
|
||||
{
|
||||
static public $m_aActionCodes = array(
|
||||
UR_ACTION_READ => 'read',
|
||||
UR_ACTION_MODIFY => 'modify',
|
||||
UR_ACTION_DELETE => 'delete',
|
||||
UR_ACTION_BULK_READ => 'bulk read',
|
||||
UR_ACTION_BULK_MODIFY => 'bulk modify',
|
||||
UR_ACTION_BULK_DELETE => 'bulk delete',
|
||||
);
|
||||
|
||||
// Installation: create the very first user
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
// Maybe we should check that no other user with userid == 0 exists
|
||||
$oUser = new UserLocal();
|
||||
$oUser->Set('login', $sAdminUser);
|
||||
$oUser->Set('password', $sAdminPwd);
|
||||
$oUser->Set('contactid', 1); // one is for root !
|
||||
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
|
||||
|
||||
// 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();
|
||||
|
||||
// Now record the admin user object
|
||||
$iUserId = $oUser->DBInsertTrackedNoReload($oChange, true /* skip security */);
|
||||
$this->SetupUser($iUserId, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsAdministrator($oUser)
|
||||
{
|
||||
return ($oUser->GetKey() == 1);
|
||||
}
|
||||
|
||||
public function IsPortalUser($oUser)
|
||||
{
|
||||
return ($oUser->GetKey() == 1);
|
||||
}
|
||||
|
||||
// Deprecated - create a new module !
|
||||
public function Setup()
|
||||
{
|
||||
// Users must be added manually
|
||||
// This procedure will then update the matrix when a new user is found or a new class/attribute appears
|
||||
$oUserSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT User"));
|
||||
while ($oUser = $oUserSet->Fetch())
|
||||
{
|
||||
$this->SetupUser($oUser->GetKey());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function SetupUser($iUserId, $bNewUser = false)
|
||||
{
|
||||
foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory)
|
||||
{
|
||||
foreach (MetaModel::GetClasses($sCategory) as $sClass)
|
||||
{
|
||||
foreach (self::$m_aActionCodes as $iActionCode => $sAction)
|
||||
{
|
||||
if ($bNewUser)
|
||||
{
|
||||
$bAddCell = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND userid = $iUserId"));
|
||||
$bAddCell = ($oSet->Count() < 1);
|
||||
}
|
||||
if ($bAddCell)
|
||||
{
|
||||
// Create a new entry
|
||||
$oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassGrant");
|
||||
$oMyClassGrant->Set("userid", $iUserId);
|
||||
$oMyClassGrant->Set("class", $sClass);
|
||||
$oMyClassGrant->Set("action", $sAction);
|
||||
$oMyClassGrant->Set("permission", "yes");
|
||||
$iId = $oMyClassGrant->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus)
|
||||
{
|
||||
if ($bNewUser)
|
||||
{
|
||||
$bAddCell = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND userid = $iUserId"));
|
||||
$bAddCell = ($oSet->Count() < 1);
|
||||
}
|
||||
if ($bAddCell)
|
||||
{
|
||||
// Create a new entry
|
||||
$oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassStimulusGrant");
|
||||
$oMyClassGrant->Set("userid", $iUserId);
|
||||
$oMyClassGrant->Set("class", $sClass);
|
||||
$oMyClassGrant->Set("stimulus", $sStimulusCode);
|
||||
$oMyClassGrant->Set("permission", "yes");
|
||||
$iId = $oMyClassGrant->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode)
|
||||
{
|
||||
if ($bNewUser)
|
||||
{
|
||||
$bAddCell = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND userid = $iUserId"));
|
||||
$bAddCell = ($oSet->Count() < 1);
|
||||
}
|
||||
if ($bAddCell)
|
||||
{
|
||||
foreach (array('read', 'modify') as $sAction)
|
||||
{
|
||||
// Create a new entry
|
||||
$oMyAttGrant = MetaModel::NewObject("UserRightsMatrixAttributeGrant");
|
||||
$oMyAttGrant->Set("userid", $iUserId);
|
||||
$oMyAttGrant->Set("class", $sClass);
|
||||
$oMyAttGrant->Set("attcode", $sAttCode);
|
||||
$oMyAttGrant->Set("action", $sAction);
|
||||
$oMyAttGrant->Set("permission", "yes");
|
||||
$iId = $oMyAttGrant->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
// Create the "My Bookmarks" menu item (parent_id = 0, rank = 6)
|
||||
if ($bNewUser)
|
||||
{
|
||||
$bAddMenu = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT menuNode WHERE type = 'user' AND parent_id = 0 AND user_id = $iUserId"));
|
||||
$bAddMenu = ($oSet->Count() < 1);
|
||||
}
|
||||
if ($bAddMenu)
|
||||
{
|
||||
$oMenu = MetaModel::NewObject('menuNode');
|
||||
$oMenu->Set('type', 'user');
|
||||
$oMenu->Set('parent_id', 0); // It's a toplevel entry
|
||||
$oMenu->Set('rank', 6); // Located just above the Admin Tools section (=7)
|
||||
$oMenu->Set('name', 'My Bookmarks');
|
||||
$oMenu->Set('label', 'My Favorite Items');
|
||||
$oMenu->Set('hyperlink', 'UI.php');
|
||||
$oMenu->Set('template', '<p></p><p></p><p style="text-align:center; font-family:Georgia, Times, serif; font-size:32px;">My bookmarks</p><p style="text-align:center; font-family:Georgia, Times, serif; font-size:14px;"><i>This section contains my most favorite search results</i></p>');
|
||||
$oMenu->Set('user_id', $iUserId);
|
||||
$oMenu->DBInsert();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
public function Init()
|
||||
{
|
||||
// Could be loaded in a shared memory (?)
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetSelectFilter($oUser, $sClass)
|
||||
{
|
||||
$oNullFilter = new DBObjectSearch($sClass);
|
||||
return $oNullFilter;
|
||||
}
|
||||
|
||||
public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
|
||||
{
|
||||
if (!array_key_exists($iActionCode, self::$m_aActionCodes))
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND userid = '{$oUser->GetKey()}'"));
|
||||
if ($oSet->Count() < 1)
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
|
||||
$oGrantRecord = $oSet->Fetch();
|
||||
switch ($oGrantRecord->Get('permission'))
|
||||
{
|
||||
case 'yes':
|
||||
$iRetCode = UR_ALLOWED_YES;
|
||||
break;
|
||||
case 'no':
|
||||
default:
|
||||
$iRetCode = UR_ALLOWED_NO;
|
||||
break;
|
||||
}
|
||||
return $iRetCode;
|
||||
}
|
||||
|
||||
public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null)
|
||||
{
|
||||
if (!array_key_exists($iActionCode, self::$m_aActionCodes))
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND action = '$sAction' AND userid = '{$oUser->GetKey()}'"));
|
||||
if ($oSet->Count() < 1)
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
|
||||
$oGrantRecord = $oSet->Fetch();
|
||||
switch ($oGrantRecord->Get('permission'))
|
||||
{
|
||||
case 'yes':
|
||||
$iRetCode = UR_ALLOWED_YES;
|
||||
break;
|
||||
case 'no':
|
||||
default:
|
||||
$iRetCode = UR_ALLOWED_NO;
|
||||
break;
|
||||
}
|
||||
return $iRetCode;
|
||||
}
|
||||
|
||||
public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, $oInstanceSet = null)
|
||||
{
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND userid = '{$oUser->GetKey()}'"));
|
||||
if ($oSet->Count() < 1)
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
|
||||
$oGrantRecord = $oSet->Fetch();
|
||||
switch ($oGrantRecord->Get('permission'))
|
||||
{
|
||||
case 'yes':
|
||||
$iRetCode = UR_ALLOWED_YES;
|
||||
break;
|
||||
case 'no':
|
||||
default:
|
||||
$iRetCode = UR_ALLOWED_NO;
|
||||
break;
|
||||
}
|
||||
return $iRetCode;
|
||||
}
|
||||
|
||||
public function FlushPrivileges()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
UserRights::SelectModule('UserRightsMatrix');
|
||||
|
||||
?>
|
||||
78
addons/userrights/userrightsnull.class.inc.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
// Copyright (C) 2010 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
|
||||
|
||||
/**
|
||||
* UserRightsNull
|
||||
* User management Module - say Yeah! to everything
|
||||
*
|
||||
* @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
|
||||
{
|
||||
// Installation: create the very first user
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsAdministrator($oUser)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsPortalUser($oUser)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function Init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetSelectFilter($oUser, $sClass)
|
||||
{
|
||||
$oNullFilter = new DBObjectSearch($sClass);
|
||||
return $oNullFilter;
|
||||
}
|
||||
|
||||
public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
|
||||
{
|
||||
return UR_ALLOWED_YES;
|
||||
}
|
||||
|
||||
public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, $oInstanceSet = null)
|
||||
{
|
||||
return UR_ALLOWED_YES;
|
||||
}
|
||||
|
||||
public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null)
|
||||
{
|
||||
return UR_ALLOWED_YES;
|
||||
}
|
||||
|
||||
public function FlushPrivileges()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
UserRights::SelectModule('UserRightsNull');
|
||||
|
||||
?>
|
||||
839
addons/userrights/userrightsprofile.class.inc.php
Normal file
@@ -0,0 +1,839 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* UserRightsProfile
|
||||
* User management Module, basing the right on profiles and a matrix (similar to UserRightsMatrix, but profiles and other decorations have been added)
|
||||
*
|
||||
* @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');
|
||||
define('PORTAL_PROFILE_NAME', 'Portal user');
|
||||
|
||||
class UserRightsBaseClass extends cmdbAbstractObject
|
||||
{
|
||||
// Whenever something changes, reload the privileges
|
||||
|
||||
protected function AfterInsert()
|
||||
{
|
||||
UserRights::FlushPrivileges();
|
||||
}
|
||||
|
||||
protected function AfterUpdate()
|
||||
{
|
||||
UserRights::FlushPrivileges();
|
||||
}
|
||||
|
||||
protected function AfterDelete()
|
||||
{
|
||||
UserRights::FlushPrivileges();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class URP_Profiles extends UserRightsBaseClass
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_profiles",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("user_list", array("linked_class"=>"URP_UserProfile", "ext_key_to_me"=>"profileid", "ext_key_to_remote"=>"userid", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
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')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
protected $m_bCheckReservedNames = true;
|
||||
protected function DisableCheckOnReservedNames()
|
||||
{
|
||||
$this->m_bCheckReservedNames = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the built-in Administrator profile with its reserved name
|
||||
*/
|
||||
public static function DoCreateAdminProfile()
|
||||
{
|
||||
$oNewObj = MetaModel::NewObject("URP_Profiles");
|
||||
$oNewObj->Set('name', ADMIN_PROFILE_NAME);
|
||||
$oNewObj->Set('description', 'Has the rights on everything (bypassing any control)');
|
||||
$oNewObj->DisableCheckOnReservedNames();
|
||||
$iNewId = $oNewObj->DBInsertNoReload();
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the built-in User Portal profile with its reserved name
|
||||
*/
|
||||
public static function DoCreateUserPortalProfile()
|
||||
{
|
||||
$oNewObj = MetaModel::NewObject("URP_Profiles");
|
||||
$oNewObj->Set('name', PORTAL_PROFILE_NAME);
|
||||
$oNewObj->Set('description', 'Has the rights to access to the user portal. People having this profile will not be allowed to access the standard application, they will be automatically redirected to the user portal.');
|
||||
$oNewObj->DisableCheckOnReservedNames();
|
||||
$iNewId = $oNewObj->DBInsertNoReload();
|
||||
}
|
||||
|
||||
/*
|
||||
* Overload the standard behavior to preserve reserved names
|
||||
*/
|
||||
public function DoCheckToWrite()
|
||||
{
|
||||
parent::DoCheckToWrite();
|
||||
|
||||
if ($this->m_bCheckReservedNames)
|
||||
{
|
||||
$aChanges = $this->ListChanges();
|
||||
if (array_key_exists('name', $aChanges))
|
||||
{
|
||||
if ($this->GetOriginal('name') == ADMIN_PROFILE_NAME)
|
||||
{
|
||||
$this->m_aCheckIssues[] = "The name of the Administrator profile must not be changed";
|
||||
}
|
||||
elseif ($this->Get('name') == ADMIN_PROFILE_NAME)
|
||||
{
|
||||
$this->m_aCheckIssues[] = ADMIN_PROFILE_NAME." is a reserved to the built-in Administrator profile";
|
||||
}
|
||||
elseif ($this->GetOriginal('name') == PORTAL_PROFILE_NAME)
|
||||
{
|
||||
$this->m_aCheckIssues[] = "The name of the User Portal profile must not be changed";
|
||||
}
|
||||
elseif ($this->Get('name') == PORTAL_PROFILE_NAME)
|
||||
{
|
||||
$this->m_aCheckIssues[] = PORTAL_PROFILE_NAME." is a reserved to the built-in User Portal profile";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function GetGrantAsHtml($oUserRights, $sClass, $sAction)
|
||||
{
|
||||
$iGrant = $oUserRights->GetProfileActionGrant($this->GetKey(), $sClass, $sAction);
|
||||
if (!is_null($iGrant))
|
||||
{
|
||||
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
function DoShowGrantSumary($oPage)
|
||||
{
|
||||
if ($this->GetName() == "Administrator")
|
||||
{
|
||||
// Looks dirty, but ok that's THE ONE
|
||||
$oPage->p(Dict::S('UI:UserManagement:AdminProfile+'));
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: for sure, we assume that the instance is derived from UserRightsProfile
|
||||
$oUserRights = UserRights::GetModuleInstance();
|
||||
|
||||
$aDisplayData = array();
|
||||
foreach (MetaModel::GetClasses('bizmodel') as $sClass)
|
||||
{
|
||||
// Skip non instantiable classes
|
||||
if (MetaModel::IsAbstract($sClass)) continue;
|
||||
|
||||
$aStimuli = array();
|
||||
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus)
|
||||
{
|
||||
$oGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
|
||||
if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes'))
|
||||
{
|
||||
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription()).'">'.htmlentities($oStimulus->GetLabel()).'</span>';
|
||||
}
|
||||
}
|
||||
$sStimuli = implode(', ', $aStimuli);
|
||||
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName($sClass),
|
||||
'read' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Read'),
|
||||
'bulkread' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Bulk Read'),
|
||||
'write' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Modify'),
|
||||
'bulkwrite' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Bulk Modify'),
|
||||
'delete' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Delete'),
|
||||
'bulkdelete' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Bulk Delete'),
|
||||
'stimuli' => $sStimuli,
|
||||
);
|
||||
}
|
||||
|
||||
$aDisplayConfig = array();
|
||||
$aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
|
||||
$aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
|
||||
$aDisplayConfig['bulkread'] = array('label' => Dict::S('UI:UserManagement:Action:BulkRead'), 'description' => Dict::S('UI:UserManagement:Action:BulkRead+'));
|
||||
$aDisplayConfig['write'] = array('label' => Dict::S('UI:UserManagement:Action:Modify'), 'description' => Dict::S('UI:UserManagement:Action:Modify+'));
|
||||
$aDisplayConfig['bulkwrite'] = array('label' => Dict::S('UI:UserManagement:Action:BulkModify'), 'description' => Dict::S('UI:UserManagement:Action:BulkModify+'));
|
||||
$aDisplayConfig['delete'] = array('label' => Dict::S('UI:UserManagement:Action:Delete'), 'description' => Dict::S('UI:UserManagement:Action:Delete+'));
|
||||
$aDisplayConfig['bulkdelete'] = array('label' => Dict::S('UI:UserManagement:Action:BulkDelete'), 'description' => Dict::S('UI:UserManagement:Action:BulkDelete+'));
|
||||
$aDisplayConfig['stimuli'] = array('label' => Dict::S('UI:UserManagement:Action:Stimuli'), 'description' => Dict::S('UI:UserManagement:Action:Stimuli+'));
|
||||
$oPage->table($aDisplayConfig, $aDisplayData);
|
||||
}
|
||||
|
||||
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
parent::DisplayBareRelations($oPage, $bEditMode);
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$oPage->SetCurrentTab(Dict::S('UI:UserManagement:GrantMatrix'));
|
||||
$this->DoShowGrantSumary($oPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class URP_UserProfile extends UserRightsBaseClass
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("userlogin", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("reason", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('userid', 'profileid', 'reason')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('userid', 'profileid', 'reason')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('userid', 'profileid')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'profileid')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
|
||||
}
|
||||
}
|
||||
|
||||
class URP_UserOrg extends UserRightsBaseClass
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userorg",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("userlogin", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("allowed_org_id", array("targetclass"=>"Organization", "jointype"=> "", "allowed_values"=>null, "sql"=>"allowed_org_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("allowed_org_name", array("allowed_values"=>null, "extkey_attcode"=> 'allowed_org_id', "target_attcode"=>"name")));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("reason", array("allowed_values"=>null, "sql"=>"reason", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('userid', 'allowed_org_id', 'reason')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('allowed_org_id', 'reason')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('userid', 'allowed_org_id')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'allowed_org_id')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Org', $this->Get('userlogin'), $this->Get('allowed_org_name'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class URP_ActionGrant extends UserRightsBaseClass
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "profileid",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_grant_actions",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
|
||||
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('profileid', 'class', 'permission', 'action')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('class', 'permission', 'action')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('profileid', 'class', 'permission', 'action')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('profileid', 'class', 'permission', 'action')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class URP_StimulusGrant extends UserRightsBaseClass
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "profileid",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_grant_stimulus",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
|
||||
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('profileid', 'class', 'permission', 'stimulus')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('class', 'permission', 'stimulus')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('profileid', 'class', 'permission', 'stimulus')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('profileid', 'class', 'permission', 'stimulus')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class URP_AttributeGrant extends UserRightsBaseClass
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "actiongrantid",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_grant_attributes",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("actiongrantid", array("targetclass"=>"URP_ActionGrant", "jointype"=> "", "allowed_values"=>null, "sql"=>"actiongrantid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('actiongrantid', 'attcode')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('attcode')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('actiongrantid', 'attcode')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('actiongrantid', 'attcode')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
class UserRightsProfile extends UserRightsAddOnAPI
|
||||
{
|
||||
static public $m_aActionCodes = array(
|
||||
UR_ACTION_READ => 'read',
|
||||
UR_ACTION_MODIFY => 'modify',
|
||||
UR_ACTION_DELETE => 'delete',
|
||||
UR_ACTION_BULK_READ => 'bulk read',
|
||||
UR_ACTION_BULK_MODIFY => 'bulk modify',
|
||||
UR_ACTION_BULK_DELETE => 'bulk delete',
|
||||
);
|
||||
|
||||
// Installation: create the very first user
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
// 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();
|
||||
|
||||
// Support drastic data model changes: no organization class !
|
||||
if (MetaModel::IsValidClass('Organization'))
|
||||
{
|
||||
$oOrg = new Organization();
|
||||
$oOrg->Set('name', 'My Company/Department');
|
||||
$oOrg->Set('code', 'SOMECODE');
|
||||
$iOrgId = $oOrg->DBInsertTrackedNoReload($oChange, true /* skip security */);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iOrgId = 0;
|
||||
}
|
||||
|
||||
// Support drastic data model changes: no Person class !
|
||||
if (MetaModel::IsValidClass('Person'))
|
||||
{
|
||||
$oContact = new Person();
|
||||
$oContact->Set('name', 'My last name');
|
||||
$oContact->Set('first_name', 'My first name');
|
||||
if (MetaModel::IsValidAttCode('Person', 'org_id'))
|
||||
{
|
||||
$oContact->Set('org_id', $iOrgId);
|
||||
}
|
||||
$oContact->Set('email', 'my.email@foo.org');
|
||||
$iContactId = $oContact->DBInsertTrackedNoReload($oChange, true /* skip security */);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iContactId = 0;
|
||||
}
|
||||
|
||||
$oUser = new UserLocal();
|
||||
$oUser->Set('login', $sAdminUser);
|
||||
$oUser->Set('password', $sAdminPwd);
|
||||
if (MetaModel::IsValidAttCode('UserLocal', 'contactid'))
|
||||
{
|
||||
$oUser->Set('contactid', $iContactId);
|
||||
}
|
||||
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
|
||||
|
||||
// Add this user to the very specific 'admin' profile
|
||||
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => ADMIN_PROFILE_NAME), true /*all data*/);
|
||||
if (is_object($oAdminProfile))
|
||||
{
|
||||
$oUserProfile = new URP_UserProfile();
|
||||
//$oUserProfile->Set('userid', $iUserId);
|
||||
$oUserProfile->Set('profileid', $oAdminProfile->GetKey());
|
||||
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
|
||||
//$oUserProfile->DBInsertTrackedNoReload($oChange, true /* skip security */);
|
||||
$oSet = DBObjectSet::FromObject($oUserProfile);
|
||||
$oUser->Set('profile_list', $oSet);
|
||||
}
|
||||
$iUserId = $oUser->DBInsertTrackedNoReload($oChange, true /* skip security */);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function Init()
|
||||
{
|
||||
MetaModel::RegisterPlugin('userrights', 'ACbyProfile');
|
||||
}
|
||||
|
||||
|
||||
protected $m_aAdmins; // id of users being linked to the well-known admin profile
|
||||
protected $m_aPortalUsers; // id of users being linked to the well-known admin profile
|
||||
|
||||
protected $m_aProfiles; // id -> object
|
||||
protected $m_aUserProfiles; // userid,profileid -> object
|
||||
protected $m_aUserOrgs; // userid,orgid -> object
|
||||
|
||||
// Those arrays could be completed on demand (inheriting parent permissions)
|
||||
protected $m_aClassActionGrants = null; // profile, class, action -> actiongrantid (or false if NO, or null/missing if undefined)
|
||||
protected $m_aClassStimulusGrants = array(); // profile, class, stimulus -> permission
|
||||
|
||||
// Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read)
|
||||
protected $m_aObjectActionGrants = array();
|
||||
|
||||
public function ResetCache()
|
||||
{
|
||||
// Loaded by Load cache
|
||||
$this->m_aProfiles = null;
|
||||
$this->m_aUserProfiles = null;
|
||||
$this->m_aUserOrgs = null;
|
||||
|
||||
$this->m_aAdmins = null;
|
||||
$this->m_aPortalUsers = null;
|
||||
|
||||
// Loaded on demand (time consuming as compared to the others)
|
||||
$this->m_aClassActionGrants = null;
|
||||
$this->m_aClassStimulusGrants = null;
|
||||
|
||||
$this->m_aObjectActionGrants = array();
|
||||
}
|
||||
|
||||
// Separate load: this cache is much more time consuming while loading
|
||||
// Thus it is loaded iif required
|
||||
// Could be improved by specifying the profile id
|
||||
public function LoadActionGrantCache()
|
||||
{
|
||||
if (!is_null($this->m_aClassActionGrants)) return;
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
|
||||
$oFilter = DBObjectSearch::FromOQL_AllData("SELECT URP_ActionGrant AS p WHERE p.permission = 'yes'");
|
||||
$aGrants = $oFilter->ToDataArray();
|
||||
foreach($aGrants as $aGrant)
|
||||
{
|
||||
$this->m_aClassActionGrants[$aGrant['profileid']][$aGrant['class']][strtolower($aGrant['action'])] = $aGrant['id'];
|
||||
}
|
||||
|
||||
$oKPI->ComputeAndReport('Load of action grants');
|
||||
}
|
||||
|
||||
public function LoadCache()
|
||||
{
|
||||
if (!is_null($this->m_aProfiles)) return;
|
||||
// Could be loaded in a shared memory (?)
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
|
||||
$oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Profiles"));
|
||||
$this->m_aProfiles = array();
|
||||
while ($oProfile = $oProfileSet->Fetch())
|
||||
{
|
||||
$this->m_aProfiles[$oProfile->GetKey()] = $oProfile;
|
||||
}
|
||||
|
||||
$oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_UserProfile"));
|
||||
$this->m_aUserProfiles = array();
|
||||
$this->m_aAdmins = array();
|
||||
$this->m_aPortalUsers = array();
|
||||
while ($oUserProfile = $oUserProfileSet->Fetch())
|
||||
{
|
||||
$this->m_aUserProfiles[$oUserProfile->Get('userid')][$oUserProfile->Get('profileid')] = $oUserProfile;
|
||||
if ($oUserProfile->Get('profile') == ADMIN_PROFILE_NAME)
|
||||
{
|
||||
$this->m_aAdmins[] = $oUserProfile->Get('userid');
|
||||
}
|
||||
elseif ($oUserProfile->Get('profile') == PORTAL_PROFILE_NAME)
|
||||
{
|
||||
$this->m_aPortalUsers[] = $oUserProfile->Get('userid');
|
||||
}
|
||||
}
|
||||
|
||||
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_UserOrg"));
|
||||
$this->m_aUserOrgs = array();
|
||||
while ($oUserOrg = $oUserOrgSet->Fetch())
|
||||
{
|
||||
$this->m_aUserOrgs[$oUserOrg->Get('userid')][$oUserOrg->Get('allowed_org_id')] = $oUserOrg;
|
||||
}
|
||||
|
||||
$this->m_aClassStimulusGrants = array();
|
||||
$oStimGrantSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_StimulusGrant"));
|
||||
$this->m_aStimGrants = array();
|
||||
while ($oStimGrant = $oStimGrantSet->Fetch())
|
||||
{
|
||||
$this->m_aClassStimulusGrants[$oStimGrant->Get('profileid')][$oStimGrant->Get('class')][$oStimGrant->Get('stimulus')] = $oStimGrant;
|
||||
}
|
||||
|
||||
$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);
|
||||
print_r($this->m_aClassActionGrants);
|
||||
print_r($this->m_aClassStimulusGrants);
|
||||
echo "</pre>\n";
|
||||
exit;
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function IsAdministrator($oUser)
|
||||
{
|
||||
$this->LoadCache();
|
||||
|
||||
if (in_array($oUser->GetKey(), $this->m_aAdmins))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function IsPortalUser($oUser)
|
||||
{
|
||||
$this->LoadCache();
|
||||
|
||||
if (in_array($oUser->GetKey(), $this->m_aPortalUsers))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetSelectFilter($oUser, $sClass)
|
||||
{
|
||||
$this->LoadCache();
|
||||
|
||||
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, UR_ACTION_READ);
|
||||
if ($aObjectPermissions['permission'] == UR_ALLOWED_NO)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine how to position the objects of this class
|
||||
//
|
||||
if ($sClass == 'Organization')
|
||||
{
|
||||
$sAttCode = 'id';
|
||||
}
|
||||
elseif(MetaModel::IsValidAttCode($sClass, 'org_id'))
|
||||
{
|
||||
$sAttCode = 'org_id';
|
||||
}
|
||||
else
|
||||
{
|
||||
// The objects of this class are not positioned in this dimension
|
||||
// All of them are visible
|
||||
return true;
|
||||
}
|
||||
$oExpression = new FieldExpression($sAttCode, $sClass);
|
||||
|
||||
// Position the user
|
||||
//
|
||||
@$aUserOrgs = $this->m_aUserOrgs[$oUser->GetKey()];
|
||||
if (!isset($aUserOrgs) || count($aUserOrgs) == 0)
|
||||
{
|
||||
// No position means 'Everywhere'
|
||||
return true;
|
||||
}
|
||||
|
||||
$aIds = array_keys($aUserOrgs);
|
||||
$oListExpr = ListExpression::FromScalars($aIds);
|
||||
$oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr);
|
||||
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$oFilter->AddConditionExpression($oCondition);
|
||||
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)
|
||||
{
|
||||
$this->LoadActionGrantCache();
|
||||
|
||||
// Note: action is forced lowercase to be more flexible (historical bug)
|
||||
$sAction = strtolower($sAction);
|
||||
if (isset($this->m_aClassActionGrants[$iProfile][$sClass][$sAction]))
|
||||
{
|
||||
return $this->m_aClassActionGrants[$iProfile][$sClass][$sAction];
|
||||
}
|
||||
|
||||
// Recursively look for the grant record in the class hierarchy
|
||||
$sParentClass = MetaModel::GetParentPersistentClass($sClass);
|
||||
if (empty($sParentClass))
|
||||
{
|
||||
$iGrant = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recursively look for the grant record in the class hierarchy
|
||||
$iGrant = $this->GetProfileActionGrant($iProfile, $sParentClass, $sAction);
|
||||
}
|
||||
|
||||
$this->m_aClassActionGrants[$iProfile][$sClass][$sAction] = $iGrant;
|
||||
return $iGrant;
|
||||
}
|
||||
|
||||
protected function GetUserActionGrant($oUser, $sClass, $iActionCode)
|
||||
{
|
||||
$this->LoadCache();
|
||||
|
||||
// load and cache permissions for the current user on the given class
|
||||
//
|
||||
$iUser = $oUser->GetKey();
|
||||
$aTest = @$this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode];
|
||||
if (is_array($aTest)) return $aTest;
|
||||
|
||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||
|
||||
$iPermission = UR_ALLOWED_NO;
|
||||
$aAttributes = array();
|
||||
if (isset($this->m_aUserProfiles[$iUser]))
|
||||
{
|
||||
foreach($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile)
|
||||
{
|
||||
$iGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
|
||||
if (is_null($iGrant) || !$iGrant)
|
||||
{
|
||||
continue; // loop to the next profile
|
||||
}
|
||||
else
|
||||
{
|
||||
$iPermission = UR_ALLOWED_YES;
|
||||
|
||||
// update the list of attributes with those allowed for this profile
|
||||
//
|
||||
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT URP_AttributeGrant WHERE actiongrantid = :actiongrantid");
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('actiongrantid' => $iGrant));
|
||||
$aProfileAttributes = $oSet->GetColumnAsArray('attcode', false);
|
||||
if (count($aProfileAttributes) == 0)
|
||||
{
|
||||
$aAllAttributes = array_keys(MetaModel::ListAttributeDefs($sClass));
|
||||
$aAttributes = array_merge($aAttributes, $aAllAttributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAttributes = array_merge($aAttributes, $aProfileAttributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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();
|
||||
|
||||
// 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
|
||||
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, $iActionCode);
|
||||
return $aObjectPermissions['permission'];
|
||||
}
|
||||
|
||||
public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null)
|
||||
{
|
||||
$this->LoadCache();
|
||||
|
||||
// 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
|
||||
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, $iActionCode);
|
||||
$aAttributes = $aObjectPermissions['attributes'];
|
||||
if (in_array($sAttCode, $aAttributes))
|
||||
{
|
||||
return $aObjectPermissions['permission'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return UR_ALLOWED_NO;
|
||||
}
|
||||
}
|
||||
|
||||
// This verb has been made public to allow the development of an accurate feedback for the current configuration
|
||||
public function GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode)
|
||||
{
|
||||
$this->LoadCache();
|
||||
|
||||
if (isset($this->m_aClassStimulusGrants[$iProfile][$sClass][$sStimulusCode]))
|
||||
{
|
||||
return $this->m_aClassStimulusGrants[$iProfile][$sClass][$sStimulusCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, $oInstanceSet = null)
|
||||
{
|
||||
$this->LoadCache();
|
||||
// Note: this code is VERY close to the code of IsActionAllowed()
|
||||
$iUser = $oUser->GetKey();
|
||||
|
||||
// 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
|
||||
$iPermission = UR_ALLOWED_NO;
|
||||
if (isset($this->m_aUserProfiles[$iUser]))
|
||||
{
|
||||
foreach($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile)
|
||||
{
|
||||
$oGrantRecord = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
|
||||
if (!is_null($oGrantRecord))
|
||||
{
|
||||
// no need to fetch the record, we've requested the records having permission = 'yes'
|
||||
$iPermission = UR_ALLOWED_YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $iPermission;
|
||||
}
|
||||
|
||||
public function FlushPrivileges()
|
||||
{
|
||||
$this->ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UserRights::SelectModule('UserRightsProfile');
|
||||
|
||||
?>
|
||||
1252
addons/userrights/userrightsprojection.class.inc.php
Normal file
235
application/ajaxwebpage.class.inc.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Simple web page with no includes, header or fancy formatting, useful to
|
||||
* generate HTML fragments when called by an AJAX method
|
||||
*
|
||||
* @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
|
||||
{
|
||||
/**
|
||||
* Jquery style ready script
|
||||
* @var Hash
|
||||
*/
|
||||
protected $m_sReadyScript;
|
||||
protected $m_sCurrentTab;
|
||||
protected $m_sCurrentTabContainer;
|
||||
protected $m_aTabs;
|
||||
|
||||
/**
|
||||
* constructor for the web page
|
||||
* @param string $s_title Not used
|
||||
*/
|
||||
function __construct($s_title)
|
||||
{
|
||||
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_sCurrentTabContainer = '';
|
||||
$this->m_sCurrentTab = '';
|
||||
$this->m_aTabs = array();
|
||||
}
|
||||
|
||||
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
||||
{
|
||||
$this->m_aTabs[$sTabContainer] = array('content' =>'', 'prefix' => $sPrefix);
|
||||
$this->add("\$Tabs:$sTabContainer\$");
|
||||
}
|
||||
|
||||
public function 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 = '')
|
||||
{
|
||||
$sPreviousTabContainer = $this->m_sCurrentTabContainer;
|
||||
$this->m_sCurrentTabContainer = $sTabContainer;
|
||||
return $sPreviousTabContainer;
|
||||
}
|
||||
|
||||
public function SetCurrentTab($sTabLabel = '')
|
||||
{
|
||||
$sPreviousTab = $this->m_sCurrentTab;
|
||||
$this->m_sCurrentTab = $sTabLabel;
|
||||
return $sPreviousTab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Echoes the content of the whole page
|
||||
* @return void
|
||||
*/
|
||||
public function output()
|
||||
{
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
|
||||
if (count($this->m_aTabs) > 0)
|
||||
{
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
// The "tab widgets" to handle.
|
||||
var tabs = $('div[id^=tabbedContent]');
|
||||
|
||||
// This selector will be reused when selecting actual tab widget A elements.
|
||||
var tab_a_selector = 'ul.ui-tabs-nav a';
|
||||
|
||||
// Enable tabs on all tab widgets. The `event` property must be overridden so
|
||||
// that the tabs aren't changed on click, and any custom event name can be
|
||||
// specified. Note that if you define a callback for the 'select' event, it
|
||||
// will be executed for the selected tab whenever the hash changes.
|
||||
tabs.tabs({ event: 'change' });
|
||||
|
||||
// Define our own click handler for the tabs, overriding the default.
|
||||
tabs.find( tab_a_selector ).click(function()
|
||||
{
|
||||
var state = {};
|
||||
|
||||
// Get the id of this tab widget.
|
||||
var id = $(this).closest( 'div[id^=tabbedContent]' ).attr( 'id' );
|
||||
|
||||
// Get the index of this tab.
|
||||
var idx = $(this).parent().prevAll().length;
|
||||
|
||||
// Set the state!
|
||||
state[ id ] = idx;
|
||||
$.bbq.pushState( state );
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
// Render the tabs in the page (if any)
|
||||
foreach($this->m_aTabs as $sTabContainerName => $aTabContainer)
|
||||
{
|
||||
$sTabs = '';
|
||||
$m_aTabs = $aTabContainer['content'];
|
||||
$sPrefix = $aTabContainer['prefix'];
|
||||
$container_index = 0;
|
||||
if (count($m_aTabs) > 0)
|
||||
{
|
||||
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$sPrefix}{$container_index}\" 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 = ob_get_contents();
|
||||
ob_end_clean();
|
||||
echo $this->s_content;
|
||||
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 (!empty($this->m_sReadyScript))
|
||||
{
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
echo $this->m_sReadyScript; // Ready Scripts are output as simple scripts
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
if (trim($s_captured_output) != "")
|
||||
{
|
||||
echo $s_captured_output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a paragraph with a smaller font into the page
|
||||
* NOT implemented (i.e does nothing)
|
||||
* @param string $sText Content of the (small) paragraph
|
||||
* @return void
|
||||
*/
|
||||
public function small_p($sText)
|
||||
{
|
||||
}
|
||||
|
||||
public function add($sHtml)
|
||||
{
|
||||
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
|
||||
{
|
||||
$this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent::add($sHtml);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a script to be executed when the DOM is ready (typical JQuery use)
|
||||
* NOT implemented in this version of the class.
|
||||
* @return void
|
||||
*/
|
||||
public function add_ready_script($sScript)
|
||||
{
|
||||
// Does nothing in ajax rendered content.. for now...
|
||||
// Maybe we should add this as a simple <script> tag at the end of the output
|
||||
// considering that at this time everything in the page is "ready"...
|
||||
$this->m_sReadyScript .= $sScript;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cannot be called in this context, since Ajax pages do not share
|
||||
* any context with the calling page !!
|
||||
*/
|
||||
public function GetUniqueId()
|
||||
{
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
37
application/application.inc.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Includes all the classes to have the application up and running
|
||||
*
|
||||
* @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/audit.category.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.rule.class.inc.php');
|
||||
//require_once(APPROOT.'/application/menunode.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
class ApplicationException extends CoreException
|
||||
{
|
||||
}
|
||||
?>
|
||||
170
application/applicationcontext.class.inc.php
Normal file
@@ -0,0 +1,170 @@
|
||||
<?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");
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
$this->aNames = array(
|
||||
'org_id', 'menu'
|
||||
);
|
||||
$this->ReadContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the context directly in the PHP parameters (either POST or GET)
|
||||
* return nothing
|
||||
*/
|
||||
protected function ReadContext()
|
||||
{
|
||||
if (empty(self::$aDefaultValues))
|
||||
{
|
||||
self::$aDefaultValues = array();
|
||||
$aContext = utils::ReadParam('c', array());
|
||||
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');
|
||||
$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=\"$sValue\" />\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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
59
application/audit.category.class.inc.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @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');
|
||||
|
||||
class AuditCategory extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditcategory",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", array("allowed_values"=>null, "sql"=>"definition_set", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
|
||||
|
||||
// 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('name', 'description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('name', 'description', 'definition_set')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
?>
|
||||
63
application/audit.rule.class.inc.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* This class manages the audit "rule" linked to a given audit category.
|
||||
* Each rule is based on an OQL expression that returns either the "good" objects
|
||||
* 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
|
||||
*
|
||||
* @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');
|
||||
|
||||
class AuditRule extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditrule",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("query", array("allowed_values"=>null, "sql"=>"query", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", array("allowed_values"=>new ValueSetEnum('true,false'), "sql"=>"valid_flag", "default_value"=>"true", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("allowed_values"=>null, "sql"=>"category_id", "targetclass"=>"AuditCategory", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values"=>null, "extkey_attcode"=> 'category_id', "target_attcode"=>"name")));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('category_id', 'name', 'description', 'query', 'valid_flag')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('category_id', 'description', 'valid_flag')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
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
|
||||
}
|
||||
}
|
||||
?>
|
||||
88
application/clipage.class.inc.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* CLI page
|
||||
* The page adds the content-type text/XML and the encoding into the headers
|
||||
*
|
||||
* @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 CLIPage
|
||||
{
|
||||
function __construct($s_title)
|
||||
{
|
||||
}
|
||||
|
||||
public function output()
|
||||
{
|
||||
}
|
||||
|
||||
public function add($sText)
|
||||
{
|
||||
echo $sText;
|
||||
}
|
||||
|
||||
public function p($sText)
|
||||
{
|
||||
echo $sText."\n";
|
||||
}
|
||||
|
||||
public function pre($sText)
|
||||
{
|
||||
echo $sText."\n";
|
||||
}
|
||||
|
||||
public function add_comment($sText)
|
||||
{
|
||||
echo "#".$sText."\n";
|
||||
}
|
||||
|
||||
public function table($aConfig, $aData, $aParams = array())
|
||||
{
|
||||
$aCells = array();
|
||||
foreach($aConfig as $sName=>$aDef)
|
||||
{
|
||||
if (strlen($aDef['description']) > 0)
|
||||
{
|
||||
$aCells[] = $aDef['label'].' ('.$aDef['description'].')';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCells[] = $aDef['label'];
|
||||
}
|
||||
}
|
||||
echo implode(';', $aCells)."\n";
|
||||
|
||||
foreach($aData as $aRow)
|
||||
{
|
||||
$aCells = array();
|
||||
foreach($aConfig as $sName=>$aAttribs)
|
||||
{
|
||||
$sValue = $aRow["$sName"];
|
||||
$aCells[] = $sValue;
|
||||
}
|
||||
echo implode(';', $aCells)."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
1813
application/cmdbabstract.class.inc.php
Normal file
96
application/csvpage.class.inc.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @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 CSVPage extends WebPage
|
||||
{
|
||||
function __construct($s_title)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
}
|
||||
|
||||
public function output()
|
||||
{
|
||||
$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 small_p($sText)
|
||||
{
|
||||
}
|
||||
|
||||
public function add($sText)
|
||||
{
|
||||
$this->s_content .= $sText;
|
||||
}
|
||||
|
||||
public function p($sText)
|
||||
{
|
||||
$this->s_content .= $sText."\n";
|
||||
}
|
||||
|
||||
public function add_comment($sText)
|
||||
{
|
||||
$this->s_content .= "#".$sText."\n";
|
||||
}
|
||||
|
||||
public function table($aConfig, $aData, $aParams = array())
|
||||
{
|
||||
$aCells = array();
|
||||
foreach($aConfig as $sName=>$aDef)
|
||||
{
|
||||
if (strlen($aDef['description']) > 0)
|
||||
{
|
||||
$aCells[] = $aDef['label'].' ('.$aDef['description'].')';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCells[] = $aDef['label'];
|
||||
}
|
||||
}
|
||||
$this->s_content .= implode(';', $aCells)."\n";
|
||||
|
||||
foreach($aData as $aRow)
|
||||
{
|
||||
$aCells = array();
|
||||
foreach($aConfig as $sName=>$aAttribs)
|
||||
{
|
||||
$sValue = $aRow["$sName"];
|
||||
$aCells[] = $sValue;
|
||||
}
|
||||
$this->s_content .= implode(';', $aCells)."\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
1122
application/displayblock.class.inc.php
Normal file
68
application/iotask.class.inc.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Persistent class InputOutputTask
|
||||
*
|
||||
* @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');
|
||||
|
||||
/**
|
||||
* This class manages the input/output tasks
|
||||
* for synchronizing information with external data sources
|
||||
*/
|
||||
class InputOutputTask extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_iotask",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("category", array("allowed_values"=>new ValueSetEnum('Input, Ouput'), "sql"=>"category", "default_value"=>"Input", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("source_type", array("allowed_values"=>new ValueSetEnum('File, Database, Web Service'), "sql"=>"source_type", "default_value"=>"File", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("source_subtype", array("allowed_values"=>new ValueSetEnum('Oracle, MySQL, Postgress, MSSQL, SOAP, HTTP-Get, HTTP-Post, XML/RPC, CSV, XML, Excel'), "sql"=>"source_subtype", "default_value"=>"CSV", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("source_path", array("allowed_values"=>null, "sql"=>"source_path", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeClass("objects_class", array("class_category"=>"", "more_values"=>"", "sql"=>"objects_class", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("test_mode", array("allowed_values"=>new ValueSetEnum('Yes,No'), "sql"=>"test_mode", "default_value"=>'No', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("verbose_mode", array("allowed_values"=>new ValueSetEnum('Yes,No'), "sql"=>"verbose_mode", "default_value" => 'No', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("options", array("allowed_values"=>new ValueSetEnum('Full, Update Only, Creation Only'), "sql"=>"options", "default_value"=> 'Full', "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'category', 'objects_class', 'source_type', 'source_subtype', 'source_path' , 'options', 'test_mode', 'verbose_mode')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description', 'category', 'objects_class', 'source_type', 'source_subtype', 'options')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'category', 'objects_class', 'source_type', 'source_subtype')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('name', 'description', 'category', 'objects_class', 'source_type', 'source_subtype')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
?>
|
||||
908
application/itopwebpage.class.inc.php
Normal file
@@ -0,0 +1,908 @@
|
||||
<?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 iTopWebPage
|
||||
*
|
||||
* @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");
|
||||
require_once(APPROOT."/application/applicationcontext.class.inc.php");
|
||||
require_once(APPROOT."/application/user.preferences.class.inc.php");
|
||||
/**
|
||||
* Web page with some associated CSS and scripts (jquery) for a fancier display
|
||||
*/
|
||||
class iTopWebPage extends NiceWebPage
|
||||
{
|
||||
private $m_sMenu;
|
||||
// private $m_currentOrganization;
|
||||
private $m_aTabs;
|
||||
private $m_sCurrentTabContainer;
|
||||
private $m_sCurrentTab;
|
||||
|
||||
public function __construct($sTitle)
|
||||
{
|
||||
parent::__construct($sTitle);
|
||||
$this->m_sCurrentTabContainer = '';
|
||||
$this->m_sCurrentTab = '';
|
||||
$this->m_aTabs = array();
|
||||
$this->m_sMenu = "";
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sExtraParams = $oAppContext->GetForLink();
|
||||
// $this->m_currentOrganization = $currentOrganization;
|
||||
$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$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/date.picker.css");
|
||||
$this->add_linked_script('../js/jquery.layout.min.js');
|
||||
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
|
||||
// $this->add_linked_script("../js/jquery.dimensions.js");
|
||||
$this->add_linked_script("../js/jquery.tablehover.js");
|
||||
$this->add_linked_script("../js/jquery.treeview.js");
|
||||
$this->add_linked_script("../js/jquery.autocomplete.js");
|
||||
$this->add_linked_script("../js/jquery.bgiframe.js");
|
||||
$this->add_linked_script("../js/jquery.positionBy.js");
|
||||
$this->add_linked_script("../js/jquery.popupmenu.js");
|
||||
$this->add_linked_script("../js/date.js");
|
||||
// $this->add_linked_script("../js/jquery.date.picker.js");
|
||||
$this->add_linked_script("../js/jquery.tablesorter.min.js");
|
||||
$this->add_linked_script("../js/jquery.blockUI.js");
|
||||
$this->add_linked_script("../js/utils.js");
|
||||
$this->add_linked_script("../js/swfobject.js");
|
||||
$this->add_linked_script("../js/ckeditor/ckeditor.js");
|
||||
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
try
|
||||
{
|
||||
var myLayout; // a var is required because this page utilizes: myLayout.allowOverflow() method
|
||||
|
||||
$(document).ready(function () {
|
||||
// Layout
|
||||
paneSize = GetUserPreference('menu_size', 300)
|
||||
myLayout = $('body').layout({
|
||||
west : {
|
||||
minSize: 200, size: paneSize, spacing_open: 16, spacing_close: 16, slideTrigger_open: "mouseover", hideTogglerOnSlide: true,
|
||||
onclose_end: function(name, elt, state, options, layout)
|
||||
{
|
||||
if (state.isSliding == false)
|
||||
{
|
||||
SetUserPreference('menu_pane', 'closed', true);
|
||||
}
|
||||
},
|
||||
onresize_end: function(name, elt, state, options, layout)
|
||||
{
|
||||
if (state.isSliding == false)
|
||||
{
|
||||
SetUserPreference('menu_size', state.size, true);
|
||||
}
|
||||
},
|
||||
|
||||
onopen_end: function(name, elt, state, options, layout)
|
||||
{
|
||||
if (state.isSliding == false)
|
||||
{
|
||||
SetUserPreference('menu_pane', 'open', true);
|
||||
}
|
||||
}
|
||||
},
|
||||
center: {
|
||||
onresize_end: function(name, elt, state, options, layout)
|
||||
{
|
||||
$('.v-resizable').each( function() {
|
||||
var fixedWidth = $(this).parent().innerWidth() - 6;
|
||||
$(this).width(fixedWidth);
|
||||
// Make sure it cannot be resized horizontally
|
||||
$(this).resizable('options', { minWidth: fixedWidth, maxWidth: fixedWidth });
|
||||
// Now adjust all the child 'items'
|
||||
var innerWidth = $(this).innerWidth() - 10;
|
||||
$(this).find('.item').width(innerWidth);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
myLayout.addPinBtn( "#tPinMenu", "west" );
|
||||
//myLayout.open( "west" );
|
||||
$('.ui-layout-resizer-west').html('<img src="../images/splitter-top-corner.png"/>');
|
||||
if (GetUserPreference('menu_pane', 'open') == 'closed')
|
||||
{
|
||||
myLayout.close('west');
|
||||
}
|
||||
|
||||
// Accordion Menu
|
||||
$("#accordion").accordion({ header: "h3", navigation: true, autoHeight: false, collapsible: false, icons: false }); // collapsible will be enabled once the item will be selected
|
||||
});
|
||||
//add new widget called TruncatedList to properly display truncated lists when they are sorted
|
||||
$.tablesorter.addWidget({
|
||||
// give the widget a id
|
||||
id: "truncatedList",
|
||||
// format is called when the on init and when a sorting has finished
|
||||
format: function(table)
|
||||
{
|
||||
// Check if there is a "truncated" line
|
||||
this.truncatedList = false;
|
||||
if ($("tr td.truncated",table).length > 0)
|
||||
{
|
||||
this.truncatedList = true;
|
||||
}
|
||||
if (this.truncatedList)
|
||||
{
|
||||
$("tr td",table).removeClass('truncated');
|
||||
$("tr:last td",table).addClass('truncated');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$.tablesorter.addWidget({
|
||||
// give the widget a id
|
||||
id: "myZebra",
|
||||
// format is called when the on init and when a sorting has finished
|
||||
format: function(table)
|
||||
{
|
||||
// Replace the 'red even' lines by 'red_even' since most browser do not support 2 classes selector in CSS, etc..
|
||||
$("tbody tr:even",table).addClass('even');
|
||||
$("tbody tr.red:even",table).removeClass('red').removeClass('even').addClass('red_even');
|
||||
$("tbody tr.orange:even",table).removeClass('orange').removeClass('even').addClass('orange_even');
|
||||
$("tbody tr.green:even",table).removeClass('green').removeClass('even').addClass('green_even');
|
||||
// In case we sort again the table, we need to remove the added 'even' classes on odd rows
|
||||
$("tbody tr:odd",table).removeClass('even');
|
||||
$("tbody tr.red_even:odd",table).removeClass('even').removeClass('red_even').addClass('red');
|
||||
$("tbody tr.orange_even:odd",table).removeClass('even').removeClass('orange_even').addClass('orange');
|
||||
$("tbody tr.green_even:odd",table).removeClass('even').removeClass('green_even').addClass('green');
|
||||
}
|
||||
});
|
||||
|
||||
$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
|
||||
// Adjust initial size
|
||||
$('.v-resizable').each( function()
|
||||
{
|
||||
var parent_id = $(this).parent().id;
|
||||
// Restore the saved height
|
||||
var iHeight = GetUserPreference(parent_id+'_'+this.id+'_height', undefined);
|
||||
if (iHeight != undefined)
|
||||
{
|
||||
$(this).height(parseInt(iHeight, 10)); // Parse in base 10 !);
|
||||
}
|
||||
// Adjust the child 'item''s height and width to fit
|
||||
var container = $(this);
|
||||
var fixedWidth = container.parent().innerWidth() - 6;
|
||||
// Set the width to fit the parent
|
||||
$(this).width(fixedWidth);
|
||||
var headerHeight = $(this).find('.drag_handle').height();
|
||||
// Now adjust the width and height of the child 'item'
|
||||
container.find('.item').height(container.innerHeight() - headerHeight - 12).width(fixedWidth - 10);
|
||||
}
|
||||
);
|
||||
// Make resizable, vertically only everything that claims to be v-resizable !
|
||||
$('.v-resizable').resizable( { handles: 's', minHeight: $(this).find('.drag_handle').height(), minWidth: $(this).parent().innerWidth() - 6, maxWidth: $(this).parent().innerWidth() - 6, stop: function()
|
||||
{
|
||||
// Adjust the content
|
||||
var container = $(this);
|
||||
var headerHeight = $(this).find('.drag_handle').height();
|
||||
container.find('.item').height(container.innerHeight() - headerHeight - 12);//.width(container.innerWidth());
|
||||
var parent_id = $(this).parent().id;
|
||||
SetUserPreference(parent_id+'_'+this.id+'_height', $(this).height(), true); // true => persistent
|
||||
}
|
||||
} );
|
||||
|
||||
// Tabs, using JQuery BBQ to store the history
|
||||
// The "tab widgets" to handle.
|
||||
var tabs = $('div[id^=tabbedContent]');
|
||||
|
||||
// This selector will be reused when selecting actual tab widget A elements.
|
||||
var tab_a_selector = 'ul.ui-tabs-nav a';
|
||||
|
||||
// Enable tabs on all tab widgets. The `event` property must be overridden so
|
||||
// that the tabs aren't changed on click, and any custom event name can be
|
||||
// specified. Note that if you define a callback for the 'select' event, it
|
||||
// will be executed for the selected tab whenever the hash changes.
|
||||
tabs.tabs({ event: 'change' });
|
||||
|
||||
// Define our own click handler for the tabs, overriding the default.
|
||||
tabs.find( tab_a_selector ).click(function()
|
||||
{
|
||||
var state = {};
|
||||
|
||||
// Get the id of this tab widget.
|
||||
var id = $(this).closest( 'div[id^=tabbedContent]' ).attr( 'id' );
|
||||
|
||||
// Get the index of this tab.
|
||||
var idx = $(this).parent().prevAll().length;
|
||||
|
||||
// Set the state!
|
||||
state[ id ] = idx;
|
||||
$.bbq.pushState( state );
|
||||
});
|
||||
|
||||
// Bind an event to window.onhashchange that, when the history state changes,
|
||||
// iterates over all tab widgets, changing the current tab as necessary.
|
||||
$(window).bind( 'hashchange', function(e)
|
||||
{
|
||||
// Iterate over all tab widgets.
|
||||
tabs.each(function()
|
||||
{
|
||||
// Get the index for this tab widget from the hash, based on the
|
||||
// appropriate id property. In jQuery 1.4, you should use e.getState()
|
||||
// instead of $.bbq.getState(). The second, 'true' argument coerces the
|
||||
// string value to a number.
|
||||
var idx = $.bbq.getState( this.id, true ) || 0;
|
||||
|
||||
// Select the appropriate tab for this tab widget by triggering the custom
|
||||
// event specified in the .tabs() init above (you could keep track of what
|
||||
// tab each widget is on using .data, and only select a tab if it has
|
||||
// changed).
|
||||
$(this).find( tab_a_selector ).eq( idx ).triggerHandler( 'change' );
|
||||
});
|
||||
|
||||
// Iterate over all truncated lists to find whether they are expanded or not
|
||||
$('a.truncated').each(function()
|
||||
{
|
||||
var state = $.bbq.getState( this.id, true ) || 'close';
|
||||
if (state == 'open')
|
||||
{
|
||||
$(this).trigger('open');
|
||||
}
|
||||
else
|
||||
{
|
||||
$(this).trigger('close');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// End of Tabs handling
|
||||
$("table.listResults").tableHover(); // hover tables
|
||||
// Check each 'listResults' table for a checkbox in the first column and make the first column sortable only if it does not contain a checkbox in the header
|
||||
$(".listResults").each( function()
|
||||
{
|
||||
var table = $(this);
|
||||
var id = $(this).parent();
|
||||
var checkbox = (table.find('th:first :checkbox').length > 0);
|
||||
if (checkbox)
|
||||
{
|
||||
// There is a checkbox in the first column, don't make it sortable
|
||||
table.tablesorter( { headers: { 0: {sorter: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is NO checkbox in the first column, all columns are considered sortable
|
||||
table.tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
|
||||
}
|
||||
});
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
// Restore the persisted sortable order, for all sortable lists... if any
|
||||
$('.sortable').each(function()
|
||||
{
|
||||
var sTemp = GetUserPreference(this.id+'_order', undefined);
|
||||
if (sTemp != undefined)
|
||||
{
|
||||
var aSerialized = sTemp.split(',');
|
||||
var sortable = $(this);
|
||||
$.each(aSerialized, function(i,v) {
|
||||
var item = $('#menu_'+v);
|
||||
if (item.length > 0) // Check that the menu exists
|
||||
{
|
||||
sortable.append(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Make sortable, everything that claims to be sortable
|
||||
$('.sortable').sortable( {axis: 'y', cursor: 'move', handle: '.drag_handle', stop: function()
|
||||
{
|
||||
if ($(this).hasClass('persistent'))
|
||||
{
|
||||
// remember the sort order for next time the page is loaded...
|
||||
sSerialized = $(this).sortable('serialize', {key: 'menu'});
|
||||
var sTemp = sSerialized.replace(/menu=/g, '');
|
||||
SetUserPreference(this.id+'_order', sTemp.replace(/&/g, ','), true); // true => persistent !
|
||||
}
|
||||
}
|
||||
});
|
||||
docWidth = $(document).width();
|
||||
$('#ModalDlg').dialog({ autoOpen: false, modal: true, width: 0.8*docWidth }); // JQuery UI dialogs
|
||||
ShowDebug();
|
||||
$('#logOffBtn>ul').popupmenu();
|
||||
// $.history.init(history_callback);
|
||||
// $("a[rel='history']").click(function()
|
||||
// {
|
||||
// $.history.load(this.href.replace(/^.*#/, ''));
|
||||
// return false;
|
||||
// });
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
// Do something with the error !
|
||||
alert(err);
|
||||
}
|
||||
|
||||
//$('.display_block').draggable(); // make the blocks draggable
|
||||
EOF
|
||||
);
|
||||
$sUserPrefs = appUserPreferences::GetAsJSON();
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
// // for JQuery history
|
||||
// function history_callback(hash)
|
||||
// {
|
||||
// // do stuff that loads page content based on hash variable
|
||||
// var aMatches = /^tab_(.*)$/.exec(hash);
|
||||
// if (aMatches != null)
|
||||
// {
|
||||
// var tab = $('#'+hash);
|
||||
// tab.parents('div[id^=tabbedContent]:first').tabs('select', aMatches[1]);
|
||||
// }
|
||||
// }
|
||||
|
||||
function formatItem(row) {
|
||||
return row[0];
|
||||
}
|
||||
|
||||
function goBack()
|
||||
{
|
||||
window.history.back();
|
||||
}
|
||||
|
||||
function BackToDetails(sClass, id, sDefaultUrl)
|
||||
{
|
||||
if (id > 0)
|
||||
{
|
||||
window.location.href = './UI.php?operation=details&class='+sClass+'&id='+id;
|
||||
}
|
||||
else
|
||||
{
|
||||
window.location.href = sDefaultUrl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function BackToList(sClass)
|
||||
{
|
||||
window.location.href = './UI.php?operation=search_oql&oql_class='+sClass+'&oql_clause=WHERE id=0';
|
||||
}
|
||||
|
||||
function ShowDebug()
|
||||
{
|
||||
if ($('#rawOutput > div').html() != '')
|
||||
{
|
||||
$('#rawOutput').dialog( {autoOpen: true, modal:false});
|
||||
}
|
||||
}
|
||||
|
||||
var oUserPreferences = $sUserPrefs;
|
||||
EOF
|
||||
);
|
||||
|
||||
// Build menus from module handlers
|
||||
//
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI'))
|
||||
{
|
||||
$aCallSpec = array($sPHPClass, 'OnMenuCreation');
|
||||
call_user_func($aCallSpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function AddToMenu($sHtml)
|
||||
{
|
||||
$this->m_sMenu .= $sHtml;
|
||||
}
|
||||
|
||||
public function GetSiloSelectionForm()
|
||||
{
|
||||
// List of visible Organizations
|
||||
$iCount = 0;
|
||||
if (MetaModel::IsValidClass('Organization'))
|
||||
{
|
||||
$oSearchFilter = new DBObjectSearch('Organization');
|
||||
$oSet = new CMDBObjectSet($oSearchFilter);
|
||||
$iCount = $oSet->Count();
|
||||
}
|
||||
switch($iCount)
|
||||
{
|
||||
case 0:
|
||||
// No such dimension/silo => nothing to select
|
||||
$sHtml = '<div id="SiloSelection"><!-- nothing to select --></div>';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// Only one possible choice... no selection, but display the value
|
||||
$oOrg = $oSet->Fetch();
|
||||
$sHtml = '<div id="SiloSelection">'.$oOrg->GetName().'</div>';
|
||||
$sHtml .= '';
|
||||
break;
|
||||
|
||||
default:
|
||||
$oAppContext = new ApplicationContext();
|
||||
$iCurrentOrganization = $oAppContext->GetCurrentValue('org_id');
|
||||
$sHtml = '<div id="SiloSelection">';
|
||||
$sHtml .= '<form style="display:inline" action="'.$_SERVER['PHP_SELF'].'"><select class="org_combo" name="c[org_id]" title="Pick an organization" onChange="this.form.submit();">';
|
||||
$sSelected = ($iCurrentOrganization == '') ? ' selected' : '';
|
||||
$sHtml .= '<option value=""'.$sSelected.'>'.Dict::S('UI:AllOrganizations').'</option>';
|
||||
while($oOrg = $oSet->Fetch())
|
||||
{
|
||||
if ($iCurrentOrganization == $oOrg->GetKey())
|
||||
{
|
||||
// $oCurrentOrganization = $oOrg;
|
||||
$sSelected = " selected";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = "";
|
||||
}
|
||||
$sHtml .= '<option title="'.$oOrg->GetName().'" value="'.$oOrg->GetKey().'"'.$sSelected.'>'.$oOrg->GetName().'</option>';
|
||||
}
|
||||
$sHtml .= '</select>';
|
||||
// Add other dimensions/context information to this form
|
||||
// $oAppContext = new ApplicationContext();
|
||||
$oAppContext->Reset('org_id'); // org_id is handled above and we want to be able to change it here !
|
||||
$sHtml .= $oAppContext->GetForForm();
|
||||
$sHtml .= '</form>';
|
||||
$sHtml .= '</div>';
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
public function DisplayMenu()
|
||||
{
|
||||
// Display the menu
|
||||
$oAppContext = new ApplicationContext();
|
||||
$iAccordionIndex = 0;
|
||||
|
||||
ApplicationMenu::DisplayMenu($this, $oAppContext->GetAsHash());
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs (via some echo) the complete HTML page by assembling all its elements
|
||||
*/
|
||||
public function output()
|
||||
{
|
||||
$this->DisplayMenu(); // Compute the menu
|
||||
|
||||
// Put here the 'ready scripts' that must be executed after all others
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
// Since the event is only triggered when the hash changes, we need to trigger
|
||||
// the event now, to handle the hash the page may have loaded with.
|
||||
$(window).trigger( 'hashchange' );
|
||||
|
||||
EOF
|
||||
);
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
|
||||
echo "<html>\n";
|
||||
echo "<head>\n";
|
||||
// Make sure that Internet Explorer renders the page using its latest/highest/greatest standards !
|
||||
echo "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n";
|
||||
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
|
||||
echo "<title>{$this->s_title}</title>\n";
|
||||
echo $this->get_base_tag();
|
||||
// Stylesheets MUST be loaded before any scripts otherwise
|
||||
// jQuery scripts may face some spurious problems (like failing on a 'reload')
|
||||
foreach($this->a_linked_stylesheets as $a_stylesheet)
|
||||
{
|
||||
if ($a_stylesheet['condition'] != "")
|
||||
{
|
||||
echo "<!--[if {$a_stylesheet['condition']}]>\n";
|
||||
}
|
||||
echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$a_stylesheet['link']}\" />\n";
|
||||
if ($a_stylesheet['condition'] != "")
|
||||
{
|
||||
echo "<![endif]-->\n";
|
||||
}
|
||||
}
|
||||
foreach($this->a_linked_scripts as $s_script)
|
||||
{
|
||||
echo "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
|
||||
}
|
||||
if (count($this->m_aReadyScripts)>0)
|
||||
{
|
||||
$this->add_script("\$(document).ready(function() {\n".implode("\n", $this->m_aReadyScripts)."\n});");
|
||||
}
|
||||
if (count($this->a_scripts)>0)
|
||||
{
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
foreach($this->a_scripts as $s_script)
|
||||
{
|
||||
echo "$s_script\n";
|
||||
}
|
||||
echo "</script>\n";
|
||||
}
|
||||
|
||||
if (count($this->a_styles)>0)
|
||||
{
|
||||
echo "<style>\n";
|
||||
foreach($this->a_styles as $s_style)
|
||||
{
|
||||
echo "$s_style\n";
|
||||
}
|
||||
echo "</style>\n";
|
||||
}
|
||||
echo "<link rel=\"search\" type=\"application/opensearchdescription+xml\" title=\"iTop\" href=\"./opensearch.xml.php\" />\n";
|
||||
echo "</head>\n";
|
||||
echo "<body>\n";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Render the revision number
|
||||
if (ITOP_REVISION == '$WCREV$')
|
||||
{
|
||||
// This is NOT a version built using the buil system, just display the main version
|
||||
$sVersionString = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a build made from SVN, let display the full information
|
||||
$sVersionString = Dict::Format('UI:iTopVersion:Long', ITOP_VERSION, ITOP_REVISION, ITOP_BUILD_DATE);
|
||||
}
|
||||
|
||||
// Render the text of the global search form
|
||||
$sText = Utils::ReadParam('text', '');
|
||||
$sOnClick = "";
|
||||
if (empty($sText))
|
||||
{
|
||||
// if no search text is supplied then
|
||||
// 1) the search text is filled with "your search"
|
||||
// 2) clicking on it will erase it
|
||||
$sText = Dict::S("UI:YourSearch");
|
||||
$sOnClick = " onclick=\"this.value='';this.onclick=null;\"";
|
||||
}
|
||||
|
||||
$sForm = $this->GetSiloSelectionForm();
|
||||
|
||||
// Render the tabs in the page (if any)
|
||||
foreach($this->m_aTabs as $sTabContainerName => $m_aTabs)
|
||||
{
|
||||
$sTabs = '';
|
||||
$container_index = 0;
|
||||
if (count($m_aTabs) > 0)
|
||||
{
|
||||
$sTabs = "<!-- tabs -->\n<div id=\"tabbedContent_{$container_index}\" 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_$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_$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++;
|
||||
}
|
||||
$sUserName = UserRights::GetUser();
|
||||
$sIsAdmin = UserRights::IsAdministrator() ? '(Administrator)' : '';
|
||||
if (UserRights::IsAdministrator())
|
||||
{
|
||||
$sLogonMessage = Dict::Format('UI:LoggedAsMessage+Admin', $sUserName);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
|
||||
}
|
||||
$sLogOffMenu = "<span id=\"logOffBtn\"><ul><li><img src=\"../images/onOffBtn.png\"><ul>";
|
||||
$sLogOffMenu .= "<li><span>$sLogonMessage</span></li>\n";
|
||||
|
||||
if (utils::CanLogOff() && UserRights::CanLogOff())
|
||||
{
|
||||
//$sLogOffMenu .= "<li><a href=\"../pages/UI.php?loginop=logoff\">".Dict::S('UI:LogOffMenu')."</a></li>\n";
|
||||
$sLogOffMenu .= "<li><a href=\"../pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a></li>\n";
|
||||
}
|
||||
if (UserRights::CanChangePassword())
|
||||
{
|
||||
$sLogOffMenu .= "<li><a href=\"../pages/UI.php?loginop=change_pwd\">".Dict::S('UI:ChangePwdMenu')."</a></li>\n";
|
||||
}
|
||||
$sLogOffMenu .= "</ul>\n</li>\n</ul></span>\n";
|
||||
|
||||
$sRestrictions = '';
|
||||
if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
|
||||
{
|
||||
if (!MetaModel::DBHasAccess(ACCESS_ADMIN_WRITE))
|
||||
{
|
||||
$sRestrictions = Dict::S('UI:AccessRO-All');
|
||||
}
|
||||
}
|
||||
elseif (!MetaModel::DBHasAccess(ACCESS_USER_WRITE))
|
||||
{
|
||||
$sRestrictions = Dict::S('UI:AccessRO-Users');
|
||||
}
|
||||
|
||||
if (strlen($sRestrictions) > 0)
|
||||
{
|
||||
$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
|
||||
$sApplicationBanner = '<div id="admin-banner">';
|
||||
$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
|
||||
$sApplicationBanner .= ' <b>'.$sRestrictions.'</b>';
|
||||
if (strlen($sAdminMessage) > 0)
|
||||
{
|
||||
$sApplicationBanner .= ' <b>'.$sAdminMessage.'</b>';
|
||||
}
|
||||
$sApplicationBanner .= '</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sApplicationBanner = '';
|
||||
}
|
||||
|
||||
$sOnlineHelpUrl = MetaModel::GetConfig()->Get('online_help');
|
||||
//$sLogOffMenu = "<span id=\"logOffBtn\" style=\"height:55px;padding:0;margin:0;\"><img src=\"../images/onOffBtn.png\"></span>";
|
||||
|
||||
echo '<div id="left-pane" class="ui-layout-west">';
|
||||
echo '<!-- Beginning of the left pane -->';
|
||||
echo ' <div id="header-logo">';
|
||||
echo ' <div id="top-left"></div><div id="logo"><a href="http://www.combodo.com/itop"><img src="../images/itop-logo.png" title="'.$sVersionString.'" style="border:0; margin-top:16px; margin-right:40px;"/></a></div>';
|
||||
echo ' </div>';
|
||||
echo ' <div class="header-menu">';
|
||||
echo ' <div class="icon ui-state-default ui-corner-all"><span id="tPinMenu" class="ui-icon ui-icon-pin-w">pin</span></div>';
|
||||
echo ' <div style="text-align:center;">'.$sForm.'</div>';
|
||||
echo ' </div>';
|
||||
echo ' <div id="menu" class="ui-layout-content">';
|
||||
echo ' <div id="inner_menu">';
|
||||
echo ' <div id="accordion">';
|
||||
echo $this->m_sMenu;
|
||||
echo ' <!-- Beginning of the accordion menu -->';
|
||||
echo ' <!-- End of the accordion menu-->';
|
||||
echo ' </div>';
|
||||
echo ' </div> <!-- /inner menu -->';
|
||||
echo ' </div> <!-- /menu -->';
|
||||
echo ' <div class="footer"><a href="http://www.combodo.com" title="www.combodo.com" target="_blank"><img src="../images/logo-combodo.png"/></a></div>';
|
||||
echo '<!-- End of the left pane -->';
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="ui-layout-center">';
|
||||
echo ' <div id="top-bar" style="width:100%">';
|
||||
echo $sApplicationBanner;
|
||||
echo ' <div id="global-search"><form action="../pages/UI.php"><table><tr><td></td><td id="g-search-input"><input type="text" name="text" value="'.$sText.'"'.$sOnClick.'/></td>';
|
||||
echo '<td><input type="image" src="../images/searchBtn.png"/></a></td>';
|
||||
echo '<td><a style="background:transparent;" href="'.$sOnlineHelpUrl.'" target="_blank"><img style="border:0;padding-left:20px;padding-right:10px;" title="'.Dict::S('UI:Help').'" src="../images/help.png"/></td>';
|
||||
echo '<td style="padding-right:20px;padding-left:10px;">'.$sLogOffMenu.'</td><td><input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
||||
//echo '<td> <input type="hidden" name="operation" value="full_text"/></td></tr></table></form></div>';
|
||||
echo ' </div>';
|
||||
echo ' <div class="ui-layout-content">';
|
||||
echo ' <!-- Beginning of page content -->';
|
||||
echo $this->s_content;
|
||||
echo ' <!-- End of page content -->';
|
||||
echo ' </div>';
|
||||
echo '</div>';
|
||||
/*
|
||||
echo "<div class=\"iTopLogo\" title=\"$sVersionString\"><span>iTop</span></div>\n";
|
||||
//echo "<div id=\"GlobalSearch\"><div style=\"border: 1px solid #999; padding:1px; background-color:#fff;\"><img src=\"../images/magnifier.gif\"/><input style=\"border:0\" type=\"text\" size=\"15\" title=\"Global Search\"></input></div></div>\n";
|
||||
$sText = Utils::ReadParam('text', '');
|
||||
$sOnClick = "";
|
||||
if (empty($sText))
|
||||
{
|
||||
// if no search text is supplied then
|
||||
// 1) the search text is filled with "your search"
|
||||
// 2) clicking on it will erase it
|
||||
$sText = Dict::S("UI:YourSearch");
|
||||
$sOnClick = " onclick=\"this.value='';this.onclick=null;\"";
|
||||
}
|
||||
$sUserName = UserRights::GetUser();
|
||||
$sIsAdmin = UserRights::IsAdministrator() ? '(Administrator)' : '';
|
||||
if (UserRights::IsAdministrator())
|
||||
{
|
||||
$sLogonMessage = Dict::Format('UI:LoggedAsMessage+Admin', $sUserName);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLogonMessage = Dict::Format('UI:LoggedAsMessage', $sUserName);
|
||||
}
|
||||
$sLogOffBtn = Dict::S('UI:Button:Logoff');
|
||||
$sSearchBtn = Dict::S('UI:Button:GlobalSearch');
|
||||
echo "<div id=\"Login\" style=\"position:absolute; top:18px; right:16px; width:600px;\">{$sLogonMessage} ";
|
||||
echo "<form action=\"../pages/UI.php\" method=\"post\" style=\"display:inline\">\n";
|
||||
echo "<input type=\"submit\" value=\"$sLogOffBtn\" />\n";
|
||||
echo "<input type=\"hidden\" name=\"loginop\" value=\"logoff\" />\n";
|
||||
echo "</form>\n";
|
||||
echo "<form action=\"../pages/UI.php\" style=\"display:inline\"><div style=\"padding:1px; background-color:#fff;display:inline;\"><img src=\"../images/magnifier.gif\"/><input style=\"border:0\" type=\"text\" size=\"15\" title=\"Global Search\" name=\"text\" value=\"$sText\"$sOnClick></input></div><input type=\"submit\" value=\"$sSearchBtn\" />
|
||||
<input type=\"hidden\" name=\"operation\" value=\"full_text\" /></form>\n";
|
||||
echo "</div>\n";
|
||||
|
||||
echo "</div>\n";
|
||||
|
||||
// Display the menu
|
||||
echo "<div id=\"MySplitter\">\n";
|
||||
echo " <div id=\"LeftPane\">\n";
|
||||
echo $this->m_sMenu;
|
||||
echo " </div> <!-- LeftPane -->\n";
|
||||
|
||||
echo "<div id=\"RightPane\">\n";
|
||||
|
||||
|
||||
// Display the page's content
|
||||
echo $this->s_content;
|
||||
|
||||
*/
|
||||
// Add the captured output
|
||||
if (trim($s_captured_output) != "")
|
||||
{
|
||||
echo "<div id=\"rawOutput\" title=\"Debug Output\"><div style=\"height:500px; overflow-y:auto;\">$s_captured_output</div></div>\n";
|
||||
}
|
||||
echo $this->s_deferred_content;
|
||||
echo "<div style=\"display:none\" title=\"ex2\" id=\"ex2\">Please wait...</div>\n"; // jqModal Window
|
||||
echo "<div style=\"display:none\" title=\"dialog\" id=\"ModalDlg\"></div>";
|
||||
echo "<div style=\"display:none\" id=\"ajax_content\"></div>";
|
||||
|
||||
echo "</body>\n";
|
||||
echo "</html>\n";
|
||||
}
|
||||
|
||||
public function AddTabContainer($sTabContainer)
|
||||
{
|
||||
$this->m_aTabs[$sTabContainer] = array();
|
||||
$this->add("\$Tabs:$sTabContainer\$");
|
||||
}
|
||||
|
||||
public function AddToTab($sTabContainer, $sTabLabel, $sHtml)
|
||||
{
|
||||
if (!isset($this->m_aTabs[$sTabContainer][$sTabLabel]))
|
||||
{
|
||||
// Set the content of the tab
|
||||
$this->m_aTabs[$sTabContainer][$sTabLabel] = $sHtml;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Append to the content of the tab
|
||||
$this->m_aTabs[$sTabContainer][$sTabLabel] .= $sHtml;
|
||||
}
|
||||
}
|
||||
|
||||
public function SetCurrentTabContainer($sTabContainer = '')
|
||||
{
|
||||
$sPreviousTabContainer = $this->m_sCurrentTabContainer;
|
||||
$this->m_sCurrentTabContainer = $sTabContainer;
|
||||
return $sPreviousTabContainer;
|
||||
}
|
||||
|
||||
public function SetCurrentTab($sTabLabel = '')
|
||||
{
|
||||
$sPreviousTab = $this->m_sCurrentTab;
|
||||
$this->m_sCurrentTab = $sTabLabel;
|
||||
return $sPreviousTab;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$container_index = 0;
|
||||
$tab_index = 0;
|
||||
foreach($this->m_aTabs as $sCurrentTabContainerName => $aTabs)
|
||||
{
|
||||
if ($sTabContainer == $sCurrentTabContainerName)
|
||||
{
|
||||
foreach($aTabs as $sCurrentTabLabel => $void)
|
||||
{
|
||||
if ($sCurrentTabLabel == $sTabLabel)
|
||||
{
|
||||
break;
|
||||
}
|
||||
$tab_index++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
$container_index++;
|
||||
}
|
||||
$sSelector = '#tabbedContent_'.$container_index.' > ul';
|
||||
$this->add_ready_script("$('$sSelector').tabs('select', $tab_index);");
|
||||
}
|
||||
|
||||
public function StartCollapsibleSection($sSectionLabel, $bOpen = false)
|
||||
{
|
||||
$this->add($this->GetStartCollapsibleSection($sSectionLabel, $bOpen));
|
||||
}
|
||||
|
||||
public function GetStartCollapsibleSection($sSectionLabel, $bOpen = false)
|
||||
{
|
||||
$sHtml = '';
|
||||
static $iSectionId = 0;
|
||||
$sImgStyle = $bOpen ? ' open' : '';
|
||||
$sHtml .= "<a id=\"LnkCollapse_$iSectionId\" class=\"CollapsibleLabel{$sImgStyle}\" href=\"#\">$sSectionLabel</a></br>\n";
|
||||
$sStyle = $bOpen ? '' : 'style="display:none" ';
|
||||
$sHtml .= "<div id=\"Collapse_$iSectionId\" $sStyle>";
|
||||
$this->add_ready_script("\$(\"#LnkCollapse_$iSectionId\").click(function() {\$(\"#Collapse_$iSectionId\").slideToggle('normal'); $(\"#LnkCollapse_$iSectionId\").toggleClass('open');});");
|
||||
//$this->add_ready_script("$('#LnkCollapse_$iSectionId').hide();");
|
||||
$iSectionId++;
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
public function EndCollapsibleSection()
|
||||
{
|
||||
$this->add($this->GetEndCollapsibleSection());
|
||||
}
|
||||
|
||||
public function GetEndCollapsibleSection()
|
||||
{
|
||||
return "</div>";
|
||||
}
|
||||
|
||||
public function add($sHtml)
|
||||
{
|
||||
if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab))
|
||||
{
|
||||
$this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml);
|
||||
}
|
||||
else
|
||||
{
|
||||
parent::add($sHtml);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public function AddSearchForm($sClassName, $bOpen = false)
|
||||
{
|
||||
$iSearchSectionId = 0;
|
||||
|
||||
$sStyle = $bOpen ? 'SearchDrawer' : 'SearchDrawer DrawerClosed';
|
||||
$this->add("<div id=\"Search_$iSearchSectionId\" class=\"$sStyle\">\n");
|
||||
$this->add("<h1>Search form for ".Metamodel::GetName($sClassName)."</h1>\n");
|
||||
$this->add_ready_script("\$(\"#LnkSearch_$iSearchSectionId\").click(function() {\$(\"#Search_$iSearchSectionId\").slideToggle('normal'); $(\"#LnkSearch_$iSearchSectionId\").toggleClass('open');});");
|
||||
$oFilter = new DBObjectSearch($sClassName);
|
||||
$sFilter = $oFilter->serialize();
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
cmdbAbstractObject::DisplaySearchForm($this, $oSet, array('operation' => 'search', 'filter' => $sFilter, 'search_form' => true));
|
||||
$this->add("</div>\n");
|
||||
$this->add("<div class=\"HRDrawer\"/></div>\n");
|
||||
$this->add("<div id=\"LnkSearch_$iSearchSectionId\" class=\"DrawerHandle\">Search</div>\n");
|
||||
|
||||
|
||||
$iSearchSectionId++;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
?>
|
||||
56
application/itopwizardwebpage.class.inc.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?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 iTopWizardWebPage
|
||||
*
|
||||
* @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');
|
||||
/**
|
||||
* Web page to display a wizard in the iTop framework
|
||||
*/
|
||||
class iTopWizardWebPage extends iTopWebPage
|
||||
{
|
||||
var $m_iCurrentStep;
|
||||
var $m_aSteps;
|
||||
public function __construct($sTitle, $currentOrganization, $iCurrentStep, $aSteps)
|
||||
{
|
||||
parent::__construct($sTitle." - step $iCurrentStep of ".count($aSteps)." - ".$aSteps[$iCurrentStep - 1], $currentOrganization);
|
||||
$this->m_iCurrentStep = $iCurrentStep;
|
||||
$this->m_aSteps = $aSteps;
|
||||
}
|
||||
|
||||
public function output()
|
||||
{
|
||||
$aSteps = array();
|
||||
$iIndex = 0;
|
||||
foreach($this->m_aSteps as $sStepTitle)
|
||||
{
|
||||
$iIndex++;
|
||||
$sStyle = ($iIndex == $this->m_iCurrentStep) ? 'wizActiveStep' : 'wizStep';
|
||||
$aSteps[] = "<div class=\"$sStyle\"><span>$sStepTitle</span></div>";
|
||||
}
|
||||
$sWizardHeader = "<div class=\"wizHeader\"><h1>{$this->s_title}</h1>\n".implode("<div class=\"wizSeparator\"><img align=\"bottom\" src=\"../images/wizArrow.gif\"></div>", $aSteps)."<br style=\"clear:both;\"/></div>\n";
|
||||
$this->s_content = "$sWizardHeader<div class=\"wizContainer\">".$this->s_content."</div>";
|
||||
parent::output();
|
||||
}
|
||||
}
|
||||
?>
|
||||
415
application/loginwebpage.class.inc.php
Normal file
@@ -0,0 +1,415 @@
|
||||
<?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 LoginWebPage
|
||||
*
|
||||
* @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");
|
||||
/**
|
||||
* Web page used for displaying the login form
|
||||
*/
|
||||
class LoginWebPage extends NiceWebPage
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct("iTop Login");
|
||||
$this->add_style(<<<EOF
|
||||
body {
|
||||
background: #eee;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#login-logo {
|
||||
margin-top: 150px;
|
||||
width: 300px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
background: #f6f6f1;
|
||||
height: 54px;
|
||||
border-top: 1px solid #000;
|
||||
border-left: 1px solid #000;
|
||||
border-right: 1px solid #000;
|
||||
border-bottom: 0;
|
||||
text-align: center;
|
||||
}
|
||||
#login-logo img {
|
||||
border: 0;
|
||||
}
|
||||
#login {
|
||||
width: 300px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #000;
|
||||
border-left: 1px solid #000;
|
||||
border-right: 1px solid #000;
|
||||
border-top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
#pwd, #user,#old_pwd, #new_pwd, #retype_new_pwd {
|
||||
width: 10em;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #1C94C4;
|
||||
font-size: 16pt;
|
||||
}
|
||||
.v-spacer {
|
||||
padding-top: 1em;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
public function DisplayLoginForm($sLoginType, $bFailedLogin = false)
|
||||
{
|
||||
switch($sLoginType)
|
||||
{
|
||||
case 'basic':
|
||||
case 'url':
|
||||
$this->add_header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
|
||||
$this->add_header('HTTP/1.0 401 Unauthorized');
|
||||
// Note: displayed when the user will click on Cancel
|
||||
$this->add('<p><strong>'.Dict::S('UI:Login:Error:AccessRestricted').'</strong></p>');
|
||||
break;
|
||||
|
||||
case 'external':
|
||||
case 'form':
|
||||
default: // In case the settings get messed up...
|
||||
$sAuthUser = utils::ReadParam('auth_user', '');
|
||||
$sAuthPwd = utils::ReadParam('suggest_pwd', '');
|
||||
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||
$this->add("<div id=\"login-logo\"><a href=\"http://www.combodo.com/itop\"><img title=\"$sVersionShort\" src=\"../images/itop-logo-external.png\"></a></div>\n");
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:Welcome')."</h1>\n");
|
||||
if ($bFailedLogin)
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectLoginPassword')."</p>\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add("<p>".Dict::S('UI:Login:IdentifyYourself')."</p>\n");
|
||||
}
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table width=\"100%\">\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"user\">".Dict::S('UI:Login:UserNamePrompt').":</label></td><td style=\"text-align:left\"><input id=\"user\" type=\"text\" name=\"auth_user\" value=\"$sAuthUser\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"pwd\">".Dict::S('UI:Login:PasswordPrompt').":</label></td><td style=\"text-align:left\"><input id=\"pwd\" type=\"password\" name=\"auth_pwd\" value=\"$sAuthPwd\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"> <input type=\"submit\" value=\"".Dict::S('UI:Button:Login')."\" /></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"login\" />\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div>\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function DisplayChangePwdForm($bFailedLogin = false)
|
||||
{
|
||||
$sAuthUser = utils::ReadParam('auth_user', '');
|
||||
$sAuthPwd = utils::ReadParam('suggest_pwd', '');
|
||||
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||
$sInconsistenPwdMsg = Dict::S('UI:Login:RetypePwdDoesNotMatch');
|
||||
$this->add_script(<<<EOF
|
||||
function GoBack()
|
||||
{
|
||||
window.history.back();
|
||||
}
|
||||
|
||||
function DoCheckPwd()
|
||||
{
|
||||
if ($('#new_pwd').val() != $('#retype_new_pwd').val())
|
||||
{
|
||||
alert('$sInconsistenPwdMsg');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$this->add("<div id=\"login-logo\"><a href=\"http://www.combodo.com/itop\"><img title=\"$sVersionShort\" src=\"../images/itop-logo.png\"></a></div>\n");
|
||||
$this->add("<div id=\"login\">\n");
|
||||
$this->add("<h1>".Dict::S('UI:Login:ChangeYourPassword')."</h1>\n");
|
||||
if ($bFailedLogin)
|
||||
{
|
||||
$this->add("<p class=\"hilite\">".Dict::S('UI:Login:IncorrectOldPassword')."</p>\n");
|
||||
}
|
||||
$this->add("<form method=\"post\">\n");
|
||||
$this->add("<table width=\"100%\">\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"old_pwd\">".Dict::S('UI:Login:OldPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"old_pwd\" name=\"old_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"new_pwd\">".Dict::S('UI:Login:NewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"new_pwd\" name=\"new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td style=\"text-align:right\"><label for=\"retype_new_pwd\">".Dict::S('UI:Login:RetypeNewPasswordPrompt').":</label></td><td style=\"text-align:left\"><input type=\"password\" id=\"retype_new_pwd\" name=\"retype_new_pwd\" value=\"\" /></td></tr>\n");
|
||||
$this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"> <input type=\"button\" onClick=\"GoBack();\" value=\"".Dict::S('UI:Button:Cancel')."\" /> <input type=\"submit\" onClick=\"return DoCheckPwd();\" value=\"".Dict::S('UI:Button:ChangePassword')."\" /></td></tr>\n");
|
||||
$this->add("</table>\n");
|
||||
$this->add("<input type=\"hidden\" name=\"loginop\" value=\"do_change_pwd\" />\n");
|
||||
$this->add("</form>\n");
|
||||
$this->add("</div>\n");
|
||||
}
|
||||
|
||||
static function ResetSession()
|
||||
{
|
||||
if (isset($_SESSION['login_mode']))
|
||||
{
|
||||
$sPreviousLoginMode = $_SESSION['login_mode'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sPreviousLoginMode = '';
|
||||
}
|
||||
// Unset all of the session variables.
|
||||
$_SESSION = array();
|
||||
// If it's desired to kill the session, also delete the session cookie.
|
||||
// Note: This will destroy the session, and not just the session data!
|
||||
if (isset($_COOKIE[session_name()]))
|
||||
{
|
||||
setcookie(session_name(), '', time()-3600, '/');
|
||||
}
|
||||
// Finally, destroy the session.
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
static function SecureConnectionRequired()
|
||||
{
|
||||
return MetaModel::GetConfig()->GetSecureConnectionRequired();
|
||||
}
|
||||
|
||||
static function IsConnectionSecure()
|
||||
{
|
||||
$bSecured = false;
|
||||
|
||||
if (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!='off'))
|
||||
{
|
||||
$bSecured = true;
|
||||
}
|
||||
return $bSecured;
|
||||
}
|
||||
|
||||
protected static function Login()
|
||||
{
|
||||
if (self::SecureConnectionRequired() && !self::IsConnectionSecure())
|
||||
{
|
||||
// Non secured URL... redirect to a secured one
|
||||
$sUrl = Utils::GetAbsoluteUrl(true /* query string */, true /* force HTTPS */);
|
||||
header("Location: $sUrl");
|
||||
exit;
|
||||
}
|
||||
|
||||
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
|
||||
|
||||
if (isset($_SESSION['auth_user']))
|
||||
{
|
||||
//echo "User: ".$_SESSION['auth_user']."\n";
|
||||
// Already authentified
|
||||
UserRights::Login($_SESSION['auth_user']); // Login & set the user's language
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$index = 0;
|
||||
$sLoginMode = '';
|
||||
$sAuthentication = 'internal';
|
||||
while(($sLoginMode == '') && ($index < count($aAllowedLoginTypes)))
|
||||
{
|
||||
$sLoginType = $aAllowedLoginTypes[$index];
|
||||
switch($sLoginType)
|
||||
{
|
||||
case 'form':
|
||||
// iTop standard mode: form based authentication
|
||||
$sAuthUser = utils::ReadPostedParam('auth_user', '');
|
||||
$sAuthPwd = utils::ReadPostedParam('auth_pwd', '');
|
||||
if ($sAuthUser != '')
|
||||
{
|
||||
$sLoginMode = 'form';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'basic':
|
||||
// Standard PHP authentication method, works with Apache...
|
||||
// Case 1) Apache running in CGI mode + rewrite rules in .htaccess
|
||||
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
|
||||
{
|
||||
list($sAuthUser, $sAuthPwd) = explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
|
||||
$sLoginMode = 'basic';
|
||||
}
|
||||
else if (isset($_SERVER['PHP_AUTH_USER']))
|
||||
{
|
||||
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
|
||||
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
|
||||
$sLoginMode = 'basic';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'external':
|
||||
// Web server supplied authentication
|
||||
$bExternalAuth = false;
|
||||
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
|
||||
$sEval = '$bExternalAuth = isset('.$sExtAuthVar.');';
|
||||
eval($sEval);
|
||||
if ($bExternalAuth)
|
||||
{
|
||||
eval('$sAuthUser = '.$sExtAuthVar.';'); // Retrieve the value
|
||||
$sAuthPwd = ''; // No password in this case the web server already authentified the user...
|
||||
$sLoginMode = 'external';
|
||||
$sAuthentication = 'external';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'url':
|
||||
// Credentials passed directly in the url
|
||||
$sAuthUser = utils::ReadParam('auth_user', '');
|
||||
if ($sAuthUser != '')
|
||||
{
|
||||
$sAuthPwd = utils::ReadParam('auth_pwd', '');
|
||||
$sLoginMode = 'url';
|
||||
}
|
||||
break;
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
//echo "\nsLoginMode: $sLoginMode (user: $sAuthUser / pwd: $sAuthPwd\n)";
|
||||
if ($sLoginMode == '')
|
||||
{
|
||||
// First connection
|
||||
$sDesiredLoginMode = utils::ReadParam('login_mode');
|
||||
if (in_array($sDesiredLoginMode, $aAllowedLoginTypes))
|
||||
{
|
||||
$sLoginMode = $sDesiredLoginMode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
|
||||
}
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sAuthentication))
|
||||
{
|
||||
self::ResetSession();
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, true /* failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
else
|
||||
{
|
||||
// User is Ok, let's save it in the session and proceed with normal login
|
||||
UserRights::Login($sAuthUser, $sAuthentication); // Login & set the user's language
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$_SESSION['login_mode'] = $sLoginMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is already authentified, if yes, then performs some additional validations:
|
||||
* - if $bMustBeAdmin is true, then the user must be an administrator, otherwise an error is displayed
|
||||
* - if $bIsAllowedToPortalUsers is false and the user has only access to the portal, then the user is redirected to the portal
|
||||
* @param bool $bMustBeAdmin Whether or not the user must be an admin to access the current page
|
||||
* @param bool $bIsAllowedToPortalUsers Whether or not the current page is considered as part of the portal
|
||||
*/
|
||||
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false)
|
||||
{
|
||||
$operation = utils::ReadParam('loginop', '');
|
||||
session_name(MetaModel::GetConfig()->Get('session_name'));
|
||||
session_start();
|
||||
|
||||
if ($operation == 'logoff')
|
||||
{
|
||||
if (isset($_SESSION['login_mode']))
|
||||
{
|
||||
$sLoginMode = $_SESSION['login_mode'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
|
||||
if (count($aAllowedLoginTypes) > 0)
|
||||
{
|
||||
$sLoginMode = $aAllowedLoginTypes[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLoginMode = 'form';
|
||||
}
|
||||
}
|
||||
self::ResetSession();
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* not a failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
else if ($operation == 'change_pwd')
|
||||
{
|
||||
$sAuthUser = $_SESSION['auth_user'];
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage->DisplayChangePwdForm();
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
if ($operation == 'do_change_pwd')
|
||||
{
|
||||
$sAuthUser = $_SESSION['auth_user'];
|
||||
UserRights::Login($sAuthUser); // Set the user's language
|
||||
$sOldPwd = utils::ReadPostedParam('old_pwd');
|
||||
$sNewPwd = utils::ReadPostedParam('new_pwd');
|
||||
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
|
||||
{
|
||||
$oPage = new LoginWebPage();
|
||||
$oPage->DisplayChangePwdForm(true); // old pwd was wrong
|
||||
$oPage->output();
|
||||
}
|
||||
}
|
||||
|
||||
self::Login();
|
||||
|
||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||
{
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
$oP = new SetupWebPage(Dict::S('UI:PageTitle:FatalError'));
|
||||
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
|
||||
$oP->p("<a href=\"../pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
|
||||
$oP->output();
|
||||
exit;
|
||||
}
|
||||
elseif ( (!$bIsAllowedToPortalUsers) && (UserRights::IsPortalUser()))
|
||||
{
|
||||
// No rights to be here, redirect to the portal
|
||||
header('Location: ../portal/index.php');
|
||||
}
|
||||
}
|
||||
|
||||
} // End of class
|
||||
?>
|
||||
687
application/menunode.class.inc.php
Normal file
@@ -0,0 +1,687 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Construction and display of the application's main menu
|
||||
*
|
||||
* @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');
|
||||
require_once(APPROOT.'/application/template.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
* This class manipulates, stores and displays the navigation menu used in the application
|
||||
* In order to improve the modularity of the data model and to ease the update/migration
|
||||
* between evolving data models, the menus are no longer stored in the database, but are instead
|
||||
* built on the fly each time a page is loaded.
|
||||
* The application's menu is organized into top-level groups with, inside each group, a tree of menu items.
|
||||
* Top level groups do not display any content, they just expand/collapse.
|
||||
* Sub-items drive the actual content of the page, they are based either on templates, OQL queries or full (external?) web pages.
|
||||
*
|
||||
* Example:
|
||||
* Here is how to insert the following items in the application's menu:
|
||||
* +----------------------------------------+
|
||||
* | Configuration Management Group | >> Top level group
|
||||
* +----------------------------------------+
|
||||
* + Configuration Management Overview >> Template based menu item
|
||||
* + Contacts >> Template based menu item
|
||||
* + Persons >> Plain list (OQL based)
|
||||
* + Teams >> Plain list (OQL based)
|
||||
*
|
||||
* // Create the top-level group. fRank = 1, means it will be inserted after the group '0', which is usually 'Welcome'
|
||||
* $oConfigMgmtMenu = new MenuGroup('ConfigurationManagementMenu', 1);
|
||||
* // Create an entry, based on a custom template, for the Configuration management overview, under the top-level group
|
||||
* new TemplateMenuNode('ConfigurationManagementMenu', '../somedirectory/configuration_management_menu.html', $oConfigMgmtMenu->GetIndex(), 0);
|
||||
* // Create an entry (template based) for the overview of contacts
|
||||
* $oContactsMenu = new TemplateMenuNode('ContactsMenu', '../somedirectory/configuration_management_menu.html',$oConfigMgmtMenu->GetIndex(), 1);
|
||||
* // Plain list of persons
|
||||
* new OQLMenuNode('PersonsMenu', 'SELECT bizPerson', $oContactsMenu->GetIndex(), 0);
|
||||
*
|
||||
*/
|
||||
|
||||
class ApplicationMenu
|
||||
{
|
||||
static $aRootMenus = array();
|
||||
static $aMenusIndex = array();
|
||||
|
||||
/**
|
||||
* Main function to add a menu entry into the application, can be called during the definition
|
||||
* of the data model objects
|
||||
*/
|
||||
static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex = -1, $fRank)
|
||||
{
|
||||
$index = self::GetMenuIndexById($oMenuNode->GetMenuId());
|
||||
if ($index == -1)
|
||||
{
|
||||
// The menu does not already exist, insert it
|
||||
$index = count(self::$aMenusIndex);
|
||||
self::$aMenusIndex[$index] = array( 'node' => $oMenuNode, 'children' => array());
|
||||
if ($iParentIndex == -1)
|
||||
{
|
||||
self::$aRootMenus[] = array ('rank' => $fRank, 'index' => $index);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index);
|
||||
}
|
||||
}
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point to display the whole menu into the web page, used by iTopWebPage
|
||||
*/
|
||||
static public function DisplayMenu(iTopWebPage $oPage, $aExtraParams)
|
||||
{
|
||||
// Sort the root menu based on the rank
|
||||
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
$iAccordion = 0;
|
||||
$iActiveMenu = ApplicationMenu::GetActiveNodeId();
|
||||
foreach(self::$aRootMenus as $aMenu)
|
||||
{
|
||||
$oMenuNode = self::GetMenuNode($aMenu['index']);
|
||||
if (!$oMenuNode->IsEnabled()) continue; // Don't display a non-enabled menu
|
||||
$oPage->AddToMenu('<h3>'.$oMenuNode->GetTitle().'</h3>');
|
||||
$oPage->AddToMenu('<div>');
|
||||
$aChildren = self::GetChildren($aMenu['index']);
|
||||
if (count($aChildren) > 0)
|
||||
{
|
||||
$oPage->AddToMenu('<ul>');
|
||||
$bActive = self::DisplaySubMenu($oPage, $aChildren, $aExtraParams, $iActiveMenu);
|
||||
$oPage->AddToMenu('</ul>');
|
||||
if ($bActive)
|
||||
{
|
||||
$oPage->add_ready_script("$('#accordion').accordion('activate', $iAccordion);");
|
||||
$oPage->add_ready_script("$('#accordion').accordion('option', {collapsible: true});"); // Make it auto-collapsible once it has been opened properly
|
||||
}
|
||||
}
|
||||
$oPage->AddToMenu('</div>');
|
||||
$iAccordion++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the display of the sub-menus (called recursively if necessary)
|
||||
* @return true if the currently selected menu is one of the submenus
|
||||
*/
|
||||
static protected function DisplaySubMenu($oPage, $aMenus, $aExtraParams, $iActiveMenu = -1)
|
||||
{
|
||||
// Sort the menu based on the rank
|
||||
$bActive = false;
|
||||
usort($aMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
foreach($aMenus as $aMenu)
|
||||
{
|
||||
$index = $aMenu['index'];
|
||||
$oMenu = self::GetMenuNode($index);
|
||||
if ($oMenu->IsEnabled())
|
||||
{
|
||||
$aChildren = self::GetChildren($index);
|
||||
$sCSSClass = (count($aChildren) > 0) ? ' class="submenu"' : '';
|
||||
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
|
||||
if ($sHyperlink != '')
|
||||
{
|
||||
$oPage->AddToMenu('<li'.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$oMenu->GetTitle().'</a></li>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->AddToMenu('<li'.$sCSSClass.'>'.$oMenu->GetTitle().'</li>');
|
||||
}
|
||||
$aCurrentMenu = self::$aMenusIndex[$index];
|
||||
if ($iActiveMenu == $index)
|
||||
{
|
||||
$bActive = true;
|
||||
}
|
||||
if (count($aChildren) > 0)
|
||||
{
|
||||
$oPage->AddToMenu('<ul>');
|
||||
$bActive |= self::DisplaySubMenu($oPage, $aChildren, $aExtraParams, $iActiveMenu);
|
||||
$oPage->AddToMenu('</ul>');
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bActive;
|
||||
}
|
||||
/**
|
||||
* Helper function to sort the menus based on their rank
|
||||
*/
|
||||
static public function CompareOnRank($a, $b)
|
||||
{
|
||||
$result = 1;
|
||||
if ($a['rank'] == $b['rank'])
|
||||
{
|
||||
$result = 0;
|
||||
}
|
||||
if ($a['rank'] < $b['rank'])
|
||||
{
|
||||
$result = -1;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to retrieve the MenuNodeObject based on its ID
|
||||
*/
|
||||
static public function GetMenuNode($index)
|
||||
{
|
||||
return isset(self::$aMenusIndex[$index]) ? self::$aMenusIndex[$index]['node'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the list of child(ren) of a menu
|
||||
*/
|
||||
static protected function GetChildren($index)
|
||||
{
|
||||
return self::$aMenusIndex[$index]['children'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the ID of a menu based on its name
|
||||
* @param string $sTitle Title of the menu (as passed when creating the menu)
|
||||
* @return integer ID of the menu, or -1 if not found
|
||||
*/
|
||||
static public function GetMenuIndexById($sTitle)
|
||||
{
|
||||
$index = -1;
|
||||
foreach(self::$aMenusIndex as $aMenu)
|
||||
{
|
||||
if ($aMenu['node']->GetMenuId() == $sTitle)
|
||||
{
|
||||
$index = $aMenu['node']->GetIndex();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the currently active menu (if any, otherwise the first menu is the default)
|
||||
* @return MenuNode or null if there is no menu at all !
|
||||
*/
|
||||
static public function GetActiveNodeId()
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$iMenuIndex = $oAppContext->GetCurrentValue('menu', -1);
|
||||
|
||||
if ($iMenuIndex == -1)
|
||||
{
|
||||
// Make sure the root menu is sorted on 'rank'
|
||||
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
$oFirstGroup = self::GetMenuNode(self::$aRootMenus[0]['index']);
|
||||
$oMenuNode = self::GetMenuNode(self::$aMenusIndex[$oFirstGroup->GetIndex()]['children'][0]['index']);
|
||||
$iMenuIndex = $oMenuNode->GetIndex();
|
||||
}
|
||||
return $iMenuIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Root class for all the kind of node in the menu tree, data model providers are responsible for instantiating
|
||||
* MenuNodes (i.e instances from derived classes) in order to populate the application's menu. Creating an objet
|
||||
* derived from MenuNode is enough to have it inserted in the application's main menu.
|
||||
* The class iTopWebPage, takes care of 3 items:
|
||||
* +--------------------+
|
||||
* | Welcome |
|
||||
* +--------------------+
|
||||
* Welcome To iTop
|
||||
* +--------------------+
|
||||
* | Tools |
|
||||
* +--------------------+
|
||||
* CSV Import
|
||||
* +--------------------+
|
||||
* | Admin Tools |
|
||||
* +--------------------+
|
||||
* User Accounts
|
||||
* Profiles
|
||||
* Notifications
|
||||
* Run Queries
|
||||
* Export
|
||||
* Data Model
|
||||
* Universal Search
|
||||
*
|
||||
* All the other menu items must constructed along with the various data model modules
|
||||
*/
|
||||
abstract class MenuNode
|
||||
{
|
||||
protected $sMenuId;
|
||||
protected $index;
|
||||
|
||||
/**
|
||||
* Class of objects to check if the menu is enabled, null if none
|
||||
*/
|
||||
protected $m_sEnableClass;
|
||||
|
||||
/**
|
||||
* User Rights Action code to check if the menu is enabled, null if none
|
||||
*/
|
||||
protected $m_iEnableAction;
|
||||
|
||||
/**
|
||||
* User Rights allowed results (actually a bitmask) to check if the menu is enabled, null if none
|
||||
*/
|
||||
protected $m_iEnableActionResults;
|
||||
|
||||
/**
|
||||
* Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu
|
||||
*/
|
||||
protected $m_sEnableStimulus;
|
||||
|
||||
/**
|
||||
* Create a menu item, sets the condition to have it displayed and inserts it into the application's main menu
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
* @param integer $iParentIndex ID of the parent menu, pass -1 for top level (group) items
|
||||
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
|
||||
* @param string $sEnableClass Name of class of object
|
||||
* @param mixed $iActionCode UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
|
||||
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
|
||||
* @param string $sEnableStimulus The user can see this menu if she/he has enough rights to apply this stimulus
|
||||
* @return MenuNode
|
||||
*/
|
||||
public function __construct($sMenuId, $iParentIndex = -1, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
$this->sMenuId = $sMenuId;
|
||||
$this->m_sEnableClass = $sEnableClass;
|
||||
$this->m_iEnableAction = $iActionCode;
|
||||
$this->m_iEnableActionResults = $iAllowedResults;
|
||||
$this->m_sEnableStimulus = $sEnableStimulus;
|
||||
$this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank);
|
||||
}
|
||||
|
||||
public function GetMenuId()
|
||||
{
|
||||
return $this->sMenuId;
|
||||
}
|
||||
|
||||
public function GetTitle()
|
||||
{
|
||||
return Dict::S("Menu:$this->sMenuId");
|
||||
}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return Dict::S("Menu:$this->sMenuId+");
|
||||
}
|
||||
|
||||
public function GetIndex()
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$aExtraParams['c[menu]'] = $this->GetIndex();
|
||||
return $this->AddParams('../pages/UI.php', $aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether the menu is enabled (i.e. displayed) for the current user
|
||||
* @return bool True if enabled, false otherwise
|
||||
*/
|
||||
public function IsEnabled()
|
||||
{
|
||||
if ($this->m_sEnableClass != null)
|
||||
{
|
||||
if (MetaModel::IsValidClass($this->m_sEnableClass))
|
||||
{
|
||||
if ($this->m_sEnableStimulus != null)
|
||||
{
|
||||
if (!UserRights::IsStimulusAllowed($this->m_sEnableClass, $this->m_sEnableStimulus))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($this->m_iEnableAction != null)
|
||||
{
|
||||
$iResult = UserRights::IsActionAllowed($this->m_sEnableClass, $this->m_iEnableAction);
|
||||
if (($iResult & $this->m_iEnableActionResults))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract function RenderContent(WebPage $oPage, $aExtraParams = array());
|
||||
|
||||
protected function AddParams($sHyperlink, $aExtraParams)
|
||||
{
|
||||
if (count($aExtraParams) > 0)
|
||||
{
|
||||
$aQuery = array();
|
||||
$sSeparator = '?';
|
||||
if (strpos($sHyperlink, '?') !== false)
|
||||
{
|
||||
$sSeparator = '&';
|
||||
}
|
||||
foreach($aExtraParams as $sName => $sValue)
|
||||
{
|
||||
$aQuery[] = urlencode($sName).'='.urlencode($sValue);
|
||||
}
|
||||
$sHyperlink .= $sSeparator.implode('&', $aQuery);
|
||||
}
|
||||
return $sHyperlink;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements a top-level menu group. A group is just a container for sub-items
|
||||
* it does not display a page by itself
|
||||
*/
|
||||
class MenuGroup extends MenuNode
|
||||
{
|
||||
/**
|
||||
* Create a top-level menu group and inserts it into the application's main menu
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
* @param float $fRank Number used to order the list, the groups are sorted based on this value
|
||||
* @param string $sEnableClass Name of class of object
|
||||
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
|
||||
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
|
||||
* @return MenuGroup
|
||||
*/
|
||||
public function __construct($sMenuId, $fRank, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, -1 /* no parent, groups are at root level */, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
assert(false); // Shall never be called, groups do not display any content
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines a menu item which content is based on a custom template.
|
||||
* Note the template can be either a local file or an URL !
|
||||
*/
|
||||
class TemplateMenuNode extends MenuNode
|
||||
{
|
||||
protected $sTemplateFile;
|
||||
|
||||
/**
|
||||
* Create a menu item based on a custom template and inserts it into the application's main menu
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
* @param string $sTemplateFile Path (or URL) to the file that will be used as a template for displaying the page's content
|
||||
* @param integer $iParentIndex ID of the parent menu
|
||||
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
|
||||
* @param string $sEnableClass Name of class of object
|
||||
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
|
||||
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
|
||||
* @return MenuNode
|
||||
*/
|
||||
public function __construct($sMenuId, $sTemplateFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sTemplateFile = $sTemplateFile;
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
if ($this->sTemplateFile == '') return '';
|
||||
return parent::GetHyperlink($aExtraParams);
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$sTemplate = @file_get_contents($this->sTemplateFile);
|
||||
if ($sTemplate !== false)
|
||||
{
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
$oTemplate->Render($oPage, $aExtraParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->p("Error: failed to load template file: '{$this->sTemplateFile}'"); // No need to translate ?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines a menu item that uses a standard template to display a list of items therefore it allows
|
||||
* only two parameters: the page's title and the OQL expression defining the list of items to be displayed
|
||||
*/
|
||||
class OQLMenuNode extends MenuNode
|
||||
{
|
||||
protected $sPageTitle;
|
||||
protected $sOQL;
|
||||
protected $bSearch;
|
||||
|
||||
/**
|
||||
* Extra parameters to be passed to the display block to fine tune its appearence
|
||||
*/
|
||||
protected $m_aParams;
|
||||
|
||||
|
||||
/**
|
||||
* Create a menu item based on an OQL query and inserts it into the application's main menu
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
* @param string $sOQL OQL query defining the set of objects to be displayed
|
||||
* @param integer $iParentIndex ID of the parent menu
|
||||
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
|
||||
* @param bool $bSearch Whether or not to display a (collapsed) search frame at the top of the page
|
||||
* @param string $sEnableClass Name of class of object
|
||||
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
|
||||
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
|
||||
* @return MenuNode
|
||||
*/
|
||||
public function __construct($sMenuId, $sOQL, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sPageTitle = "Menu:$sMenuId+";
|
||||
$this->sOQL = $sOQL;
|
||||
$this->bSearch = $bSearch;
|
||||
$this->m_aParams = array();
|
||||
// Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects
|
||||
// of the class specified by the OQL...
|
||||
}
|
||||
|
||||
/**
|
||||
* Set some extra parameters to be passed to the display block to fine tune its appearence
|
||||
* @param Hash $aParams paramCode => value. See DisplayBlock::GetDisplay for the meaning of the parameters
|
||||
*/
|
||||
public function SetParameters($aParams)
|
||||
{
|
||||
$this->m_aParams = $aParams;
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
$aExtraParams = array_merge($aExtraParams, $this->m_aParams);
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($this->sOQL);
|
||||
$sIcon = MetaModel::GetClassIcon($oSearch->GetClass());
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$sIcon = '';
|
||||
}
|
||||
// The standard template used for all such pages: a (closed) search form at the top and a list of results at the bottom
|
||||
$sTemplate = '';
|
||||
|
||||
if ($this->bSearch)
|
||||
{
|
||||
$sTemplate .= <<<EOF
|
||||
<itopblock BlockClass="DisplayBlock" type="search" asynchronous="false" encoding="text/oql">$this->sOQL</itopblock>
|
||||
EOF;
|
||||
}
|
||||
$sParams = '';
|
||||
if (!empty($this->m_aParams))
|
||||
{
|
||||
$sParams = 'parameters="';
|
||||
foreach($this->m_aParams as $sName => $sValue)
|
||||
{
|
||||
$sParams .= $sName.':'.$sValue.';';
|
||||
}
|
||||
$sParams .= '"';
|
||||
}
|
||||
$sTemplate .= <<<EOF
|
||||
<p class="page-header">$sIcon<itopstring>$this->sPageTitle</itopstring></p>
|
||||
<itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql" $sParams>$this->sOQL</itopblock>
|
||||
EOF;
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
$oTemplate->Render($oPage, $aExtraParams);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This class defines a menu item that displays a search form for the given class of objects
|
||||
*/
|
||||
class SearchMenuNode extends MenuNode
|
||||
{
|
||||
protected $sPageTitle;
|
||||
protected $sClass;
|
||||
|
||||
/**
|
||||
* Create a menu item based on an OQL query and inserts it into the application's main menu
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
* @param string $sClass The class of objects to search for
|
||||
* @param string $sPageTitle Title displayed into the page's content (will be looked-up in the dictionnary for translation)
|
||||
* @param integer $iParentIndex ID of the parent menu
|
||||
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
|
||||
* @param string $sEnableClass Name of class of object
|
||||
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
|
||||
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
|
||||
* @return MenuNode
|
||||
*/
|
||||
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sPageTitle = "Menu:$sMenuId+";
|
||||
$this->sClass = $sClass;
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
// The standard template used for all such pages: an open search form at the top
|
||||
$sTemplate = <<<EOF
|
||||
<itopblock BlockClass="DisplayBlock" type="search" asynchronous="false" encoding="text/oql" parameters="open:true">SELECT $this->sClass</itopblock>
|
||||
EOF;
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
$oTemplate->Render($oPage, $aExtraParams);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines a menu that points to any web page. It takes only two parameters:
|
||||
* - The hyperlink to point to
|
||||
* - The name of the menu
|
||||
* Note: the parameter menu=xxx (where xxx is the id of the menu itself) will be added to the hyperlink
|
||||
* in order to make it the active one, if the target page is based on iTopWebPage and therefore displays the menu
|
||||
*/
|
||||
class WebPageMenuNode extends MenuNode
|
||||
{
|
||||
protected $sHyperlink;
|
||||
|
||||
/**
|
||||
* Create a menu item that points to any web page (not only UI.php)
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
* @param string $sHyperlink URL to the page to load. Use relative URL if you want to keep the application portable !
|
||||
* @param integer $iParentIndex ID of the parent menu
|
||||
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
|
||||
* @param string $sEnableClass Name of class of object
|
||||
* @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE
|
||||
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them...
|
||||
* @return MenuNode
|
||||
*/
|
||||
public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sHyperlink = $sHyperlink;
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$aExtraParams['c[menu]'] = $this->GetIndex();
|
||||
return $this->AddParams( $this->sHyperlink, $aExtraParams);
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
assert(false); // Shall never be called, the external web page will handle the display by itself
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class defines a menu that points to the page for creating a new object of the specified class.
|
||||
* It take only one parameter: the name of the class
|
||||
* Note: the parameter menu=xxx (where xxx is the id of the menu itself) will be added to the hyperlink
|
||||
* in order to make it the active one
|
||||
*/
|
||||
class NewObjectMenuNode extends MenuNode
|
||||
{
|
||||
protected $sClass;
|
||||
|
||||
/**
|
||||
* Create a menu item that points to the URL for creating a new object, the menu will be added only if the current user has enough
|
||||
* rights to create such an object (or an object of a child class)
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
* @param string $sClass URL to the page to load. Use relative URL if you want to keep the application portable !
|
||||
* @param integer $iParentIndex ID of the parent menu
|
||||
* @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value
|
||||
* @return MenuNode
|
||||
*/
|
||||
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank);
|
||||
$this->sClass = $sClass;
|
||||
}
|
||||
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$sHyperlink = '../pages/UI.php?operation=new&class='.$this->sClass;
|
||||
$aExtraParams['c[menu]'] = $this->GetIndex();
|
||||
return $this->AddParams($sHyperlink, $aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overload the check of the "enable" state of this menu to take into account
|
||||
* derived classes of objects
|
||||
*/
|
||||
public function IsEnabled()
|
||||
{
|
||||
// Enable this menu, only if the current user has enough rights to create such an object, or an object of
|
||||
// any child class
|
||||
|
||||
$aSubClasses = MetaModel::EnumChildClasses($this->sClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
|
||||
$bActionIsAllowed = false;
|
||||
|
||||
foreach($aSubClasses as $sCandidateClass)
|
||||
{
|
||||
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
{
|
||||
$bActionIsAllowed = true;
|
||||
break; // Enough for now
|
||||
}
|
||||
}
|
||||
return $bActionIsAllowed;
|
||||
}
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
assert(false); // Shall never be called, the external web page will handle the display by itself
|
||||
}
|
||||
}
|
||||
?>
|
||||
111
application/nicewebpage.class.inc.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?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 NiceWebPage
|
||||
*
|
||||
* @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");
|
||||
/**
|
||||
* Web page with some associated CSS and scripts (jquery) for a fancier display
|
||||
*/
|
||||
class NiceWebPage extends WebPage
|
||||
{
|
||||
var $m_aReadyScripts;
|
||||
|
||||
public function __construct($s_title)
|
||||
{
|
||||
parent::__construct($s_title);
|
||||
$this->m_aReadyScripts = array();
|
||||
$this->add_linked_script("../js/jquery-1.4.2.min.js");
|
||||
//$this->add_linked_script("../js/jquery.history_remote.pack.js");
|
||||
$this->add_linked_stylesheet('../css/ui-lightness/jquery-ui-1.8.2.custom.css');
|
||||
$this->add_linked_script('../js/jquery-ui-1.8.2.custom.min.js');
|
||||
//$this->add_linked_script("../js/ui.resizable.js");
|
||||
// $this->add_linked_script("../js/ui.tabs.js");
|
||||
$this->add_linked_script("../js/hovertip.js");
|
||||
// $this->add_linked_script("../js/jqModal.js");
|
||||
$this->add_linked_stylesheet("../css/light-grey.css");
|
||||
// $this->add_linked_stylesheet("../js/themes/light/light.tabs.css");
|
||||
//$this->add_linked_stylesheet("../css/jquery.tabs-ie.css", "lte IE 7");
|
||||
// $this->add_linked_stylesheet("../css/jqModal.css");
|
||||
$this->add_ready_script(' window.setTimeout(hovertipInit, 1);');
|
||||
}
|
||||
|
||||
public function small_p($sText)
|
||||
{
|
||||
$this->add("<p style=\"font-size:smaller\">$sText</p>\n");
|
||||
}
|
||||
|
||||
// By Rom, used by CSVImport and Advanced search
|
||||
public function MakeClassesSelect($sName, $sDefaultValue, $iWidthPx, $iActionCode = null)
|
||||
{
|
||||
// $aTopLevelClasses = array('bizService', 'bizContact', 'logInfra', 'bizDocument');
|
||||
// These are classes wich root class is cmdbAbstractObject !
|
||||
$this->add("<select id=\"select_$sName\" name=\"$sName\">");
|
||||
$aValidClasses = array();
|
||||
foreach(MetaModel::GetClasses('bizmodel') as $sClassName)
|
||||
{
|
||||
if (is_null($iActionCode) || UserRights::IsActionAllowed($sClassName, $iActionCode))
|
||||
{
|
||||
$sSelected = ($sClassName == $sDefaultValue) ? " SELECTED" : "";
|
||||
$sDescription = MetaModel::GetClassDescription($sClassName);
|
||||
$sDisplayName = MetaModel::GetName($sClassName);
|
||||
$aValidClasses[$sDisplayName] = "<option style=\"width: ".$iWidthPx." px;\" title=\"$sDescription\" value=\"$sClassName\"$sSelected>$sDisplayName</option>";
|
||||
}
|
||||
}
|
||||
ksort($aValidClasses);
|
||||
$this->add(implode("\n", $aValidClasses));
|
||||
|
||||
$this->add("</select>");
|
||||
}
|
||||
|
||||
// By Rom, used by Advanced search
|
||||
public function add_select($aChoices, $sName, $sDefaultValue, $iWidthPx)
|
||||
{
|
||||
$this->add("<select id=\"select_$sName\" name=\"$sName\">");
|
||||
foreach($aChoices as $sKey => $sValue)
|
||||
{
|
||||
$sSelected = ($sKey == $sDefaultValue) ? " SELECTED" : "";
|
||||
$this->add("<option style=\"width: ".$iWidthPx." px;\" value=\"".htmlspecialchars($sKey)."\"$sSelected>".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."</option>");
|
||||
}
|
||||
$this->add("</select>");
|
||||
}
|
||||
|
||||
public function add_ready_script($sScript)
|
||||
{
|
||||
$this->m_aReadyScripts[] = $sScript;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs (via some echo) the complete HTML page by assembling all its elements
|
||||
*/
|
||||
public function output()
|
||||
{
|
||||
if (count($this->m_aReadyScripts)>0)
|
||||
{
|
||||
$this->add_script("\$(document).ready(function() {\n".implode("\n", $this->m_aReadyScripts)."\n});");
|
||||
}
|
||||
parent::output();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
177
application/portalwebpage.class.inc.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?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 PortalWebPage
|
||||
*
|
||||
* @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");
|
||||
require_once(APPROOT."/application/applicationcontext.class.inc.php");
|
||||
require_once(APPROOT."/application/user.preferences.class.inc.php");
|
||||
/**
|
||||
* Web page with some associated CSS and scripts (jquery) for a fancier display
|
||||
* of the Portal web page
|
||||
*/
|
||||
class PortalWebPage extends NiceWebPage
|
||||
{
|
||||
/**
|
||||
* Portal menu
|
||||
*/
|
||||
protected $m_aMenuButtons;
|
||||
|
||||
public function __construct($sTitle, $sAlternateStyleSheet = '')
|
||||
{
|
||||
$this->m_aMenuButtons = array();
|
||||
parent::__construct($sTitle);
|
||||
$this->add_header("Content-type: text/html; charset=utf-8");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
$this->add_linked_stylesheet("../css/jquery.treeview.css");
|
||||
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
|
||||
if ($sAlternateStyleSheet != '')
|
||||
{
|
||||
$this->add_linked_stylesheet("../portal/$sAlternateStyleSheet/portal.css");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->add_linked_stylesheet("../portal/portal.css");
|
||||
}
|
||||
$this->add_linked_script('../js/jquery.layout.min.js');
|
||||
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
|
||||
$this->add_linked_script("../js/jquery.tablehover.js");
|
||||
$this->add_linked_script("../js/jquery.treeview.js");
|
||||
$this->add_linked_script("../js/jquery.autocomplete.js");
|
||||
$this->add_linked_script("../js/jquery.bgiframe.js");
|
||||
$this->add_linked_script("../js/jquery.positionBy.js");
|
||||
$this->add_linked_script("../js/jquery.popupmenu.js");
|
||||
$this->add_linked_script("../js/date.js");
|
||||
$this->add_linked_script("../js/jquery.tablesorter.min.js");
|
||||
$this->add_linked_script("../js/jquery.blockUI.js");
|
||||
$this->add_linked_script("../js/utils.js");
|
||||
$this->add_linked_script("../js/forms-json-utils.js");
|
||||
$this->add_linked_script("../js/swfobject.js");
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
try
|
||||
{
|
||||
//add new widget called TruncatedList to properly display truncated lists when they are sorted
|
||||
$.tablesorter.addWidget({
|
||||
// give the widget a id
|
||||
id: "truncatedList",
|
||||
// format is called when the on init and when a sorting has finished
|
||||
format: function(table)
|
||||
{
|
||||
// Check if there is a "truncated" line
|
||||
this.truncatedList = false;
|
||||
if ($("tr td.truncated",table).length > 0)
|
||||
{
|
||||
this.truncatedList = true;
|
||||
}
|
||||
if (this.truncatedList)
|
||||
{
|
||||
$("tr td",table).removeClass('truncated');
|
||||
$("tr:last td",table).addClass('truncated');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$.tablesorter.addWidget({
|
||||
// give the widget a id
|
||||
id: "myZebra",
|
||||
// format is called when the on init and when a sorting has finished
|
||||
format: function(table)
|
||||
{
|
||||
// Replace the 'red even' lines by 'red_even' since most browser do not support 2 classes selector in CSS, etc..
|
||||
$("tbody tr:even",table).addClass('even');
|
||||
$("tbody tr.red:even",table).removeClass('red').removeClass('even').addClass('red_even');
|
||||
$("tbody tr.orange:even",table).removeClass('orange').removeClass('even').addClass('orange_even');
|
||||
$("tbody tr.green:even",table).removeClass('green').removeClass('even').addClass('green_even');
|
||||
}
|
||||
});
|
||||
|
||||
$("table.listResults").tableHover(); // hover tables
|
||||
$(".listResults").tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
|
||||
$(".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 !
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
// Do something with the error !
|
||||
alert(err);
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
$this->add_script(
|
||||
<<<EOF
|
||||
function CheckSelection(sMessage)
|
||||
{
|
||||
var bResult = ($('input:checked').length > 0);
|
||||
if (!bResult)
|
||||
{
|
||||
alert(sMessage);
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
|
||||
function GoBack()
|
||||
{
|
||||
var form = $('#request_form');
|
||||
var step = $('input[name=step]');
|
||||
|
||||
form.unbind('submit'); // De-activate validation
|
||||
step.val(step.val() -2); // To go Back one step: next step is x, current step is x-1, previous step is x-2
|
||||
form.submit(); // Go
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a button to the portal's main menu
|
||||
*/
|
||||
public function AddMenuButton($sId, $sLabel, $sHyperlink)
|
||||
{
|
||||
$this->m_aMenuButtons[] = array('id' => $sId, 'label' => $sLabel, 'hyperlink' => $sHyperlink);
|
||||
}
|
||||
|
||||
public function output()
|
||||
{
|
||||
$sMenu = '';
|
||||
$this->AddMenuButton('logoff', 'Portal:Disconnect', '../pages/logoff.php?portal=1'); // 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="banner"><div id="logo"></div>'.$sMenu.'</div><div id="content">'.$this->s_content.'</div></div>';
|
||||
parent::output();
|
||||
}
|
||||
}
|
||||
?>
|
||||
31
application/startup.inc.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* File to include to initialize the datamodel in memory
|
||||
*
|
||||
* @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');
|
||||
|
||||
MetaModel::Startup(ITOP_CONFIG_FILE);
|
||||
|
||||
?>
|
||||
253
application/template.class.inc.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?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 DisplayTemplate
|
||||
*
|
||||
* @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');
|
||||
/**
|
||||
* This class manages the special template format used internally to build the iTop web pages
|
||||
*/
|
||||
class DisplayTemplate
|
||||
{
|
||||
protected $m_sTemplate;
|
||||
protected $m_aTags;
|
||||
static protected $iBlockCount = 0;
|
||||
|
||||
public function __construct($sTemplate)
|
||||
{
|
||||
$this->m_aTags = array('itopblock', 'itopcheck', 'itoptabs', 'itoptab', 'itoptoggle', 'itopstring');
|
||||
$this->m_sTemplate = $sTemplate;
|
||||
}
|
||||
|
||||
public function Render(WebPage $oPage, $aParams = array())
|
||||
{
|
||||
$this->m_sTemplate = MetaModel::ApplyParams($this->m_sTemplate, $aParams);
|
||||
$iStart = 0;
|
||||
$iEnd = strlen($this->m_sTemplate);
|
||||
$iCount = 0;
|
||||
$iBeforeTagPos = $iStart;
|
||||
$iAfterTagPos = $iStart;
|
||||
while($sTag = $this->GetNextTag($iStart, $iEnd))
|
||||
{
|
||||
$sContent = $this->GetTagContent($sTag, $iStart, $iEnd);
|
||||
$iAfterTagPos = $iEnd + strlen('</'.$sTag.'>');
|
||||
$sOuterTag = substr($this->m_sTemplate, $iStart, $iAfterTagPos - $iStart);
|
||||
$oPage->add(substr($this->m_sTemplate, $iBeforeTagPos, $iStart - $iBeforeTagPos));
|
||||
if ($sTag == DisplayBlock::TAG_BLOCK)
|
||||
{
|
||||
try
|
||||
{
|
||||
$oBlock = DisplayBlock::FromTemplate($sOuterTag);
|
||||
if (is_object($oBlock))
|
||||
{
|
||||
$oBlock->Display($oPage, 'block_'.self::$iBlockCount, $aParams);
|
||||
}
|
||||
}
|
||||
catch(OQLException $e)
|
||||
{
|
||||
$oPage->p('Error in template (please contact your administrator) - Invalid query<!--'.$sOuterTag.'-->');
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$oPage->p('Error in template (please contact your administrator)<!--'.$e->getMessage().'--><!--'.$sOuterTag.'-->');
|
||||
}
|
||||
|
||||
self::$iBlockCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAttributes = $this->GetTagAttributes($sTag, $iStart, $iEnd);
|
||||
//$oPage->p("Tag: $sTag - ($iStart, $iEnd)");
|
||||
$this->RenderTag($oPage, $sTag, $aAttributes, $sContent);
|
||||
|
||||
}
|
||||
$iAfterTagPos = $iEnd + strlen('</'.$sTag.'>');
|
||||
$iBeforeTagPos = $iAfterTagPos;
|
||||
$iStart = $iEnd;
|
||||
$iEnd = strlen($this->m_sTemplate);
|
||||
$iCount++;
|
||||
}
|
||||
$oPage->add(substr($this->m_sTemplate, $iAfterTagPos));
|
||||
}
|
||||
|
||||
public function GetNextTag(&$iStartPos, &$iEndPos)
|
||||
{
|
||||
$iChunkStartPos = $iStartPos;
|
||||
$sNextTag = null;
|
||||
$iStartPos = $iEndPos;
|
||||
foreach($this->m_aTags as $sTag)
|
||||
{
|
||||
// Search for the opening tag
|
||||
$iOpeningPos = stripos($this->m_sTemplate, '<'.$sTag.' ', $iChunkStartPos);
|
||||
if ($iOpeningPos === false)
|
||||
{
|
||||
$iOpeningPos = stripos($this->m_sTemplate, '<'.$sTag.'>', $iChunkStartPos);
|
||||
}
|
||||
if ($iOpeningPos !== false)
|
||||
{
|
||||
$iClosingPos = stripos($this->m_sTemplate, '</'.$sTag.'>', $iOpeningPos);
|
||||
}
|
||||
if ( ($iOpeningPos !== false) && ($iClosingPos !== false))
|
||||
{
|
||||
if ($iOpeningPos < $iStartPos)
|
||||
{
|
||||
// This is the next tag
|
||||
$iStartPos = $iOpeningPos;
|
||||
$iEndPos = $iClosingPos;
|
||||
$sNextTag = $sTag;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sNextTag;
|
||||
}
|
||||
|
||||
public function GetTagContent($sTag, $iStartPos, $iEndPos)
|
||||
{
|
||||
$sContent = "";
|
||||
$iContentStart = strpos($this->m_sTemplate, '>', $iStartPos); // Content of tag start immediatly after the first closing bracket
|
||||
if ($iContentStart !== false)
|
||||
{
|
||||
$sContent = substr($this->m_sTemplate, 1+$iContentStart, $iEndPos - $iContentStart - 1);
|
||||
}
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
public function GetTagAttributes($sTag, $iStartPos, $iEndPos)
|
||||
{
|
||||
$aAttr = array();
|
||||
$iAttrStart = strpos($this->m_sTemplate, ' ', $iStartPos); // Attributes start just after the first space
|
||||
$iAttrEnd = strpos($this->m_sTemplate, '>', $iStartPos); // Attributes end just before the first closing bracket
|
||||
if ( ($iAttrStart !== false) && ($iAttrEnd !== false) && ($iAttrEnd > $iAttrStart))
|
||||
{
|
||||
$sAttributes = substr($this->m_sTemplate, 1+$iAttrStart, $iAttrEnd - $iAttrStart - 1);
|
||||
$aAttributes = explode(' ', $sAttributes);
|
||||
foreach($aAttributes as $sAttr)
|
||||
{
|
||||
if ( preg_match('/(.+) *= *"(.+)"$/', $sAttr, $aMatches) )
|
||||
{
|
||||
$aAttr[strtolower($aMatches[1])] = $aMatches[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aAttr;
|
||||
}
|
||||
|
||||
protected function RenderTag($oPage, $sTag, $aAttributes, $sContent)
|
||||
{
|
||||
static $iTabContainerCount = 0;
|
||||
switch($sTag)
|
||||
{
|
||||
case 'itoptabs':
|
||||
$oPage->AddTabContainer('Tabs_'.$iTabContainerCount);
|
||||
$oPage->SetCurrentTabContainer('Tabs_'.$iTabContainerCount);
|
||||
$iTabContainerCount++;
|
||||
//$oPage->p('Content:<pre>'.htmlentities($sContent).'</pre>');
|
||||
$oTemplate = new DisplayTemplate($sContent);
|
||||
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
|
||||
$oPage->SetCurrentTabContainer('');
|
||||
break;
|
||||
|
||||
case 'itopcheck':
|
||||
$sClassName = $aAttributes['class'];
|
||||
if (MetaModel::IsValidClass($sClassName) && UserRights::IsActionAllowed($sClassName, UR_ACTION_READ))
|
||||
{
|
||||
$oTemplate = new DisplayTemplate($sContent);
|
||||
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave a trace for those who'd like to understand why nothing is displayed
|
||||
$oPage->add("<!-- class $sClassName does not exist, skipping some part of the template -->\n");
|
||||
}
|
||||
break;
|
||||
|
||||
case 'itoptab':
|
||||
$oPage->SetCurrentTab(Dict::S(str_replace('_', ' ', $aAttributes['name'])));
|
||||
$oTemplate = new DisplayTemplate($sContent);
|
||||
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
|
||||
//$oPage->p('iTop Tab Content:<pre>'.htmlentities($sContent).'</pre>');
|
||||
$oPage->SetCurrentTab('');
|
||||
break;
|
||||
|
||||
case 'itoptoggle':
|
||||
$sName = isset($aAttributes['name']) ? $aAttributes['name'] : 'Tagada';
|
||||
$bOpen = isset($aAttributes['open']) ? $aAttributes['open'] : true;
|
||||
$oPage->StartCollapsibleSection(Dict::S($sName), $bOpen);
|
||||
$oTemplate = new DisplayTemplate($sContent);
|
||||
$oTemplate->Render($oPage, array()); // no params to apply, they have already been applied
|
||||
//$oPage->p('iTop Tab Content:<pre>'.htmlentities($sContent).'</pre>');
|
||||
$oPage->EndCollapsibleSection();
|
||||
break;
|
||||
|
||||
case 'itopstring':
|
||||
$oPage->add(Dict::S($sContent));
|
||||
break;
|
||||
|
||||
case 'itopblock': // No longer used, handled by DisplayBlock::FromTemplate see above
|
||||
$oPage->add("<!-- Application Error: should be handled by DisplayBlock::FromTemplate -->");
|
||||
break;
|
||||
|
||||
default:
|
||||
// Unknown tag, just ignore it or now -- output an HTML comment
|
||||
$oPage->add("<!-- unsupported tag: $sTag -->");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unit test
|
||||
*/
|
||||
static public function UnitTest()
|
||||
{
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
require_once(APPROOT."/application/itopwebpage.class.inc.php");
|
||||
|
||||
$sTemplate = '<div class="page_header">
|
||||
<div class="actions_details"><a href="#"><span>Actions</span></a></div>
|
||||
<h1>$class$: <span class="hilite">$name$</span></h1>
|
||||
<itopblock blockclass="HistoryBlock" type="toggle" encoding="text/oql">SELECT CMDBChangeOp WHERE objkey = $id$ AND objclass = \'$class$\'</itopblock>
|
||||
</div>
|
||||
<img src="../../images/connect_to_network.png" style="margin-top:-10px; margin-right:10px; float:right">
|
||||
<itopblock blockclass="DisplayBlock" asynchronous="false" type="bare_details" encoding="text/oql">SELECT NetworkDevice AS d WHERE d.id = $id$</itopblock>
|
||||
<itoptabs>
|
||||
<itoptab name="Interfaces">
|
||||
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT Interface AS i WHERE i.device_id = $id$</itopblock>
|
||||
</itoptab>
|
||||
<itoptab name="Contacts">
|
||||
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT Contact AS c JOIN lnkContactToCI AS l ON l.contact_id = c.id WHERE l.ci_id = $id$</itopblock>
|
||||
</itoptab>
|
||||
<itoptab name="Documents">
|
||||
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT Document AS d JOIN lnkDocumentToCI as l ON l.document_id = d.id WHERE l.ci_id = $id$)</itopblock>
|
||||
</itoptab>
|
||||
</itoptabs>';
|
||||
|
||||
$oPage = new iTopWebPage('Unit Test');
|
||||
//$oPage->add("Template content: <pre>".htmlentities($sTemplate)."</pre>\n");
|
||||
$oTemplate = new DisplayTemplate($sTemplate);
|
||||
$oTemplate->Render($oPage, array('class'=>'Network device','pkey'=> 271, 'name' => 'deliversw01.mecanorama.fr', 'org_id' => 3));
|
||||
$oPage->output();
|
||||
}
|
||||
}
|
||||
|
||||
//DisplayTemplate::UnitTest();
|
||||
|
||||
?>
|
||||
12
application/templates/audit_category.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<div class="page_header">
|
||||
<itopblock blockclass="MenuBlock" type="popup" encoding="text/oql" label="Actions">SELECT $class$ WHERE id = $id$</itopblock>
|
||||
<h1>$class$: <span class="hilite">$name$</span></h1>
|
||||
<itopblock blockclass="HistoryBlock" type="toggle" encoding="text/oql">SELECT CMDBChangeOp WHERE objkey = $id$ AND objclass = '$class$'</itopblock>
|
||||
</div>
|
||||
<img src="../../images/clean.png" style="margin-top:-20px; margin-right:10px; float:right">
|
||||
<itopblock blockclass="DisplayBlock" asynchronous="false" type="bare_details" encoding="text/oql">SELECT $class$ WHERE id = $id$</itopblock>
|
||||
<itoptabs>
|
||||
<itoptab name="Rules">
|
||||
<itopblock blockclass="DisplayBlock" type="list" encoding="text/oql">SELECT AuditRule WHERE category_id = $id$</itopblock>
|
||||
</itoptab>
|
||||
</itoptabs>
|
||||
20
application/templates/notifications_menu.html
Normal 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> </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>
|
||||
46
application/templates/welcome_popup.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<div style="width:100%;background: url(../images/welcome.jpg) top left no-repeat;">
|
||||
<style>
|
||||
.dashboard {
|
||||
vertical-align:top;
|
||||
width:50%;
|
||||
border:0px solid #000;
|
||||
background-color:#F9F9F1;
|
||||
padding:10px;
|
||||
text-align:left;
|
||||
font-size:10pt;
|
||||
}
|
||||
.dashboard2 {
|
||||
vertical-align:top;
|
||||
width:50%;
|
||||
border:0px solid #000;
|
||||
-moz-border-radius:10px;
|
||||
padding:5px;
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
td.dashboard li {
|
||||
margin-top: 5px;
|
||||
display: list-item;
|
||||
}
|
||||
td div.display_block {
|
||||
padding:0;
|
||||
}
|
||||
tr td.dashboard2, tr td.dashboard2 ul {
|
||||
font-size:10pt;
|
||||
}
|
||||
</style>
|
||||
<p></p>
|
||||
<p></p>
|
||||
<p style="text-align:left; font-size:32px;padding-left:400px;padding-top:40px;margin-bottom:30px;margin-top:0;color:#FFFFFF;"><itopstring>UI:WelcomeMenu:Title</itopstring></p>
|
||||
<p></p>
|
||||
<table border="0" style="padding:10px;border-spacing: 10px;width:100%">
|
||||
<tr>
|
||||
<td class="dashboard2">
|
||||
<itopstring>UI:WelcomeMenu:LeftBlock</itopstring>
|
||||
</td>
|
||||
<td class="dashboard2">
|
||||
<itopstring>UI:WelcomeMenu:RightBlock</itopstring>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
97
application/transaction.class.inc.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @package iTop
|
||||
*/
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
if (!isset($_SESSION['transactions']))
|
||||
{
|
||||
$_SESSION['transactions'] = array();
|
||||
}
|
||||
// Strictly speaking, the two lines below should be grouped together
|
||||
// by a critical section
|
||||
// sem_acquire($rSemIdentified);
|
||||
$id = 1 + count($_SESSION['transactions']);
|
||||
$_SESSION['transactions'][$id] = true;
|
||||
// sem_release($rSemIdentified);
|
||||
|
||||
return sprintf("%d", $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)
|
||||
{
|
||||
$bResult = false;
|
||||
if (isset($_SESSION['transactions']))
|
||||
{
|
||||
// Strictly speaking, the eight lines below should be grouped together
|
||||
// inside the same critical section as above
|
||||
// sem_acquire($rSemIdentified);
|
||||
if (isset($_SESSION['transactions'][$id]))
|
||||
{
|
||||
$bResult = true;
|
||||
if ($bRemoveTransaction)
|
||||
{
|
||||
unset($_SESSION['transactions'][$id]);
|
||||
}
|
||||
}
|
||||
// sem_release($rSemIdentified);
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
if (isset($_SESSION['transactions']))
|
||||
{
|
||||
// Strictly speaking, the three lines below should be grouped together
|
||||
// inside the same critical section as above
|
||||
// sem_acquire($rSemIdentified);
|
||||
if (isset($_SESSION['transactions'][$id]))
|
||||
{
|
||||
unset($_SESSION['transactions'][$id]);
|
||||
}
|
||||
// sem_release($rSemIdentified);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
271
application/ui.extkeywidget.class.inc.php
Normal file
@@ -0,0 +1,271 @@
|
||||
<?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 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)------------+ | |
|
||||
* | | | +--------+---+ | | |
|
||||
* | | | | Class | V | | | |
|
||||
* | | | +--------+---+ | | |
|
||||
* | | | | | |
|
||||
* | | | S e a r c h F o r m | | |
|
||||
* | | | +--------+ | | |
|
||||
* | | | | Search | | | |
|
||||
* | | | +--------+ | | |
|
||||
* | | +----------------------------------------+ | |
|
||||
* | +--------------+-dh_<id>-+--------------------+ |
|
||||
* | \ Search / |
|
||||
* | +------+ |
|
||||
* | +--- fr_<id> (form)--------------------------+ |
|
||||
* | | +------------ dr_<id> (div)--------------+ | |
|
||||
* | | | | | |
|
||||
* | | | S e a r c h R e s u l t s | | |
|
||||
* | | | | | |
|
||||
* | | +----------------------------------------+ | |
|
||||
* | | +--------+ +-----+ | |
|
||||
* | | | Cancel | | Add | | |
|
||||
* | | +--------+ +-----+ | |
|
||||
* | +--------------------------------------------+ |
|
||||
* +------------------------------------------------+
|
||||
* @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
|
||||
{
|
||||
protected static $iWidgetIndex = 0;
|
||||
protected $sAttCode;
|
||||
protected $sNameSuffix;
|
||||
protected $iId;
|
||||
protected $sTitle;
|
||||
|
||||
public function __construct($sAttCode, $sClass, $sTitle, $aAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
|
||||
{
|
||||
self::$iWidgetIndex++;
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->sClass = $sClass;
|
||||
$this->oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$this->sNameSuffix = $sNameSuffix;
|
||||
$this->iId = $iInputId;
|
||||
$this->aAllowedValues = $aAllowedValues;
|
||||
$this->value = $value;
|
||||
$this->sFieldPrefix = $sFieldPrefix;
|
||||
$this->sTargetClass = $this->oAttDef->GetTargetClass();
|
||||
$this->sTitle = $sTitle;
|
||||
$this->sFormPrefix = $sFormPrefix;
|
||||
$this->bMandatory = $bMandatory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML fragment corresponding to the linkset editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, $aArgs = array())
|
||||
{
|
||||
$bCreate = (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $this->oAttDef->AllowTargetCreation());
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
|
||||
$sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap
|
||||
if (count($this->aAllowedValues) < $this->oAttDef->GetMaximumComboLength())
|
||||
{
|
||||
// Few choices, use a normal 'select'
|
||||
$sSelectMode = 'true';
|
||||
|
||||
$sHelpText = $this->oAttDef->GetHelpOnEdition();
|
||||
|
||||
// In case there are no valid values, the select will be empty, thus blocking the user from validating the form
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"attr_{$this->sFieldPrefix}{$this->sAttCode}{$this->sNameSuffix}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
|
||||
foreach($this->aAllowedValues as $key => $display_value)
|
||||
{
|
||||
if ((count($this->aAllowedValues) == 1) && ($this->bMandatory == 'true') )
|
||||
{
|
||||
// When there is only once choice, select it by default
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = ($this->value == $key) ? ' selected' : '';
|
||||
}
|
||||
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
|
||||
}
|
||||
$sHTMLValue .= "</select>\n";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sClass}', '{$this->sAttCode}', '{$this->sNameSuffix}', $sSelectMode, oWizardHelper{$this->sFormPrefix});
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
|
||||
EOF
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Too many choices, use an autocomplete
|
||||
$sSelectMode = 'false';
|
||||
|
||||
if ($this->oAttDef->IsNull($this->value)) // Null values are displayed as ''
|
||||
{
|
||||
$sDisplayValue = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDisplayValue = $this->GetObjectName($this->value);
|
||||
}
|
||||
$sFormPrefix = $this->sFormPrefix;
|
||||
$iMinChars = $this->oAttDef->GetMinAutoCompleteChars();
|
||||
$iFieldSize = $this->oAttDef->GetMaxSize();
|
||||
|
||||
// the input for the auto-complete
|
||||
$sHTMLValue = "<input count=\"".count($this->aAllowedValues)."\" type=\"text\" id=\"label_$this->iId\" size=\"30\" maxlength=\"$iFieldSize\" value=\"$sDisplayValue\"/> ";
|
||||
$sHTMLValue .= "<a class=\"no-arrow\" href=\"javascript:oACWidget_{$this->iId}.Search();\"><img style=\"border:0;vertical-align:middle;\" src=\"../images/mini_search.gif\" /></a> ";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"attr_{$this->sFieldPrefix}{$this->sAttCode}{$this->sNameSuffix}\" value=\"$this->value\" />\n";
|
||||
|
||||
// Scripts to start the autocomplete and bind some events to it
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sClass}', '{$this->sAttCode}', '{$this->sNameSuffix}', $sSelectMode, oWizardHelper{$this->sFormPrefix});
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#label_$this->iId').autocomplete('./ajax.render.php', { scroll:true, minChars:{$iMinChars}, formatItem:formatItem, autoFill:false, matchContains:true, keyHolder:'#{$this->iId}', extraParams:{operation:'autocomplete', sclass:'{$this->sClass}',attCode:'{$this->sAttCode}'}});
|
||||
$('#label_$this->iId').blur(function() { $(this).search(); } );
|
||||
$('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } );
|
||||
$('#ac_dlg_$this->iId').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '{$this->sTitle}', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
|
||||
|
||||
EOF
|
||||
);
|
||||
$oPage->add_at_the_end($this->GetSearchDialog($oPage)); // To prevent adding forms inside the main form
|
||||
|
||||
}
|
||||
if ($bCreate)
|
||||
{
|
||||
$sHTMLValue .= "<a class=\"no-arrow\" href=\"javascript:oACWidget_{$this->iId}.CreateObject();\"><img style=\"border:0;vertical-align:middle;\" src=\"../images/mini_add.gif\" /></a> ";
|
||||
$oPage->add_at_the_end('<div id="ajax_'.$this->iId.'"></div>');
|
||||
}
|
||||
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span>";
|
||||
$sHTMLValue .= "</span>"; // end of no wrap
|
||||
return $sHTMLValue;
|
||||
}
|
||||
|
||||
protected function GetSearchDialog(WebPage $oPage)
|
||||
{
|
||||
$sHTML = '<div id="ac_dlg_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dc_'.$this->iId.'">';
|
||||
|
||||
$oFilter = new DBObjectSearch($this->sTargetClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$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";
|
||||
$sHTML .= "</div>\n";
|
||||
$sHTML .= "<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#ac_dlg_{$this->iId}').dialog('close');\"> ";
|
||||
$sHTML .= "<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoOk();\">";
|
||||
$sHTML .= "</form>\n";
|
||||
$sHTML .= '</div></div></div>';
|
||||
|
||||
$oPage->add_ready_script("$('#fs_{$this->iId}').bind('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);");
|
||||
$oPage->add_ready_script("$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);");
|
||||
|
||||
return $sHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for objects to be selected
|
||||
* @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
|
||||
*/
|
||||
public function SearchObjectsToSelect(WebPage $oP, $sTargetClass = '')
|
||||
{
|
||||
if ($sTargetClass != '')
|
||||
{
|
||||
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No remote class specified use the one defined in the linkedset
|
||||
$oFilter = new DBObjectSearch($this->sTargetClass);
|
||||
}
|
||||
$oFilter->AddCondition('id', array_keys($this->aAllowedValues), 'IN');
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, $this->iId, array('menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'display_limit' => false)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the display name of the selected object, to fill back the autocomplete
|
||||
*/
|
||||
public function GetObjectName($iObjId)
|
||||
{
|
||||
$oObj = MetaModel::GetObject($this->sTargetClass, $iObjId);
|
||||
return $oObj->GetName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form to create a new object of the 'target' class
|
||||
*/
|
||||
public function GetObjectCreationForm(WebPage $oPage)
|
||||
{
|
||||
$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)." ".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sTargetClass))."</h1>\n");
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, null, 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: '$this->sTitle'});\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);");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form to create a new object of the 'target' class
|
||||
*/
|
||||
public function DoCreateObject($oPage)
|
||||
{
|
||||
$oObj = MetaModel::NewObject($this->sTargetClass);
|
||||
$oObj->UpdateObject($this->sFormPrefix.$this->iId);
|
||||
$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());
|
||||
|
||||
//return array('name' => 'test', 'id' => '42');
|
||||
}
|
||||
}
|
||||
?>
|
||||
86
application/ui.htmleditorwidget.class.inc.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?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 UIHTMLEditorWidget
|
||||
* UI wdiget for displaying and editing one-way encrypted passwords
|
||||
*
|
||||
* @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_sAttCode;
|
||||
protected $m_sNameSuffix;
|
||||
protected $m_sFieldPrefix;
|
||||
protected $m_sHelpText;
|
||||
protected $m_sValidationField;
|
||||
protected $m_sValue;
|
||||
protected $m_sMandatory;
|
||||
|
||||
public function __construct($iInputId, $sAttCode, $sNameSuffix, $sFieldPrefix, $sHelpText, $sValidationField, $sValue, $sMandatory)
|
||||
{
|
||||
$this->m_iId = $iInputId;
|
||||
$this->m_sAttCode = $sAttCode;
|
||||
$this->m_sNameSuffix = $sNameSuffix;
|
||||
$this->m_sHelpText = $sHelpText;
|
||||
$this->m_sValidationField = $sValidationField;
|
||||
$this->m_sValue = $sValue;
|
||||
$this->m_sMandatory = $sMandatory;
|
||||
$this->m_sFieldPrefix = $sFieldPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML fragment corresponding to the HTML editor widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, $aArgs = array())
|
||||
{
|
||||
$iId = $this->m_iId;
|
||||
$sCode = $this->m_sAttCode.$this->m_sNameSuffix;
|
||||
$sValue = $this->m_sValue;
|
||||
$sHelpText = $this->m_sHelpText;
|
||||
$sValidationField = $this->m_sValidationField;
|
||||
|
||||
$sHtmlValue = "<table><tr><td><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"14\" cols=\"110\" 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()
|
||||
$sLanguage = strtolower(trim(UserRights::GetUserLanguage()));
|
||||
$oPage->add_ready_script("$('#$iId').ckeditor(function() { /* callback code */ }, { language : '$sLanguage' , contentsLanguage : '$sLanguage' });"); // Transform $iId into a CKEdit
|
||||
|
||||
// Please read...
|
||||
// ValidateCKEditField triggers a timer... calling itself indefinitely
|
||||
// This design was the quickest way to achieve the field validation (only checking if the field is blank)
|
||||
// because the ckeditor does not fire events like "change" or "keyup", etc.
|
||||
// See http://dev.ckeditor.com/ticket/900 => won't fix
|
||||
// The most relevant solution would be to implement a plugin to CKEdit, and handle the internal events like: setData, insertHtml, insertElement, loadSnapshot, key, afterUndo, afterRedo
|
||||
|
||||
// Could also be bound to 'instanceReady.ckeditor'
|
||||
$oPage->add_ready_script("$('#$iId').bind('validate', function(evt, sFormId) { return ValidateCKEditField('$iId', '', {$this->m_sMandatory}, sFormId, '') } );");
|
||||
|
||||
return $sHtmlValue;
|
||||
}
|
||||
}
|
||||
?>
|
||||
443
application/ui.linkswidget.class.inc.php
Normal file
@@ -0,0 +1,443 @@
|
||||
<?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 UILinksWidget
|
||||
*
|
||||
* @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 UILinksWidget
|
||||
{
|
||||
protected $m_sClass;
|
||||
protected $m_sAttCode;
|
||||
protected $m_sNameSuffix;
|
||||
protected $m_iInputId;
|
||||
protected $m_aAttributes;
|
||||
protected $m_sExtKeyToRemote;
|
||||
protected $m_sLinkedClass;
|
||||
protected $m_sRemoteClass;
|
||||
protected $m_bDuplicatesAllowed;
|
||||
|
||||
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sAttCode = $sAttCode;
|
||||
$this->m_sNameSuffix = $sNameSuffix;
|
||||
$this->m_iInputId = $iInputId;
|
||||
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
|
||||
$this->m_aEditableFields = array();
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
|
||||
$this->m_sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
|
||||
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
|
||||
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass);
|
||||
$sDefaultState = MetaModel::GetDefaultState($this->m_sClass);
|
||||
|
||||
$this->m_aEditableFields = array();
|
||||
$this->m_aTableConfig = array();
|
||||
$this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+'));
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs($this->m_sLinkedClass) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
if ($sStateAttCode == $sAttCode)
|
||||
{
|
||||
// State attribute is always hidden from the UI
|
||||
}
|
||||
else if (!$oAttDef->IsExternalField() && ($sAttCode != $sExtKeyToMe) && ($sAttCode != $this->m_sExtKeyToRemote) && ($sAttCode != 'finalclass'))
|
||||
{
|
||||
$iFlags = MetaModel::GetAttributeFlags($this->m_sLinkedClass, $sDefaultState, $sAttCode);
|
||||
if ( !($iFlags & OPT_ATT_HIDDEN) && !($iFlags & OPT_ATT_READONLY) )
|
||||
{
|
||||
$this->m_aEditableFields[] = $sAttCode;
|
||||
$this->m_aTableConfig[$sAttCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->m_aTableConfig['static::key'] = array( 'label' => MetaModel::GetName($this->m_sRemoteClass), 'description' => MetaModel::GetClassDescription($this->m_sRemoteClass));
|
||||
foreach(MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode)
|
||||
{
|
||||
// TO DO: check the state of the attribute: hidden or visible ?
|
||||
if ($sFieldCode != 'finalclass')
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sRemoteClass, $sFieldCode);
|
||||
$this->m_aTableConfig['static::'.$sFieldCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A one-row form for editing a link record
|
||||
* @param WebPage $oP Web page used for the ouput
|
||||
* @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 Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment of the one-row form
|
||||
*/
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array() )
|
||||
{
|
||||
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
|
||||
$aRow = array();
|
||||
if(is_object($linkObjOrId))
|
||||
{
|
||||
$key = $linkObjOrId->GetKey();
|
||||
$sPrefix .= "[$key][";
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$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 = str_replace(array('[',']','-'), '_', $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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// form for creating a new record
|
||||
$sPrefix .= "[$linkObjOrId][";
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$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.'['.$linkObjOrId.']';
|
||||
$sSafeId = str_replace(array('[',']','-'), '_', $sFieldId);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, $sSafeId /* id */, $sNameSuffix, 0, $aArgs);
|
||||
}
|
||||
}
|
||||
|
||||
$aRow['static::key'] = $oLinkedObj->GetHyperLink();
|
||||
foreach(MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode)
|
||||
{
|
||||
$aRow['static::'.$sFieldCode] = $oLinkedObj->GetAsHTML($sFieldCode);
|
||||
}
|
||||
return $aRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display one row of the whole form
|
||||
* @return none
|
||||
*/
|
||||
protected function DisplayFormRow(WebPage $oP, $aConfig, $aRow, $iRowId)
|
||||
{
|
||||
$sHtml = '';
|
||||
$sHtml .= "<tr id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_row_$iRowId\">\n";
|
||||
foreach($aConfig as $sName=>$void)
|
||||
{
|
||||
$sHtml .= "<td>".$aRow[$sName]."</td>\n";
|
||||
}
|
||||
$sHtml .= "</tr>\n";
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the table with the form for editing all the links at once
|
||||
* @param WebPage $oP The web page used for the output
|
||||
* @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 = '';
|
||||
$sHtml .= "<table class=\"listResults\">\n";
|
||||
// Header
|
||||
$sHtml .= "<thead>\n";
|
||||
$sHtml .= "<tr>\n";
|
||||
foreach($aConfig as $sName=>$aDef)
|
||||
{
|
||||
$sHtml .= "<th title=\"".$aDef['description']."\">".$aDef['label']."</th>\n";
|
||||
}
|
||||
$sHtml .= "</tr>\n";
|
||||
$sHtml .= "</thead>\n";
|
||||
|
||||
// Content
|
||||
$sHtml .= "</tbody>\n";
|
||||
if (count($aData) == 0)
|
||||
{
|
||||
$sHtml .= "<tr 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>";
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($aData as $iRowId => $aRow)
|
||||
{
|
||||
$sHtml .= $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
|
||||
}
|
||||
}
|
||||
$sHtml .= "</tbody>\n";
|
||||
|
||||
// Footer
|
||||
$sHtml .= "</table>\n";
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the HTML fragment corresponding to the linkset editing widget
|
||||
* @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
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array())
|
||||
{
|
||||
$sHtmlValue = '';
|
||||
$sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
|
||||
$sHtmlValue .= "<div id=\"linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
|
||||
$oValue->Rewind();
|
||||
$aForm = array();
|
||||
while($oCurrentLink = $oValue->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$key = $oCurrentLink->GetKey();
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote));
|
||||
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs);
|
||||
}
|
||||
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
|
||||
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
|
||||
$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);
|
||||
oWidget{$this->m_iInputId}.Init();
|
||||
EOF
|
||||
);
|
||||
$sHtmlValue .= "<span style=\"float:left;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"oWidget{$this->m_iInputId}.RemoveSelected();\" >";
|
||||
$sHtmlValue .= " <input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sRemoteClass))."\" onClick=\"oWidget{$this->m_iInputId}.AddObjects();\"></span>\n";
|
||||
$sHtmlValue .= "<span style=\"clear:both;\"><p> </p></span>\n";
|
||||
$sHtmlValue .= "</div>\n";
|
||||
$oPage->add_at_the_end($this->GetObjectPickerDialog($oPage)); // To prevent adding forms inside the main form
|
||||
return $sHtmlValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* This static function is called by the Ajax Page when there is a need to fill an autocomplete combo
|
||||
* @param $oPage WebPage The ajax page used for the output (sent back to the browser)
|
||||
* @param $sClass string The name of the class of the current object being edited
|
||||
* @param $sAttCode string The name of the attribute being edited
|
||||
* @param $sName string The partial name that was typed by the user
|
||||
* @param $iMaxCount integer The maximum number of items to return
|
||||
* @return void
|
||||
*/
|
||||
static public function Autocomplete(WebPage $oPage, $sClass, $sAttCode, $sName, $iMaxCount)
|
||||
{
|
||||
// #@# todo - add context information, otherwise any value will be authorized for external keys
|
||||
$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array() /* $aArgs */, $sName);
|
||||
if ($aAllowedValues != null)
|
||||
{
|
||||
$iCount = $iMaxCount;
|
||||
foreach($aAllowedValues as $key => $value)
|
||||
{
|
||||
$oPage->add($value."|".$key."\n");
|
||||
$iCount--;
|
||||
if ($iCount == 0) break;
|
||||
}
|
||||
}
|
||||
else // No limitation to the allowed values
|
||||
{
|
||||
// Search for all the object of the linked class
|
||||
$oAttDef = $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$sSearchClass = self::GetTargetClass($sClass, $sAttCode);
|
||||
$oFilter = new DBObjectSearch($sSearchClass);
|
||||
$sSearchAttCode = MetaModel::GetNameAttributeCode($sSearchClass);
|
||||
$oFilter->AddCondition($sSearchAttCode, $sName, 'Begins with');
|
||||
$oSet = new CMDBObjectSet($oFilter, array($sSearchAttCode => true));
|
||||
$iCount = 0;
|
||||
while( ($iCount < $iMaxCount) && ($oObj = $oSet->fetch()) )
|
||||
{
|
||||
$oPage->add($oObj->GetName()."|".$oObj->GetKey()."\n");
|
||||
$iCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This static function is called by the Ajax Page display a set of objects being linked
|
||||
* to the object being created
|
||||
* @param $oPage WebPage The ajax page used for the put^put (sent back to the browser
|
||||
* @param $sClass string The name of the 'linking class' which is the class of the objects to display
|
||||
* @param $sSet JSON serialized set of objects
|
||||
* @param $sExtKeyToMe Name of the attribute in sClass that is pointing to a given object
|
||||
* @param $iObjectId The id of the object $sExtKeyToMe is pointing to
|
||||
* @return void
|
||||
*/
|
||||
static public function RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe, $sExtKeyToRemote, $iObjectId)
|
||||
{
|
||||
$aSet = json_decode($sJSONSet, true); // true means hash array instead of object
|
||||
$oSet = CMDBObjectSet::FromScratch($sClass);
|
||||
foreach($aSet as $aObject)
|
||||
{
|
||||
$oObj = MetaModel::NewObject($sClass);
|
||||
foreach($aObject as $sAttCode => $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->IsExternalKey() && ($value != 0))
|
||||
{
|
||||
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value); // @@ optimization, don't do & query per object in the set !
|
||||
$oObj->Set($sAttCode, $oTargetObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj->Set($sAttCode, $value);
|
||||
}
|
||||
|
||||
}
|
||||
$oSet->AddObject($oObj);
|
||||
}
|
||||
$aExtraParams = array();
|
||||
$aExtraParams['link_attr'] = $sExtKeyToMe;
|
||||
$aExtraParams['object_id'] = $iObjectId;
|
||||
$aExtraParams['target_attr'] = $sExtKeyToRemote;
|
||||
$aExtraParams['menu'] = false;
|
||||
$aExtraParams['select'] = false;
|
||||
$aExtraParams['view_link'] = false;
|
||||
|
||||
cmdbAbstractObject::DisplaySet($oPage, $oSet, $aExtraParams);
|
||||
}
|
||||
|
||||
|
||||
protected static function GetTargetClass($sClass, $sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
switch(get_class($oAttDef))
|
||||
{
|
||||
case 'AttributeLinkedSetIndirect':
|
||||
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
|
||||
$sTargetClass = $oLinkingAttDef->GetTargetClass();
|
||||
break;
|
||||
|
||||
case 'AttributeLinkedSet':
|
||||
$sTargetClass = $sLinkedClass;
|
||||
break;
|
||||
}
|
||||
|
||||
return $sTargetClass;
|
||||
}
|
||||
|
||||
protected function GetObjectPickerDialog($oPage)
|
||||
{
|
||||
$sHtml = "<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\">";
|
||||
$sHtml .= "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$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=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\"> <input type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
|
||||
$sHtml .= "</div>\n";
|
||||
$sHtml .= "</form>\n";
|
||||
$sHtml .= "</div>\n";
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oWidget{$this->m_iInputId}.UpdateSizes });");
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('option', {title:'".addslashes(Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName($this->m_sClass)))."'});");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix} form').bind('submit.uilinksWizard', oWidget{$this->m_iInputId}.SearchObjectsToAdd);");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}').resize(oWidget{$this->m_iInputId}.UpdateSizes);");
|
||||
return $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 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())
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
|
||||
{
|
||||
// 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');
|
||||
}
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true, 'display_limit' => false)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
public function DoAddObjects(WebPage $oP, $aLinkedObjectIds = array())
|
||||
{
|
||||
$aTable = array();
|
||||
foreach($aLinkedObjectIds as $iObjectId)
|
||||
{
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId);
|
||||
if (is_object($oLinkedObj))
|
||||
{
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids
|
||||
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oP->p(Dict::Format('UI:Error:Object_Class_Id_NotFound', $this->m_sLinkedClass, $iObjectId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
70
application/ui.passwordwidget.class.inc.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?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 UIPasswordWidget
|
||||
* UI wdiget for displaying and editing one-way encrypted passwords
|
||||
*
|
||||
* @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 UIPasswordWidget
|
||||
{
|
||||
protected static $iWidgetIndex = 0;
|
||||
protected $sAttCode;
|
||||
protected $sNameSuffix;
|
||||
protected $iId;
|
||||
|
||||
public function __construct($sAttCode, $iInputId, $sNameSuffix = '')
|
||||
{
|
||||
self::$iWidgetIndex++;
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->sNameSuffix = $sNameSuffix;
|
||||
$this->iId = $iInputId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the HTML fragment corresponding to the linkset editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, $aArgs = array())
|
||||
{
|
||||
$sCode = $this->sAttCode.$this->sNameSuffix;
|
||||
$iWidgetIndex = self::$iWidgetIndex;
|
||||
$sPasswordValue = utils::ReadPostedParam("attr_$sCode", '*****');
|
||||
$sConfirmPasswordValue = utils::ReadPostedParam("attr_{$sCode}_confirmed", '*****');
|
||||
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
|
||||
$sHtmlValue = '';
|
||||
$sHtmlValue = '<input type="password" maxlength="255" name="attr_'.$sCode.'" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/> <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.'_confirmed"/> '.Dict::S('UI:PasswordConfirm').' <input 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.'"/>';
|
||||
|
||||
$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
|
||||
$oPage->add_ready_script("$('#{$this->iId}_confirm').bind('keyup change', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
|
||||
|
||||
return $sHtmlValue;
|
||||
}
|
||||
}
|
||||
?>
|
||||
424
application/uilinkswizard.class.inc.php
Normal file
@@ -0,0 +1,424 @@
|
||||
<?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->GetName());
|
||||
$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 type=\"text/javascript\">\n");
|
||||
$oP->add(
|
||||
<<<EOF
|
||||
function OnSelectChange()
|
||||
{
|
||||
var nbChecked = $('.selection:checked').length;
|
||||
if (nbChecked > 0)
|
||||
{
|
||||
$('#btnRemove').attr('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( '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( '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( '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("</script>\n");
|
||||
$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;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"RemoveSelected();\" >");
|
||||
$oP->add(" <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(" <input id=\"btnOk\" type=\"submit\" value=\"".Dict::S('UI:Button:Ok')."\"></span>\n");
|
||||
$oP->add("<span style=\"clear:both;\"><p> </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');\"> <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, 'display_limit' => false)); // 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
|
||||
}
|
||||
}
|
||||
?>
|
||||
332
application/uiwizard.class.inc.php
Normal file
@@ -0,0 +1,332 @@
|
||||
<?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 UIWizard
|
||||
*
|
||||
* @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
|
||||
{
|
||||
protected $m_oPage;
|
||||
protected $m_sClass;
|
||||
protected $m_sTargetState;
|
||||
protected $m_aWizardSteps;
|
||||
|
||||
public function __construct($oPage, $sClass, $sTargetState = '')
|
||||
{
|
||||
$this->m_oPage = $oPage;
|
||||
$this->m_sClass = $sClass;
|
||||
if (empty($sTargetState))
|
||||
{
|
||||
$sTargetState = MetaModel::GetDefaultState($sClass);
|
||||
}
|
||||
$this->m_sTargetState = $sTargetState;
|
||||
$this->m_aWizardSteps = $this->ComputeWizardStructure();
|
||||
}
|
||||
|
||||
public function GetObjectClass() { return $this->m_sClass; }
|
||||
public function GetTargetState() { return $this->m_sTargetState; }
|
||||
public function GetWizardStructure() { return $this->m_aWizardSteps; }
|
||||
|
||||
/**
|
||||
* Displays one step of the wizard
|
||||
*/
|
||||
public function DisplayWizardStep($aStep, $iStepIndex, &$iMaxInputId, &$aFieldsMap, $bFinishEnabled = false, $aArgs = array())
|
||||
{
|
||||
if ($iStepIndex == 1) // one big form that contains everything, to make sure that the uploaded files are posted too
|
||||
{
|
||||
$this->m_oPage->add("<form method=\"post\" enctype=\"multipart/form-data\" action=\"../pages/UI.php\">\n");
|
||||
}
|
||||
$this->m_oPage->add("<div class=\"wizContainer\" id=\"wizStep$iStepIndex\" style=\"display:none;\">\n");
|
||||
$this->m_oPage->add("<a name=\"step$iStepIndex\" />\n");
|
||||
$aStates = MetaModel::EnumStates($this->m_sClass);
|
||||
$aDetails = array();
|
||||
$sJSHandlerCode = ''; // Javascript code to be executed each time this step of the wizard is entered
|
||||
foreach($aStep as $sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oAttDef->IsWritable())
|
||||
{
|
||||
$sAttLabel = $oAttDef->GetLabel();
|
||||
$iOptions = isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode]) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
|
||||
|
||||
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
|
||||
if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT))
|
||||
{
|
||||
$aFields[$sAttCode] = array();
|
||||
foreach($aPrerequisites as $sCode)
|
||||
{
|
||||
$aFields[$sAttCode][$sCode] = '';
|
||||
}
|
||||
}
|
||||
if (count($aPrerequisites) > 0)
|
||||
{
|
||||
$aOptions[] = 'Prerequisites: '.implode(', ', $aPrerequisites);
|
||||
}
|
||||
|
||||
$sFieldFlag = (($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE)) || (!$oAttDef->IsNullAllowed()) )? ' <span class="hilite">*</span>' : '';
|
||||
$oDefaultValuesSet = $oAttDef->GetDefaultValue(/* $oObject->ToArgs() */); // @@@ TO DO: get the object's current value if the object exists
|
||||
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($this->m_oPage, $this->m_sClass, $sAttCode, $oAttDef, $oDefaultValuesSet, '', "att_$iMaxInputId", '', $iOptions, $aArgs);
|
||||
$aFieldsMap["att_$iMaxInputId"] = $sAttCode;
|
||||
$aDetails[] = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().$sFieldFlag.'</span>', 'value' => "<span id=\"field_att_$iMaxInputId\">$sHTMLValue</span>");
|
||||
if ($oAttDef->GetValuesDef() != null)
|
||||
{
|
||||
$sJSHandlerCode .= "\toWizardHelper.RequestAllowedValues('$sAttCode');\n";
|
||||
}
|
||||
if ($oAttDef->GetDefaultValue() != null)
|
||||
{
|
||||
$sJSHandlerCode .= "\toWizardHelper.RequestDefaultValue('$sAttCode');\n";
|
||||
}
|
||||
if ($oAttDef->IsLinkSet())
|
||||
{
|
||||
$sJSHandlerCode .= "\toLinkWidgetatt_$iMaxInputId.Init();";
|
||||
}
|
||||
$iMaxInputId++;
|
||||
}
|
||||
}
|
||||
//$aDetails[] = array('label' => '', 'value' => '<input type="button" value="Next >>">');
|
||||
$this->m_oPage->details($aDetails);
|
||||
$sBackButtonDisabled = ($iStepIndex <= 1) ? 'disabled' : '';
|
||||
$sDisabled = $bFinishEnabled ? '' : 'disabled';
|
||||
$nbSteps = count($this->m_aWizardSteps['mandatory']) + count($this->m_aWizardSteps['optional']);
|
||||
$this->m_oPage->add("<div style=\"text-align:center\">
|
||||
<input type=\"button\" value=\"".Dict::S('UI:Button:Back')."\" $sBackButtonDisabled onClick=\"GoToStep($iStepIndex, $iStepIndex - 1)\" />
|
||||
<input type=\"button\" value=\"".Dict::S('UI:Button:Next')."\" onClick=\"GoToStep($iStepIndex, 1+$iStepIndex)\" />
|
||||
<input type=\"button\" value=\"".Dict::S('UI:Button:Finish')."\" $sDisabled onClick=\"GoToStep($iStepIndex, 1+$nbSteps)\" />
|
||||
</div>\n");
|
||||
$this->m_oPage->add("
|
||||
<script type=\"text/javascript\">
|
||||
function OnEnterStep{$iStepIndex}()
|
||||
{
|
||||
oWizardHelper.ResetQuery();
|
||||
oWizardHelper.UpdateWizard();
|
||||
|
||||
$sJSHandlerCode
|
||||
|
||||
oWizardHelper.AjaxQueryServer();
|
||||
}
|
||||
</script>\n");
|
||||
$this->m_oPage->add("</div>\n\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the final step of the wizard: a confirmation screen
|
||||
*/
|
||||
public function DisplayFinalStep($iStepIndex, $aFieldsMap)
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$this->m_oPage->add("<div class=\"wizContainer\" id=\"wizStep$iStepIndex\" style=\"display:none;\">\n");
|
||||
$this->m_oPage->add("<a name=\"step$iStepIndex\" />\n");
|
||||
$this->m_oPage->P(Dict::S('UI:Wizard:FinalStepTitle'));
|
||||
$this->m_oPage->add("<input type=\"hidden\" name=\"operation\" value=\"wizard_apply_new\" />\n");
|
||||
$this->m_oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\" />\n");
|
||||
$this->m_oPage->add("<input type=\"hidden\" id=\"wizard_json_obj\" name=\"json_obj\" value=\"\" />\n");
|
||||
$this->m_oPage->add("<script type=\"text/javascript\">\n");
|
||||
$this->m_oPage->add("function OnEnterStep$iStepIndex() {\n");
|
||||
foreach($aFieldsMap as $iInputId => $sAttCode)
|
||||
{
|
||||
$this->m_oPage->add("\toWizardHelper.UpdateCurrentValue('$sAttCode');\n");
|
||||
}
|
||||
$this->m_oPage->add("\toWizardHelper.Preview('object_preview');\n");
|
||||
$this->m_oPage->add("\t$('#wizard_json_obj').val(oWizardHelper.ToJSON());\n");
|
||||
$this->m_oPage->add("}\n");
|
||||
$this->m_oPage->add("</script>\n");
|
||||
$this->m_oPage->add("<div id=\"object_preview\">\n");
|
||||
$this->m_oPage->add("</div>\n");
|
||||
$this->m_oPage->add($oAppContext->GetForForm());
|
||||
$this->m_oPage->add("<input type=\"button\" value=\"".Dict::S('UI:Button:Back')."\" onClick=\"GoToStep($iStepIndex, $iStepIndex - 1)\" />");
|
||||
$this->m_oPage->add("<input type=\"submit\" value=\"Create ".MetaModel::GetName($this->m_sClass)."\" />\n");
|
||||
$this->m_oPage->add("</div>\n");
|
||||
$this->m_oPage->add("</form>\n");
|
||||
}
|
||||
/**
|
||||
* Compute the order of the fields & pages in the wizard
|
||||
* @param $oPage iTopWebPage The current page (used to display error messages)
|
||||
* @param $sClass string Name of the class
|
||||
* @param $sStateCode string Code of the target state of the object
|
||||
* @return hash Two dimensional array: each element represents the list of fields for a given page
|
||||
*/
|
||||
protected function ComputeWizardStructure()
|
||||
{
|
||||
$aWizardSteps = array( 'mandatory' => array(), 'optional' => array());
|
||||
$aFieldsDone = array(); // Store all the fields that are already covered by a previous step of the wizard
|
||||
|
||||
$aStates = MetaModel::EnumStates($this->m_sClass);
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass);
|
||||
|
||||
$aMandatoryAttributes = array();
|
||||
// Some attributes are always mandatory independently of the state machine (if any)
|
||||
foreach(MetaModel::GetAttributesList($this->m_sClass) as $sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if (!$oAttDef->IsExternalField() && !$oAttDef->IsNullAllowed() &&
|
||||
$oAttDef->IsWritable() && ($sAttCode != $sStateAttCode) )
|
||||
{
|
||||
$aMandatoryAttributes[$sAttCode] = OPT_ATT_MANDATORY;
|
||||
}
|
||||
}
|
||||
|
||||
// Now check the attributes that are mandatory in the specified state
|
||||
if ( (!empty($this->m_sTargetState)) && (count($aStates[$this->m_sTargetState]['attribute_list']) > 0) )
|
||||
{
|
||||
// Check all the fields that *must* be included in the wizard for this
|
||||
// particular target state
|
||||
$aFields = array();
|
||||
foreach($aStates[$this->m_sTargetState]['attribute_list'] as $sAttCode => $iOptions)
|
||||
{
|
||||
if ( (isset($aMandatoryAttributes[$sAttCode])) &&
|
||||
($aMandatoryAttributes[$sAttCode] & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) )
|
||||
{
|
||||
$aMandatoryAttributes[$sAttCode] |= $iOptions;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aMandatoryAttributes[$sAttCode] = $iOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check all the fields that *must* be included in the wizard
|
||||
// i.e. all mandatory, must-change or must-prompt fields that are
|
||||
// not also read-only or hidden.
|
||||
// Some fields may be required (null not allowed) from the database
|
||||
// perspective, but hidden or read-only from the user interface perspective
|
||||
$aFields = array();
|
||||
foreach($aMandatoryAttributes as $sAttCode => $iOptions)
|
||||
{
|
||||
if ( ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) &&
|
||||
!($iOptions & (OPT_ATT_READONLY | OPT_ATT_HIDDEN)) )
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
|
||||
$aFields[$sAttCode] = array();
|
||||
foreach($aPrerequisites as $sCode)
|
||||
{
|
||||
$aFields[$sAttCode][$sCode] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now use the dependencies between the fields to order them
|
||||
// Start from the order of the 'details'
|
||||
$aList = MetaModel::GetZListItems($this->m_sClass, 'details');
|
||||
$index = 0;
|
||||
$aOrder = array();
|
||||
foreach($aFields as $sAttCode => $void)
|
||||
{
|
||||
$aOrder[$sAttCode] = 999; // At the end of the list...
|
||||
}
|
||||
foreach($aList as $sAttCode)
|
||||
{
|
||||
if (array_key_exists($sAttCode, $aFields))
|
||||
{
|
||||
$aOrder[$sAttCode] = $index;
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
foreach($aFields as $sAttCode => $aDependencies)
|
||||
{
|
||||
// All fields with no remaining dependencies can be entered at this
|
||||
// step of the wizard
|
||||
if (count($aDependencies) > 0)
|
||||
{
|
||||
$iMaxPos = 0;
|
||||
// Remove this field from the dependencies of the other fields
|
||||
foreach($aDependencies as $sDependentAttCode => $void)
|
||||
{
|
||||
// position the current field after the ones it depends on
|
||||
$iMaxPos = max($iMaxPos, 1+$aOrder[$sDependentAttCode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
asort($aOrder);
|
||||
$aCurrentStep = array();
|
||||
foreach($aOrder as $sAttCode => $rank)
|
||||
{
|
||||
$aCurrentStep[] = $sAttCode;
|
||||
$aFieldsDone[$sAttCode] = '';
|
||||
}
|
||||
$aWizardSteps['mandatory'][] = $aCurrentStep;
|
||||
|
||||
|
||||
// Now computes the steps to fill the optional fields
|
||||
$aFields = array(); // reset
|
||||
foreach(MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
$iOptions = (isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode])) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
|
||||
if ( ($sStateAttCode != $sAttCode) &&
|
||||
(!$oAttDef->IsExternalField()) &&
|
||||
(($iOptions & (OPT_ATT_HIDDEN | OPT_ATT_READONLY)) == 0) &&
|
||||
(!isset($aFieldsDone[$sAttCode])) )
|
||||
|
||||
{
|
||||
// 'State', external fields, read-only and hidden fields
|
||||
// and fields that are already listed in the wizard
|
||||
// are removed from the 'optional' part of the wizard
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
|
||||
$aFields[$sAttCode] = array();
|
||||
foreach($aPrerequisites as $sCode)
|
||||
{
|
||||
if (!isset($aFieldsDone[$sCode]))
|
||||
{
|
||||
// retain only the dependencies that were not covered
|
||||
// in the 'mandatory' part of the wizard
|
||||
$aFields[$sAttCode][$sCode] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now use the dependencies between the fields to order them
|
||||
while(count($aFields) > 0)
|
||||
{
|
||||
$aCurrentStep = array();
|
||||
foreach($aFields as $sAttCode => $aDependencies)
|
||||
{
|
||||
// All fields with no remaining dependencies can be entered at this
|
||||
// step of the wizard
|
||||
if (count($aDependencies) == 0)
|
||||
{
|
||||
$aCurrentStep[] = $sAttCode;
|
||||
$aFieldsDone[$sAttCode] = '';
|
||||
unset($aFields[$sAttCode]);
|
||||
// Remove this field from the dependencies of the other fields
|
||||
foreach($aFields as $sUpdatedCode => $aDummy)
|
||||
{
|
||||
// remove the dependency
|
||||
unset($aFields[$sUpdatedCode][$sAttCode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($aCurrentStep) == 0)
|
||||
{
|
||||
// This step of the wizard would contain NO field !
|
||||
$this->m_oPage->add(Dict::S('UI:Error:WizardCircularReferenceInDependencies'));
|
||||
print_r($aFields);
|
||||
break;
|
||||
}
|
||||
$aWizardSteps['optional'][] = $aCurrentStep;
|
||||
}
|
||||
return $aWizardSteps;
|
||||
|
||||
}
|
||||
}
|
||||
?>
|
||||
177
application/user.preferences.class.inc.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Store and retrieve user's preferences (i.e persistent per user settings)
|
||||
*
|
||||
* @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');
|
||||
|
||||
/**
|
||||
* This class is used to store, in a persistent manner, user related settings (preferences)
|
||||
* For each user, one record in the database will be created, making their preferences permanent and storing a list
|
||||
* of properties (pairs of name/value strings)
|
||||
* This overcomes some limitations of cookies: limited number of cookies, maximum size, depends on the browser, etc..
|
||||
* This class is used in conjunction with the GetUserPreferences/SetUserPreferences javascript functions (utils.js)
|
||||
*/
|
||||
class appUserPreferences extends DBObject
|
||||
{
|
||||
static $oUserPrefs = null; // Local cache
|
||||
|
||||
/**
|
||||
* Get the value of the given property/preference
|
||||
* If not set, the default value will be returned
|
||||
* @param string $sCode Code/Name of the property to set
|
||||
* @param string $sDefaultValue The default value
|
||||
* @return string The value of the property for the current user
|
||||
*/
|
||||
static function GetPref($sCode, $sDefaultValue)
|
||||
{
|
||||
if (self::$oUserPrefs == null)
|
||||
{
|
||||
self::Load();
|
||||
}
|
||||
$aPrefs = self::$oUserPrefs->Get('preferences');
|
||||
if (isset($aPrefs[$sCode]))
|
||||
{
|
||||
return $aPrefs[$sCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sDefaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value for a given preference, and stores it into the database
|
||||
* @param string $sCode Code/Name of the property/preference to set
|
||||
* @param string $sValue Value to set
|
||||
*/
|
||||
static function SetPref($sCode, $sValue)
|
||||
{
|
||||
if (self::$oUserPrefs == null)
|
||||
{
|
||||
self::Load();
|
||||
}
|
||||
$aPrefs = self::$oUserPrefs->Get('preferences');
|
||||
$aPrefs[$sCode] = $sValue;
|
||||
self::$oUserPrefs->Set('preferences', $aPrefs);
|
||||
self::Save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function to get all the preferences for the user, packed as a JSON object
|
||||
* @return string JSON representation of the preferences
|
||||
*/
|
||||
static function GetAsJSON()
|
||||
{
|
||||
if (self::$oUserPrefs == null)
|
||||
{
|
||||
self::Load();
|
||||
}
|
||||
$aPrefs = self::$oUserPrefs->Get('preferences');
|
||||
return json_encode($aPrefs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this function if the user has changed (like when doing a logoff...)
|
||||
*/
|
||||
static public function Reset()
|
||||
{
|
||||
self::$oUserPrefs = null;
|
||||
}
|
||||
/**
|
||||
* Call this function to ERASE all the preferences from the current user
|
||||
*/
|
||||
static public function ClearPreferences()
|
||||
{
|
||||
self::$oUserPrefs = null;
|
||||
}
|
||||
|
||||
static protected function Save()
|
||||
{
|
||||
if (self::$oUserPrefs != null)
|
||||
{
|
||||
if (self::$oUserPrefs->IsModified())
|
||||
{
|
||||
self::$oUserPrefs->DBUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the preferences for the current user, creating the record in the database
|
||||
* if needed
|
||||
*/
|
||||
static protected function Load()
|
||||
{
|
||||
if (self::$oUserPrefs != null) return;
|
||||
$oSearch = new DBObjectSearch('appUserPreferences');
|
||||
$oSearch->AddCondition('userid', UserRights::GetUserId(), '=');
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$oObj = $oSet->Fetch();
|
||||
if ($oObj == null)
|
||||
{
|
||||
// No prefs (yet) for this user, create the object
|
||||
$oObj = new appUserPreferences();
|
||||
$oObj->Set('userid', UserRights::GetUserId());
|
||||
$oObj->Set('preferences', array()); // Default preferences: an empty array
|
||||
try
|
||||
{
|
||||
$oObj->DBInsert();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
self::$oUserPrefs = $oObj;
|
||||
}
|
||||
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_app_preferences",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", array("allowed_values"=>null, "sql"=>"preferences", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overloading this function here to secure a fix done right before the release
|
||||
* The real fix should be to implement this verb in DBObject
|
||||
*/
|
||||
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
||||
{
|
||||
$this->DBDelete();
|
||||
}
|
||||
}
|
||||
?>
|
||||
309
application/utils.inc.php
Normal file
@@ -0,0 +1,309 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Static class utils
|
||||
*
|
||||
* @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/config.class.inc.php');
|
||||
require_once(APPROOT.'/application/transaction.class.inc.php');
|
||||
|
||||
define('ITOP_CONFIG_FILE', APPROOT.'/config-itop.php');
|
||||
|
||||
class FileUploadException extends Exception
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper functions to interact with forms: read parameters, upload files...
|
||||
* @package iTop
|
||||
*/
|
||||
class utils
|
||||
{
|
||||
private static $m_sConfigFile = ITOP_CONFIG_FILE;
|
||||
private static $m_oConfig = null;
|
||||
|
||||
|
||||
public static function IsModeCLI()
|
||||
{
|
||||
$sSAPIName = php_sapi_name();
|
||||
$sCleanName = strtolower(trim($sSAPIName));
|
||||
if ($sCleanName == 'cli')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function ReadParam($sName, $defaultValue = "", $bAllowCLI = false)
|
||||
{
|
||||
global $argv;
|
||||
$retValue = $defaultValue;
|
||||
if (isset($_REQUEST[$sName]))
|
||||
{
|
||||
$retValue = $_REQUEST[$sName];
|
||||
}
|
||||
elseif ($bAllowCLI && isset($argv))
|
||||
{
|
||||
foreach($argv as $iArg => $sArg)
|
||||
{
|
||||
if (preg_match('/^--'.$sName.'=(.*)$/', $sArg, $aMatches))
|
||||
{
|
||||
$retValue = $aMatches[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
public static function ReadPostedParam($sName, $defaultValue = "")
|
||||
{
|
||||
return isset($_POST[$sName]) ? $_POST[$sName] : $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an uploaded file and turns it into an ormDocument object - Triggers an exception in case of error
|
||||
* @param string $sName Name of the input used from uploading the file
|
||||
* @return ormDocument The uploaded file (can be 'empty' if nothing was uploaded)
|
||||
*/
|
||||
public static function ReadPostedDocument($sName)
|
||||
{
|
||||
$oDocument = new ormDocument(); // an empty document
|
||||
if(isset($_FILES[$sName]))
|
||||
{
|
||||
switch($_FILES[$sName]['error'])
|
||||
{
|
||||
case UPLOAD_ERR_OK:
|
||||
$doc_content = file_get_contents($_FILES[$sName]['tmp_name']);
|
||||
$sMimeType = $_FILES[$sName]['type'];
|
||||
if (function_exists('finfo_file'))
|
||||
{
|
||||
// as of PHP 5.3 the fileinfo extension is bundled within PHP
|
||||
// in which case we don't trust the mime type provided by the browser
|
||||
$rInfo = @finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
|
||||
if ($rInfo !== false)
|
||||
{
|
||||
$sType = @finfo_file($rInfo, $file);
|
||||
if ( ($sType !== false)
|
||||
&& is_string($sType)
|
||||
&& (strlen($sType)>0))
|
||||
{
|
||||
$sMimeType = $sType;
|
||||
}
|
||||
}
|
||||
@finfo_close($rInfo);
|
||||
}
|
||||
$oDocument = new ormDocument($doc_content, $sMimeType, $_FILES[$sName]['name']);
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
// no file to load, it's a normal case, just return an empty document
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadedFileTooBig', ini_get('upload_max_filesize')));
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
throw new FileUploadException(Dict::S('UI:Error:UploadedFileTruncated.'));
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
throw new FileUploadException(Dict::S('UI:Error:NoTmpDir'));
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_CANT_WRITE:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:CannotWriteToTmp_Dir', ini_get('upload_tmp_dir')));
|
||||
break;
|
||||
|
||||
case UPLOAD_ERR_EXTENSION:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $_FILES[$sName]['name']));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $_FILES[$sName]['error']));
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
return $oDocument;
|
||||
}
|
||||
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
return privUITransaction::GetNewTransactionId();
|
||||
}
|
||||
|
||||
public static function IsTransactionValid($sId, $bRemoveTransaction = true)
|
||||
{
|
||||
return privUITransaction::IsTransactionValid($sId, $bRemoveTransaction);
|
||||
}
|
||||
|
||||
public static function RemoveTransaction($sId)
|
||||
{
|
||||
return privUITransaction::RemoveTransaction($sId);
|
||||
}
|
||||
|
||||
public static function ReadFromFile($sFileName)
|
||||
{
|
||||
if (!file_exists($sFileName)) return false;
|
||||
return file_get_contents($sFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert a value expressed in a 'user friendly format'
|
||||
* as in php.ini, e.g. 256k, 2M, 1G etc. Into a number of bytes
|
||||
* @param mixed $value The value as read from php.ini
|
||||
* @return number
|
||||
*/
|
||||
public static function ConvertToBytes( $value )
|
||||
{
|
||||
$iReturn = $value;
|
||||
if ( !is_numeric( $value ) )
|
||||
{
|
||||
$iLength = strlen( $value );
|
||||
$iReturn = substr( $value, 0, $iLength - 1 );
|
||||
$sUnit = strtoupper( substr( $value, $iLength - 1 ) );
|
||||
switch ( $sUnit )
|
||||
{
|
||||
case 'G':
|
||||
$iReturn *= 1024;
|
||||
case 'M':
|
||||
$iReturn *= 1024;
|
||||
case 'K':
|
||||
$iReturn *= 1024;
|
||||
}
|
||||
}
|
||||
return $iReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an absolute URL to the current page
|
||||
* @param $bQueryString bool True to also get the query string, false otherwise
|
||||
* @param $bForceHTTPS bool True to force HTTPS, false otherwise
|
||||
* @return string The absolute URL to the current page
|
||||
*/
|
||||
static public function GetAbsoluteUrl($bQueryString = true, $bForceHTTPS = false)
|
||||
{
|
||||
// Build an absolute URL to this page on this server/port
|
||||
$sServerName = isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '';
|
||||
if (MetaModel::GetConfig()->GetSecureConnectionRequired() || MetaModel::GetConfig()->GetHttpsHyperlinks())
|
||||
{
|
||||
// If a secure connection is required, or if the URL is requested to start with HTTPS
|
||||
// then any URL must start with https !
|
||||
$bForceHTTPS = true;
|
||||
}
|
||||
if ($bForceHTTPS)
|
||||
{
|
||||
$sProtocol = 'https';
|
||||
$sPort = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sProtocol = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']!="off")) ? 'https' : 'http';
|
||||
$iPort = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : 80;
|
||||
if ($sProtocol == 'http')
|
||||
{
|
||||
$sPort = ($iPort == 80) ? '' : ':'.$iPort;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sPort = ($iPort == 443) ? '' : ':'.$iPort;
|
||||
}
|
||||
}
|
||||
// $_SERVER['REQUEST_URI'] is empty when running on IIS
|
||||
// Let's use Ivan Tcholakov's fix (found on www.dokeos.com)
|
||||
if (!empty($_SERVER['REQUEST_URI']))
|
||||
{
|
||||
$sPath = $_SERVER['REQUEST_URI'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sPath = $_SERVER['SCRIPT_NAME'];
|
||||
if (!empty($_SERVER['QUERY_STRING']))
|
||||
{
|
||||
$sPath .= '?'.$_SERVER['QUERY_STRING'];
|
||||
}
|
||||
$_SERVER['REQUEST_URI'] = $sPath;
|
||||
}
|
||||
$sPath = $_SERVER['REQUEST_URI'];
|
||||
if (!$bQueryString)
|
||||
{
|
||||
// remove all the parameters from the query string
|
||||
$iQuestionMarkPos = strpos($sPath, '?');
|
||||
if ($iQuestionMarkPos !== false)
|
||||
{
|
||||
$sPath = substr($sPath, 0, $iQuestionMarkPos);
|
||||
}
|
||||
}
|
||||
$sUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}";
|
||||
|
||||
return $sUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute URL PATH of the current page
|
||||
* @param $bForceHTTPS bool True to force HTTPS, false otherwise
|
||||
* @return string The absolute URL to the current page
|
||||
*/
|
||||
static public function GetAbsoluteUrlPath($bForceHTTPS = false)
|
||||
{
|
||||
$sAbsoluteUrl = self::GetAbsoluteUrl(false, $bForceHTTPS); // False => Don't get the query string
|
||||
$sAbsoluteUrl = substr($sAbsoluteUrl, 0, 1+strrpos($sAbsoluteUrl, '/')); // remove the current page, keep just the path, up to the last /
|
||||
return $sAbsoluteUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute URL to the server's root path
|
||||
* @param $bForceHTTPS bool True to force HTTPS, false otherwise
|
||||
* @return string The absolute URL to the server's root, without the first slash
|
||||
*/
|
||||
static public function GetAbsoluteUrlRoot($bForceHTTPS = false)
|
||||
{
|
||||
$sAbsoluteUrl = self::GetAbsoluteUrl(false, $bForceHTTPS); // False => Don't get the query string
|
||||
$sServerPos = 3 + strpos($sAbsoluteUrl, '://');
|
||||
$iFirstSlashPos = strpos($sAbsoluteUrl, '/', $sServerPos);
|
||||
if ($iFirstSlashPos !== false)
|
||||
{
|
||||
$sAbsoluteUrl = substr($sAbsoluteUrl, 0, $iFirstSlashPos); // remove the current page, keep just the path, without the first /
|
||||
}
|
||||
return $sAbsoluteUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether or not log off operation is supported.
|
||||
* Actually in only one case:
|
||||
* 1) iTop is using an internal authentication
|
||||
* 2) the user did not log-in using the "basic" mode (i.e basic authentication) or by passing credentials in the URL
|
||||
* @return boolean True if logoff is supported, false otherwise
|
||||
*/
|
||||
static function CanLogOff()
|
||||
{
|
||||
return (isset($_SESSION['login_mode']) && $_SESSION['login_mode'] == 'form');
|
||||
}
|
||||
}
|
||||
?>
|
||||
373
application/webpage.class.inc.php
Normal file
@@ -0,0 +1,373 @@
|
||||
<?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 WebPage
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Simple helper class to ease the production of HTML pages
|
||||
*
|
||||
* This class provide methods to add content, scripts, includes... to a web page
|
||||
* and renders the full web page by putting the elements in the proper place & order
|
||||
* when the output() method is called.
|
||||
* Usage:
|
||||
* $oPage = new WebPage("Title of my page");
|
||||
* $oPage->p("Hello World !");
|
||||
* $oPage->output();
|
||||
*/
|
||||
class WebPage
|
||||
{
|
||||
protected $s_title;
|
||||
protected $s_content;
|
||||
protected $s_deferred_content;
|
||||
protected $a_scripts;
|
||||
protected $a_styles;
|
||||
protected $a_include_scripts;
|
||||
protected $a_include_stylesheets;
|
||||
protected $a_headers;
|
||||
protected $a_base;
|
||||
protected $iNextId;
|
||||
|
||||
public function __construct($s_title)
|
||||
{
|
||||
$this->s_title = $s_title;
|
||||
$this->s_content = "";
|
||||
$this->s_deferred_content = '';
|
||||
$this->a_scripts = array();
|
||||
$this->a_styles = array();
|
||||
$this->a_linked_scripts = array();
|
||||
$this->a_linked_stylesheets = array();
|
||||
$this->a_headers = array();
|
||||
$this->a_base = array( 'href' => '', 'target' => '');
|
||||
$this->iNextId = 0;
|
||||
ob_start(); // Start capturing the output
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the title of the page after its creation
|
||||
*/
|
||||
public function set_title($s_title)
|
||||
{
|
||||
$this->s_title = $s_title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify a default URL and a default target for all links on a page
|
||||
*/
|
||||
public function set_base($s_href = '', $s_target = '')
|
||||
{
|
||||
$this->a_base['href'] = $s_href;
|
||||
$this->a_base['target'] = $s_target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any text or HTML fragment to the body of the page
|
||||
*/
|
||||
public function add($s_html)
|
||||
{
|
||||
$this->s_content .= $s_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any text or HTML fragment at the end of the body of the page
|
||||
* This is useful to add hidden content, DIVs or FORMs that should not
|
||||
* be embedded into each other.
|
||||
*/
|
||||
public function add_at_the_end($s_html)
|
||||
{
|
||||
$this->s_deferred_content .= $s_html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a paragraph to the body of the page
|
||||
*/
|
||||
public function p($s_html)
|
||||
{
|
||||
$this->add($this->GetP($s_html));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a pre-formatted text to the body of the page
|
||||
*/
|
||||
public function pre($s_html)
|
||||
{
|
||||
$this->add('<pre>'.$s_html.'</pre>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a paragraph to the body of the page
|
||||
*/
|
||||
public function GetP($s_html)
|
||||
{
|
||||
return "<p>$s_html</p>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a tabular content to the web page
|
||||
* @param Hash $aConfig Configuration of the table: hash array of 'column_id' => 'Column Label'
|
||||
* @param Hash $aData Hash array. Data to display in the table: each row is made of 'column_id' => Data. A column 'pkey' is expected for each row
|
||||
* @param Hash $aParams Hash array. Extra parameters for the table.
|
||||
* @return void
|
||||
*/
|
||||
public function table($aConfig, $aData, $aParams = array())
|
||||
{
|
||||
$this->add($this->GetTable($aConfig, $aData, $aParams));
|
||||
}
|
||||
|
||||
public function GetTable($aConfig, $aData, $aParams = array())
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
|
||||
static $iNbTables = 0;
|
||||
$iNbTables++;
|
||||
$sHtml = "";
|
||||
$sHtml .= "<table class=\"listResults\">\n";
|
||||
$sHtml .= "<thead>\n";
|
||||
$sHtml .= "<tr>\n";
|
||||
foreach($aConfig as $sName=>$aDef)
|
||||
{
|
||||
$sHtml .= "<th title=\"".$aDef['description']."\">".$aDef['label']."</th>\n";
|
||||
}
|
||||
$sHtml .= "</tr>\n";
|
||||
$sHtml .= "</thead>\n";
|
||||
$sHtml .= "<tbody>\n";
|
||||
foreach($aData as $aRow)
|
||||
{
|
||||
if (isset($aRow['@class'])) // Row specific class, for hilighting certain rows
|
||||
{
|
||||
$sHtml .= "<tr class=\"{$aRow['@class']}\">\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= "<tr>\n";
|
||||
}
|
||||
foreach($aConfig as $sName=>$aAttribs)
|
||||
{
|
||||
$aMatches = array();
|
||||
$sClass = isset($aAttribs['class']) ? 'class="'.$aAttribs['class'].'"' : '';
|
||||
$sValue = ($aRow[$sName] === '') ? ' ' : $aRow[$sName];
|
||||
$sHtml .= "<td $sClass>$sValue</td>\n";
|
||||
}
|
||||
$sHtml .= "</tr>\n";
|
||||
}
|
||||
$sHtml .= "</tbody>\n";
|
||||
$sHtml .= "</table>\n";
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add some Javascript to the header of the page
|
||||
*/
|
||||
public function add_script($s_script)
|
||||
{
|
||||
$this->a_scripts[] = $s_script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add some Javascript to the header of the page
|
||||
*/
|
||||
public function add_ready_script($s_script)
|
||||
{
|
||||
// Do nothing silently... this is not supported by this type of page...
|
||||
}
|
||||
/**
|
||||
* Add some CSS definitions to the header of the page
|
||||
*/
|
||||
public function add_style($s_style)
|
||||
{
|
||||
$this->a_styles[] = $s_style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a script (as an include, i.e. link) to the header of the page
|
||||
*/
|
||||
public function add_linked_script($s_linked_script)
|
||||
{
|
||||
$this->a_linked_scripts[] = $s_linked_script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a CSS stylesheet (as an include, i.e. link) to the header of the page
|
||||
*/
|
||||
public function add_linked_stylesheet($s_linked_stylesheet, $s_condition = "")
|
||||
{
|
||||
$this->a_linked_stylesheets[] = array( 'link' => $s_linked_stylesheet, 'condition' => $s_condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add some custom header to the page
|
||||
*/
|
||||
public function add_header($s_header)
|
||||
{
|
||||
$this->a_headers[] = $s_header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add needed headers to the page so that it will no be cached
|
||||
*/
|
||||
public function no_cache()
|
||||
{
|
||||
$this->add_header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
|
||||
$this->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a special kind of TABLE useful for displaying the details of an object from a hash array of data
|
||||
*/
|
||||
public function details($aFields)
|
||||
{
|
||||
|
||||
$this->add($this->GetDetails($aFields));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a special kind of TABLE useful for displaying the details of an object from a hash array of data
|
||||
*/
|
||||
public function GetDetails($aFields)
|
||||
{
|
||||
$sHtml = "<table>\n";
|
||||
$sHtml .= "<tbody>\n";
|
||||
foreach($aFields as $aAttrib)
|
||||
{
|
||||
$sHtml .= "<tr>\n";
|
||||
// By Rom, for csv import, proposed to show several values for column selection
|
||||
if (is_array($aAttrib['value']))
|
||||
{
|
||||
$sHtml .= "<td class=\"label\">".$aAttrib['label']."</td><td>".implode("</td><td>", $aAttrib['value'])."</td>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= "<td class=\"label\">".$aAttrib['label']."</td><td>".$aAttrib['value']."</td>\n";
|
||||
}
|
||||
$sHtml .= "</tr>\n";
|
||||
}
|
||||
$sHtml .= "</tbody>\n";
|
||||
$sHtml .= "</table>\n";
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs (via some echo) the complete HTML page by assembling all its elements
|
||||
*/
|
||||
public function output()
|
||||
{
|
||||
foreach($this->a_headers as $s_header)
|
||||
{
|
||||
header($s_header);
|
||||
}
|
||||
$s_captured_output = ob_get_contents();
|
||||
ob_end_clean();
|
||||
echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n";
|
||||
echo "<html>\n";
|
||||
echo "<head>\n";
|
||||
echo "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n";
|
||||
echo "<title>{$this->s_title}</title>\n";
|
||||
echo $this->get_base_tag();
|
||||
foreach($this->a_linked_scripts as $s_script)
|
||||
{
|
||||
echo "<script type=\"text/javascript\" src=\"$s_script\"></script>\n";
|
||||
}
|
||||
if (count($this->a_scripts)>0)
|
||||
{
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
foreach($this->a_scripts as $s_script)
|
||||
{
|
||||
echo "$s_script\n";
|
||||
}
|
||||
echo "</script>\n";
|
||||
}
|
||||
foreach($this->a_linked_stylesheets as $a_stylesheet)
|
||||
{
|
||||
if ($a_stylesheet['condition'] != "")
|
||||
{
|
||||
echo "<!--[if {$a_stylesheet['condition']}]>\n";
|
||||
}
|
||||
echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"{$a_stylesheet['link']}\" />\n";
|
||||
if ($a_stylesheet['condition'] != "")
|
||||
{
|
||||
echo "<![endif]-->\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (count($this->a_styles)>0)
|
||||
{
|
||||
echo "<style>\n";
|
||||
foreach($this->a_styles as $s_style)
|
||||
{
|
||||
echo "$s_style\n";
|
||||
}
|
||||
echo "</style>\n";
|
||||
}
|
||||
echo "</head>\n";
|
||||
echo "<body>\n";
|
||||
echo $this->s_content;
|
||||
if (trim($s_captured_output) != "")
|
||||
{
|
||||
echo "<div class=\"raw_output\">$s_captured_output</div>\n";
|
||||
}
|
||||
echo $this->s_deferred_content;
|
||||
echo "</body>\n";
|
||||
echo "</html>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a series of hidden field[s] from an array
|
||||
*/
|
||||
// By Rom - je verrais bien une serie d'outils pour gerer des parametres que l'on retransmet entre pages d'un wizard...
|
||||
// ptet deriver webpage en webwizard
|
||||
public function add_input_hidden($sLabel, $aData)
|
||||
{
|
||||
foreach($aData as $sKey=>$sValue)
|
||||
{
|
||||
$this->add("<input type=\"hidden\" name=\"".$sLabel."[$sKey]\" value=\"$sValue\">");
|
||||
}
|
||||
}
|
||||
|
||||
protected function get_base_tag()
|
||||
{
|
||||
$sTag = '';
|
||||
if (($this->a_base['href'] != '') || ($this->a_base['target'] != ''))
|
||||
{
|
||||
$sTag = '<base ';
|
||||
if (($this->a_base['href'] != ''))
|
||||
{
|
||||
$sTag .= "href =\"{$this->a_base['href']}\" ";
|
||||
}
|
||||
if (($this->a_base['target'] != ''))
|
||||
{
|
||||
$sTag .= "target =\"{$this->a_base['target']}\" ";
|
||||
}
|
||||
$sTag .= " />\n";
|
||||
}
|
||||
return $sTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an ID (for any kind of HTML tag) that is guaranteed unique in this page
|
||||
* @return int The unique ID (in this page)
|
||||
*/
|
||||
public function GetUniqueId()
|
||||
{
|
||||
return $this->iNextId++;
|
||||
}
|
||||
}
|
||||
?>
|
||||
273
application/wizardhelper.class.inc.php
Normal file
@@ -0,0 +1,273 @@
|
||||
<?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 WizardHelper
|
||||
*
|
||||
* @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');
|
||||
|
||||
class WizardHelper
|
||||
{
|
||||
protected $m_aData;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Constructs the PHP target object from the parameters sent to the web page by the wizard
|
||||
* @param boolean $bReadUploadedFiles True to also ready any uploaded file (for blob/document fields)
|
||||
* @return object
|
||||
*/
|
||||
public function GetTargetObject($bReadUploadedFiles = false)
|
||||
{
|
||||
if (isset($this->m_aData['m_oCurrentValues']['id']))
|
||||
{
|
||||
$oObj = MetaModel::GetObject($this->m_aData['m_sClass'], $this->m_aData['m_oCurrentValues']['id']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj = MetaModel::NewObject($this->m_aData['m_sClass']);
|
||||
}
|
||||
foreach($this->m_aData['m_oCurrentValues'] as $sAttCode => $value)
|
||||
{
|
||||
// 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') && ($sAttCode !== false) && ($value !== null) && ($value !== '$$NULL$$'))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode);
|
||||
if (($oAttDef->IsLinkSet()) && ($value != '') )
|
||||
{
|
||||
// special handling for lists
|
||||
// assumes this is handled as an array of objects
|
||||
// thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...]
|
||||
$aData = json_decode($value, true); // true means decode as a hash array (not an object)
|
||||
// Check what are the meaningful attributes
|
||||
$aFields = $this->GetLinkedWizardStructure($oAttDef);
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$aLinkedObjectsArray = array();
|
||||
if (!is_array($aData))
|
||||
{
|
||||
echo ("aData: '$aData' (value: '$value')\n");
|
||||
}
|
||||
foreach($aData as $aLinkedObject)
|
||||
{
|
||||
$oLinkedObj = MetaModel::NewObject($sLinkedClass);
|
||||
foreach($aFields as $sLinkedAttCode)
|
||||
{
|
||||
if ( isset($aLinkedObject[$sLinkedAttCode]) && ($aLinkedObject[$sLinkedAttCode] !== null) )
|
||||
{
|
||||
$sLinkedAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sLinkedAttCode);
|
||||
if (($sLinkedAttDef->IsExternalKey()) && ($aLinkedObject[$sLinkedAttCode] != '') && ($aLinkedObject[$sLinkedAttCode] != 0) )
|
||||
{
|
||||
// For external keys: load the target object so that external fields
|
||||
// get filled too
|
||||
$oTargetObj = MetaModel::GetObject($sLinkedAttDef->GetTargetClass(), $aLinkedObject[$sLinkedAttCode]);
|
||||
$oLinkedObj->Set($sLinkedAttCode, $oTargetObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oLinkedObj->Set($sLinkedAttCode, $aLinkedObject[$sLinkedAttCode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$aLinkedObjectsArray[] = $oLinkedObj;
|
||||
}
|
||||
$oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray);
|
||||
$oObj->Set($sAttCode, $oSet);
|
||||
}
|
||||
else if ( $oAttDef->GetEditClass() == 'Document' )
|
||||
{
|
||||
if ($bReadUploadedFiles)
|
||||
{
|
||||
$oDocument = utils::ReadPostedDocument('file_'.$sAttCode);
|
||||
$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)) )
|
||||
{
|
||||
// For external keys: load the target object so that external fields
|
||||
// get filled too
|
||||
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value);
|
||||
$oObj->Set($sAttCode, $oTargetObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj->Set($sAttCode, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $oObj;
|
||||
}
|
||||
|
||||
public function GetFieldsForDefaultValue()
|
||||
{
|
||||
return $this->m_aData['m_aDefaultValueRequested'];
|
||||
}
|
||||
|
||||
public function SetDefaultValue($sAttCode, $value)
|
||||
{
|
||||
// Protect against a request for a non existing field
|
||||
if (isset($this->m_aData['m_oFieldsMap'][$sAttCode]))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode);
|
||||
if ($oAttDef->GetEditClass() == 'List')
|
||||
{
|
||||
// special handling for lists
|
||||
// this as to be handled as an array of objects
|
||||
// thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...]
|
||||
// NOT YET IMPLEMENTED !!
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$oSet = $value;
|
||||
$aData = array();
|
||||
$aFields = $this->GetLinkedWizardStructure($oAttDef);
|
||||
while($oSet->fetch())
|
||||
{
|
||||
foreach($aFields as $sLinkedAttCode)
|
||||
{
|
||||
$aRow[$sAttCode] = $oLinkedObj->Get($sLinkedAttCode);
|
||||
}
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
$this->m_aData['m_oDefaultValue'][$sAttCode] = json_encode($aData);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Normal handling for all other scalar attributes
|
||||
$this->m_aData['m_oDefaultValue'][$sAttCode] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function GetFieldsForAllowedValues()
|
||||
{
|
||||
return $this->m_aData['m_aAllowedValuesRequested'];
|
||||
}
|
||||
|
||||
public function SetAllowedValuesHtml($sAttCode, $sHtml)
|
||||
{
|
||||
// Protect against a request for a non existing field
|
||||
if (isset($this->m_aData['m_oFieldsMap'][$sAttCode]))
|
||||
{
|
||||
$this->m_aData['m_oAllowedValues'][$sAttCode] = $sHtml;
|
||||
}
|
||||
}
|
||||
|
||||
public function ToJSON()
|
||||
{
|
||||
return json_encode($this->m_aData);
|
||||
}
|
||||
|
||||
static public function FromJSON($sJSON)
|
||||
{
|
||||
$oWizHelper = new WizardHelper();
|
||||
if (get_magic_quotes_gpc())
|
||||
{
|
||||
$sJSON = stripslashes($sJSON);
|
||||
}
|
||||
$aData = json_decode($sJSON, true); // true means hash array instead of object
|
||||
$oWizHelper->m_aData = $aData;
|
||||
return $oWizHelper;
|
||||
}
|
||||
|
||||
protected function GetLinkedWizardStructure($oAttDef)
|
||||
{
|
||||
$oWizard = new UIWizard(null, $oAttDef->GetLinkedClass());
|
||||
$aWizardSteps = $oWizard->GetWizardStructure();
|
||||
$aFields = array();
|
||||
$sExtKeyToMeCode = $oAttDef->GetExtKeyToMe();
|
||||
// Retrieve as a flat list, all the attributes that are needed to create
|
||||
// an object of the linked class and put them into a flat array, except
|
||||
// the attribute 'ext_key_to_me' which is a constant in our case
|
||||
foreach($aWizardSteps as $sDummy => $aMainSteps)
|
||||
{
|
||||
// 2 entries: 'mandatory' and 'optional'
|
||||
foreach($aMainSteps as $aSteps)
|
||||
{
|
||||
// One entry for each step of the wizard
|
||||
foreach($aSteps as $sAttCode)
|
||||
{
|
||||
if ($sAttCode != $sExtKeyToMeCode)
|
||||
{
|
||||
$aFields[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aFields;
|
||||
}
|
||||
|
||||
public function GetTargetClass()
|
||||
{
|
||||
return $this->m_aData['m_sClass'];
|
||||
}
|
||||
|
||||
public function GetFormPrefix()
|
||||
{
|
||||
return $this->m_aData['m_sFormPrefix'];
|
||||
}
|
||||
|
||||
public function GetIdForField($sFieldName)
|
||||
{
|
||||
$sResult = '';
|
||||
// It may happen that the field we'd like to update does not
|
||||
// exist in the form. For example, if the field should be hidden/read-only
|
||||
// in the current state of the object
|
||||
if (isset($this->m_aData['m_oFieldsMap'][$sFieldName]))
|
||||
{
|
||||
$sResult = $this->m_aData['m_oFieldsMap'][$sFieldName];
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
static function ParseJsonSet($oMe, $sLinkClass, $sExtKeyToMe, $sJsonSet)
|
||||
{
|
||||
$aSet = json_decode($sJsonSet, true); // true means hash array instead of object
|
||||
$oSet = CMDBObjectSet::FromScratch($sLinkClass);
|
||||
foreach($aSet as $aLinkObj)
|
||||
{
|
||||
$oLink = MetaModel::NewObject($sLinkClass);
|
||||
foreach($aLinkObj as $sAttCode => $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode);
|
||||
if (($oAttDef->IsExternalKey()) && ($value != '') )
|
||||
{
|
||||
// For external keys: load the target object so that external fields
|
||||
// get filled too
|
||||
$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value);
|
||||
$oLink->Set($sAttCode, $oTargetObj);
|
||||
}
|
||||
$oLink->Set($sAttCode, $value);
|
||||
}
|
||||
$oLink->Set($sExtKeyToMe, $oMe->GetKey());
|
||||
$oSet->AddObject($oLink);
|
||||
}
|
||||
return $oSet;
|
||||
}
|
||||
}
|
||||
?>
|
||||
100
application/xmlpage.class.inc.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?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 XMLPage
|
||||
*
|
||||
* @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");
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
class XMLPage extends WebPage
|
||||
{
|
||||
/**
|
||||
* For big XML files, it's better NOT to store everything in memory and output the XML piece by piece
|
||||
*/
|
||||
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");
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
$this->add_header("Content-location: export.xml");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function small_p($sText)
|
||||
{
|
||||
}
|
||||
|
||||
public function table($aConfig, $aData, $aParams = array())
|
||||
{
|
||||
}
|
||||
}
|
||||
?>
|
||||
3
approot.inc.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
define('APPROOT', dirname(__FILE__).'/');
|
||||
?>
|
||||
500
core/MyHelpers.class.inc.php
Normal file
@@ -0,0 +1,500 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Various dev/debug helpers
|
||||
* TODO: cleanup or at least re-organize
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* MyHelpers
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class MyHelpers
|
||||
{
|
||||
public static function CheckValueInArray($sDescription, $value, $aData)
|
||||
{
|
||||
if (!in_array($value, $aData))
|
||||
{
|
||||
self::HandleWrongValue($sDescription, $value, $aData);
|
||||
}
|
||||
}
|
||||
|
||||
public static function CheckKeyInArray($sDescription, $key, $aData)
|
||||
{
|
||||
if (!array_key_exists($key, $aData))
|
||||
{
|
||||
self::HandleWrongValue($sDescription, $key, array_keys($aData));
|
||||
}
|
||||
}
|
||||
|
||||
public static function HandleWrongValue($sDescription, $value, $aData)
|
||||
{
|
||||
if (count($aData) == 0)
|
||||
{
|
||||
$sArrayDesc = "{}";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sArrayDesc = "{".implode(", ", $aData)."}";
|
||||
}
|
||||
// exit!
|
||||
throw new CoreException("Wrong value for $sDescription, found '$value' while expecting a value in $sArrayDesc");
|
||||
}
|
||||
|
||||
// getmicrotime()
|
||||
// format sss.mmmuuupppnnn
|
||||
public static function getmicrotime()
|
||||
{
|
||||
list($usec, $sec) = explode(" ",microtime());
|
||||
return ((float)$usec + (float)$sec);
|
||||
}
|
||||
|
||||
/*
|
||||
* MakeSQLComment
|
||||
* converts hash into text comment which we can use in a (mySQL) query
|
||||
*/
|
||||
public static function MakeSQLComment ($aHash)
|
||||
{
|
||||
if (empty($aHash)) return "";
|
||||
$sComment = "";
|
||||
{
|
||||
foreach($aHash as $sKey=>$sValue)
|
||||
{
|
||||
$sComment .= "\n-- ". $sKey ."=>" . $sValue;
|
||||
}
|
||||
}
|
||||
return $sComment;
|
||||
}
|
||||
|
||||
public static function var_dump_html($aWords, $bFullDisplay = false)
|
||||
{
|
||||
echo "<pre>\n";
|
||||
if ($bFullDisplay)
|
||||
{
|
||||
print_r($aWords); // full dump!
|
||||
}
|
||||
else
|
||||
{
|
||||
var_dump($aWords); // truncate things when they are too big
|
||||
}
|
||||
echo "\n</pre>\n";
|
||||
}
|
||||
|
||||
public static function arg_dump_html()
|
||||
{
|
||||
echo "<pre>\n";
|
||||
echo "GET:\n";
|
||||
var_dump($_GET);
|
||||
echo "POST:\n";
|
||||
var_dump($_POST);
|
||||
echo "\n</pre>\n";
|
||||
}
|
||||
|
||||
public static function var_dump_string($var)
|
||||
{
|
||||
ob_start();
|
||||
print_r($var);
|
||||
$sRet = ob_get_clean();
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
protected static function first_diff_line($s1, $s2)
|
||||
{
|
||||
$aLines1 = explode("\n", $s1);
|
||||
$aLines2 = explode("\n", $s2);
|
||||
for ($i = 0 ; $i < min(count($aLines1), count($aLines2)) ; $i++)
|
||||
{
|
||||
if ($aLines1[$i] != $aLines2[$i]) return $i;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function highlight_line($sMultiline, $iLine, $sHighlightStart = '<b>', $sHightlightEnd = '</b>')
|
||||
{
|
||||
$aLines = explode("\n", $sMultiline);
|
||||
$aLines[$iLine] = $sHighlightStart.$aLines[$iLine].$sHightlightEnd;
|
||||
return implode("\n", $aLines);
|
||||
}
|
||||
|
||||
protected static function first_diff($s1, $s2)
|
||||
{
|
||||
// do not work fine with multiline strings
|
||||
$iLen1 = strlen($s1);
|
||||
$iLen2 = strlen($s2);
|
||||
for ($i = 0 ; $i < min($iLen1, $iLen2) ; $i++)
|
||||
{
|
||||
if ($s1[$i] !== $s2[$i]) return $i;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function last_diff($s1, $s2)
|
||||
{
|
||||
// do not work fine with multiline strings
|
||||
$iLen1 = strlen($s1);
|
||||
$iLen2 = strlen($s2);
|
||||
for ($i = 0 ; $i < min(strlen($s1), strlen($s2)) ; $i++)
|
||||
{
|
||||
if ($s1[$iLen1 - $i - 1] !== $s2[$iLen2 - $i - 1]) return array($iLen1 - $i, $iLen2 - $i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function text_cmp_html($sText1, $sText2, $sHighlight)
|
||||
{
|
||||
$iDiffPos = self::first_diff_line($sText1, $sText2);
|
||||
$sDisp1 = self::highlight_line($sText1, $iDiffPos, '<div style="'.$sHighlight.'">', '</div>');
|
||||
$sDisp2 = self::highlight_line($sText2, $iDiffPos, '<div style="'.$sHighlight.'">', '</div>');
|
||||
echo "<table style=\"valign=top;\">\n";
|
||||
echo "<tr>\n";
|
||||
echo "<td><pre>$sDisp1</pre></td>\n";
|
||||
echo "<td><pre>$sDisp2</pre></td>\n";
|
||||
echo "</tr>\n";
|
||||
echo "</table>\n";
|
||||
}
|
||||
|
||||
protected static function string_cmp_html($s1, $s2, $sHighlight)
|
||||
{
|
||||
$iDiffPos = self::first_diff($s1, $s2);
|
||||
if ($iDiffPos === false)
|
||||
{
|
||||
echo "strings are identical";
|
||||
return;
|
||||
}
|
||||
$sStart = substr($s1, 0, $iDiffPos);
|
||||
|
||||
$aLastDiff = self::last_diff($s1, $s2);
|
||||
$sEnd = substr($s1, $aLastDiff[0]);
|
||||
|
||||
$sMiddle1 = substr($s1, $iDiffPos, $aLastDiff[0] - $iDiffPos);
|
||||
$sMiddle2 = substr($s2, $iDiffPos, $aLastDiff[1] - $iDiffPos);
|
||||
|
||||
echo "<p>$sStart<span style=\"$sHighlight\">$sMiddle1</span>$sEnd</p>\n";
|
||||
echo "<p>$sStart<span style=\"$sHighlight\">$sMiddle2</span>$sEnd</p>\n";
|
||||
}
|
||||
|
||||
protected static function object_cmp_html($oObj1, $oObj2, $sHighlight)
|
||||
{
|
||||
$sObj1 = self::var_dump_string($oObj1);
|
||||
$sObj2 = self::var_dump_string($oObj2);
|
||||
return self::text_cmp_html($sObj1, $sObj2, $sHighlight);
|
||||
}
|
||||
|
||||
public static function var_cmp_html($var1, $var2, $sHighlight = 'color:red; font-weight:bold;')
|
||||
{
|
||||
if (is_object($var1))
|
||||
{
|
||||
return self::object_cmp_html($var1, $var2, $sHighlight);
|
||||
}
|
||||
else if (count(explode("\n", $var1)) > 1)
|
||||
{
|
||||
// multiline string
|
||||
return self::text_cmp_html($var1, $var2, $sHighlight);
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::string_cmp_html($var1, $var2, $sHighlight);
|
||||
}
|
||||
}
|
||||
|
||||
public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null)
|
||||
{
|
||||
if ($aCallStack == null) $aCallStack = debug_backtrace();
|
||||
|
||||
$aCallStack = array_slice($aCallStack, $iLevelsToIgnore);
|
||||
|
||||
$aDigestCallStack = array();
|
||||
$bFirstLine = true;
|
||||
foreach ($aCallStack as $aCallInfo)
|
||||
{
|
||||
$sLine = empty($aCallInfo['line']) ? "" : $aCallInfo['line'];
|
||||
$sFile = empty($aCallInfo['file']) ? "" : $aCallInfo['file'];
|
||||
$sClass = empty($aCallInfo['class']) ? "" : $aCallInfo['class'];
|
||||
$sType = empty($aCallInfo['type']) ? "" : $aCallInfo['type'];
|
||||
$sFunction = empty($aCallInfo['function']) ? "" : $aCallInfo['function'];
|
||||
|
||||
if ($bFirstLine)
|
||||
{
|
||||
$bFirstLine = false;
|
||||
// For this line do not display the "function name" because
|
||||
// that will be the name of our error handler for sure !
|
||||
$sFunctionInfo = "N/A";
|
||||
}
|
||||
else
|
||||
{
|
||||
$args = '';
|
||||
if (empty($aCallInfo['args'])) $aCallInfo['args'] = array();
|
||||
foreach ($aCallInfo['args'] as $a)
|
||||
{
|
||||
if (!empty($args))
|
||||
{
|
||||
$args .= ', ';
|
||||
}
|
||||
switch (gettype($a))
|
||||
{
|
||||
case 'integer':
|
||||
case 'double':
|
||||
$args .= $a;
|
||||
break;
|
||||
case 'string':
|
||||
$a = Str::pure2html(self::beautifulstr($a, 1024, true, true));
|
||||
$args .= "\"$a\"";
|
||||
break;
|
||||
case 'array':
|
||||
$args .= 'Array('.count($a).')';
|
||||
break;
|
||||
case 'object':
|
||||
$args .= 'Object('.get_class($a).')';
|
||||
break;
|
||||
case 'resource':
|
||||
$args .= 'Resource('.strstr($a, '#').')';
|
||||
break;
|
||||
case 'boolean':
|
||||
$args .= $a ? 'True' : 'False';
|
||||
break;
|
||||
case 'NULL':
|
||||
$args .= 'Null';
|
||||
break;
|
||||
default:
|
||||
$args .= 'Unknown';
|
||||
}
|
||||
}
|
||||
$sFunctionInfo = "$sClass $sType $sFunction($args)";
|
||||
}
|
||||
$aDigestCallStack[] = array('File'=>$sFile, 'Line'=>$sLine, 'Function'=>$sFunctionInfo);
|
||||
}
|
||||
return self::make_table_from_assoc_array($aDigestCallStack);
|
||||
}
|
||||
|
||||
public static function dump_callstack($iLevelsToIgnore = 0, $aCallStack = null)
|
||||
{
|
||||
return self::get_callstack_html($iLevelsToIgnore, $aCallStack);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Source: New
|
||||
// Last modif: 2004/12/20 RQU
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
public static function make_table_from_assoc_array(&$aData)
|
||||
{
|
||||
if (!is_array($aData)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not an array");
|
||||
$aFirstRow = reset($aData);
|
||||
if (count($aData) == 0) return '';
|
||||
if (!is_array($aFirstRow)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array");
|
||||
$sOutput = "";
|
||||
$sOutput .= "<TABLE WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"1\" CELLPADDING=\"1\">\n";
|
||||
|
||||
// Table header
|
||||
//
|
||||
$sOutput .= " <TR CLASS=celltitle>\n";
|
||||
foreach ($aFirstRow as $fieldname=>$trash) {
|
||||
$sOutput .= " <TD><B>".$fieldname."</B></TD>\n";
|
||||
}
|
||||
$sOutput .= " </TR>\n";
|
||||
|
||||
// Table contents
|
||||
//
|
||||
$iCount = 0;
|
||||
foreach ($aData as $aRow) {
|
||||
$sStyle = ($iCount++ % 2 ? "STYLE=\"background-color : #eeeeee\"" : "");
|
||||
$sOutput .= " <TR $sStyle CLASS=cell>\n";
|
||||
foreach ($aRow as $data) {
|
||||
if (strlen($data) == 0) {
|
||||
$data = " ";
|
||||
}
|
||||
$sOutput .= " <TD>".$data."</TD>\n";
|
||||
}
|
||||
$sOutput .= " </TR>\n";
|
||||
}
|
||||
|
||||
$sOutput .= "</TABLE>\n";
|
||||
return $sOutput;
|
||||
}
|
||||
|
||||
public static function debug_breakpoint($arg)
|
||||
{
|
||||
echo "<H1> Debug breakpoint </H1>\n";
|
||||
MyHelpers::var_dump_html($arg);
|
||||
MyHelpers::dump_callstack();
|
||||
exit;
|
||||
}
|
||||
public static function debug_breakpoint_notempty($arg)
|
||||
{
|
||||
if (empty($arg)) return;
|
||||
echo "<H1> Debug breakpoint (triggered on non-empty value) </H1>\n";
|
||||
MyHelpers::var_dump_html($arg);
|
||||
MyHelpers::dump_callstack();
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlentities()
|
||||
* ... same as htmlentities, but designed for xml !
|
||||
*/
|
||||
public static function xmlentities($string)
|
||||
{
|
||||
return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&' , '"', ''' , '<' , '>' ), $string );
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlencode()
|
||||
* Encodes a string so that for sure it can be output as an xml data string
|
||||
*/
|
||||
public static function xmlencode($string)
|
||||
{
|
||||
return xmlentities(iconv("UTF-8", "UTF-8//IGNORE",$string));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Source: New - format strings for output
|
||||
// Last modif: 2005/01/18 RQU
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
public static function beautifulstr($sLongString, $iMaxLen, $bShowLen=false, $bShowTooltip=true)
|
||||
{
|
||||
if (!is_string($sLongString)) throw new CoreException("beautifulstr: expect a string as 1st argument");
|
||||
|
||||
// Nothing to do if the string is short
|
||||
if (strlen($sLongString) <= $iMaxLen) return $sLongString;
|
||||
|
||||
// Truncate the string
|
||||
$sSuffix = "...";
|
||||
if ($bShowLen) {
|
||||
$sSuffix .= "(".strlen($sLongString)." chars)...";
|
||||
}
|
||||
$sOutput = substr($sLongString, 0, $iMaxLen - strlen($sSuffix)).$sSuffix;
|
||||
$sOutput = htmlspecialchars($sOutput);
|
||||
|
||||
// Add tooltip if required
|
||||
//if ($bShowTooltip) {
|
||||
// $oTooltip = new gui_tooltip($sLongString);
|
||||
// $sOutput = "<SPAN ".$oTooltip->get_mouseOver_code().">".$sOutput."</SPAN>";
|
||||
//}
|
||||
return $sOutput;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Utility class: static methods for cleaning & escaping untrusted (i.e.
|
||||
user-supplied) strings.
|
||||
Any string can (usually) be thought of as being in one of these 'modes':
|
||||
pure = what the user actually typed / what you want to see on the page /
|
||||
what is actually stored in the DB
|
||||
gpc = incoming GET, POST or COOKIE data
|
||||
sql = escaped for passing safely to RDBMS via SQL (also, data from DB
|
||||
queries and file reads if you have magic_quotes_runtime on--which
|
||||
is rare)
|
||||
html = safe for html display (htmlentities applied)
|
||||
Always knowing what mode your string is in--using these methods to
|
||||
convert between modes--will prevent SQL injection and cross-site scripting.
|
||||
This class refers to its own namespace (so it can work in PHP 4--there is no
|
||||
self keyword until PHP 5). Do not change the name of the class w/o changing
|
||||
all the internal references.
|
||||
Example usage: a POST value that you want to query with:
|
||||
$username = Str::gpc2sql($_POST['username']);
|
||||
*/
|
||||
//This sets SQL escaping to use slashes; for Sybase(/MSSQL)-style escaping
|
||||
// ( ' --> '' ), set to true.
|
||||
define('STR_SYBASE', false);
|
||||
class Str
|
||||
{
|
||||
public static function gpc2sql($gpc, $maxLength = false)
|
||||
{
|
||||
return self::pure2sql(self::gpc2pure($gpc), $maxLength);
|
||||
}
|
||||
public static function gpc2html($gpc, $maxLength = false)
|
||||
{
|
||||
return self::pure2html(self::gpc2pure($gpc), $maxLength);
|
||||
}
|
||||
public static function gpc2pure($gpc)
|
||||
{
|
||||
if (ini_get('magic_quotes_sybase')) $pure = str_replace("''", "'", $gpc);
|
||||
else $pure = get_magic_quotes_gpc() ? stripslashes($gpc) : $gpc;
|
||||
return $pure;
|
||||
}
|
||||
public static function html2pure($html)
|
||||
{
|
||||
return html_entity_decode($html);
|
||||
}
|
||||
public static function html2sql($html, $maxLength = false)
|
||||
{
|
||||
return self::pure2sql(self::html2pure($html), $maxLength);
|
||||
}
|
||||
public static function pure2html($pure, $maxLength = false)
|
||||
{
|
||||
// Check for HTML entities, but be careful the DB is in UTF-8
|
||||
return $maxLength
|
||||
? htmlentities(substr($pure, 0, $maxLength), ENT_COMPAT, 'UTF-8')
|
||||
: htmlentities($pure, ENT_COMPAT, 'UTF-8');
|
||||
}
|
||||
public static function pure2sql($pure, $maxLength = false)
|
||||
{
|
||||
if ($maxLength) $pure = substr($pure, 0, $maxLength);
|
||||
return (STR_SYBASE)
|
||||
? str_replace("'", "''", $pure)
|
||||
: addslashes($pure);
|
||||
}
|
||||
public static function sql2html($sql, $maxLength = false)
|
||||
{
|
||||
$pure = self::sql2pure($sql);
|
||||
if ($maxLength) $pure = substr($pure, 0, $maxLength);
|
||||
return self::pure2html($pure);
|
||||
}
|
||||
public static function sql2pure($sql)
|
||||
{
|
||||
return (STR_SYBASE)
|
||||
? str_replace("''", "'", $sql)
|
||||
: stripslashes($sql);
|
||||
}
|
||||
|
||||
public static function xml2pure($xml)
|
||||
{
|
||||
// #@# - not implemented
|
||||
return $xml;
|
||||
}
|
||||
public static function pure2xml($pure)
|
||||
{
|
||||
return self::xmlencode($pure);
|
||||
}
|
||||
|
||||
protected static function xmlentities($string)
|
||||
{
|
||||
return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&' , '"', ''' , '<' , '>' ), $string );
|
||||
}
|
||||
|
||||
/**
|
||||
* xmlencode()
|
||||
* Encodes a string so that for sure it can be output as an xml data string
|
||||
*/
|
||||
protected static function xmlencode($string)
|
||||
{
|
||||
return self::xmlentities(iconv("UTF-8", "UTF-8//IGNORE",$string));
|
||||
}
|
||||
|
||||
public static function islowcase($sString)
|
||||
{
|
||||
return (strtolower($sString) == $sString);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
349
core/action.class.inc.php
Normal file
@@ -0,0 +1,349 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Persistent classes (internal): user defined actions
|
||||
*
|
||||
* @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/email.class.inc.php');
|
||||
|
||||
/**
|
||||
* A user defined action, to customize the application
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class Action extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_action",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum(array('test'=>'Being tested' ,'enabled'=>'In production', 'disabled'=>'Inactive')), "sql"=>"status", "default_value"=>"test", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list", array("linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"action_id", "ext_key_to_remote"=>"trigger_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
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('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 DoExecute($oTrigger, $aContextArgs);
|
||||
|
||||
public function IsActive()
|
||||
{
|
||||
switch($this->Get('status'))
|
||||
{
|
||||
case 'enabled':
|
||||
case 'test':
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function IsBeingTested()
|
||||
{
|
||||
switch($this->Get('status'))
|
||||
{
|
||||
case 'test':
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A notification
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class ActionNotification extends Action
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_action_notification",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
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('standard_search', array('name')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An email notification
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class ActionEmail extends ActionNotification
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_action_email",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeEmailAddress("test_recipient", array("allowed_values"=>null, "sql"=>"test_recipient", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("from", array("allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("reply_to", array("allowed_values"=>null, "sql"=>"reply_to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("to", array("allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("cc", array("allowed_values"=>null, "sql"=>"cc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeOQL("bcc", array("allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", array("allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new 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
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance', 'trigger_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
// count the recipients found
|
||||
protected $m_iRecipients;
|
||||
|
||||
// Errors management : not that simple because we need that function to be
|
||||
// executed in the background, while making sure that any issue would be reported clearly
|
||||
protected $m_aMailErrors; //array of strings explaining the issue
|
||||
|
||||
// returns a the list of emails as a string, or a detailed error description
|
||||
protected function FindRecipients($sRecipAttCode, $aArgs)
|
||||
{
|
||||
$sOQL = $this->Get($sRecipAttCode);
|
||||
if (strlen($sOQL) == '') return '';
|
||||
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
||||
}
|
||||
catch (OQLException $e)
|
||||
{
|
||||
$this->m_aMailErrors[] = "query syntax error for recipient '$sRecipAttCode'";
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$sClass = $oSearch->GetClass();
|
||||
// Determine the email attribute (the first one will be our choice)
|
||||
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef instanceof AttributeEmailAddress)
|
||||
{
|
||||
$sEmailAttCode = $sAttCode;
|
||||
// we've got one, exit the loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isset($sEmailAttCode))
|
||||
{
|
||||
$this->m_aMailErrors[] = "wrong target for recipient '$sRecipAttCode'";
|
||||
return "The objects of the class '$sClass' do not have any email attribute";
|
||||
}
|
||||
|
||||
$oSet = new DBObjectSet($oSearch, array() /* order */, $aArgs);
|
||||
$aRecipients = array();
|
||||
while ($oObj = $oSet->Fetch())
|
||||
{
|
||||
$aRecipients[] = $oObj->Get($sEmailAttCode);
|
||||
$this->m_iRecipients++;
|
||||
}
|
||||
return implode(', ', $aRecipients);
|
||||
}
|
||||
|
||||
|
||||
public function DoExecute($oTrigger, $aContextArgs)
|
||||
{
|
||||
$this->m_iRecipients = 0;
|
||||
$this->m_aMailErrors = array();
|
||||
$bRes = false; // until we do succeed in sending the email
|
||||
try
|
||||
{
|
||||
// Determine recicipients
|
||||
//
|
||||
$sTo = $this->FindRecipients('to', $aContextArgs);
|
||||
$sCC = $this->FindRecipients('cc', $aContextArgs);
|
||||
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
|
||||
|
||||
$sFrom = $this->Get('from');
|
||||
$sReplyTo = $this->Get('reply_to');
|
||||
|
||||
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
||||
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
|
||||
|
||||
$oObj = $aContextArgs['this->object()'];
|
||||
$sServerIP = $_SERVER['SERVER_ADDR']; //gethostbyname(gethostname());
|
||||
$sReference = '<iTop/'.get_class($oObj).'/'.$oObj->GetKey().'@'.$sServerIP.'>';
|
||||
|
||||
$oEmail = new EMail();
|
||||
|
||||
if ($this->IsBeingTested())
|
||||
{
|
||||
$oEmail->SetSubject('TEST['.$sSubject.']');
|
||||
$sTestBody = $sBody;
|
||||
$sTestBody .= "<div style=\"border: dashed;\">\n";
|
||||
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
|
||||
$sTestBody .= "<p>The email should be sent with the following properties\n";
|
||||
$sTestBody .= "<ul>\n";
|
||||
$sTestBody .= "<li>TO: $sTo</li>\n";
|
||||
$sTestBody .= "<li>CC: $sCC</li>\n";
|
||||
$sTestBody .= "<li>BCC: $sBCC</li>\n";
|
||||
$sTestBody .= "<li>From: $sFrom</li>\n";
|
||||
$sTestBody .= "<li>Reply-To: $sReplyTo</li>\n";
|
||||
$sTestBody .= "<li>References: $sReference</li>\n";
|
||||
$sTestBody .= "</ul>\n";
|
||||
$sTestBody .= "</p>\n";
|
||||
$sTestBody .= "</div>\n";
|
||||
$oEmail->SetBody($sTestBody);
|
||||
$oEmail->SetRecipientTO($this->Get('test_recipient'));
|
||||
$oEmail->SetRecipientFrom($this->Get('test_recipient'));
|
||||
$oEmail->SetReferences($sReference);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oEmail->SetSubject($sSubject);
|
||||
$oEmail->SetBody($sBody);
|
||||
$oEmail->SetRecipientTO($sTo);
|
||||
$oEmail->SetRecipientCC($sCC);
|
||||
$oEmail->SetRecipientBCC($sBCC);
|
||||
$oEmail->SetRecipientFrom($sFrom);
|
||||
$oEmail->SetRecipientReplyTo($sReplyTo);
|
||||
$oEmail->SetReferences($sReference);
|
||||
}
|
||||
|
||||
if (empty($this->m_aMailErrors))
|
||||
{
|
||||
if ($this->m_iRecipients == 0)
|
||||
{
|
||||
$this->m_aMailErrors[] = 'No recipient';
|
||||
}
|
||||
else
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->m_aMailErrors = array_merge($this->m_aMailErrors, $oEmail->Send());
|
||||
$oKPI->ComputeStats('Send mail', $sTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$this->m_aMailErrors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
if (MetaModel::IsLogEnabledNotification())
|
||||
{
|
||||
$oLog = new EventNotificationEmail();
|
||||
if (empty($this->m_aMailErrors))
|
||||
{
|
||||
if ($this->IsBeingTested())
|
||||
{
|
||||
$oLog->Set('message', 'TEST - Notification sent ('.$this->Get('test_recipient').')');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oLog->Set('message', 'Notification sent');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_array($this->m_aMailErrors) && count($this->m_aMailErrors) > 0)
|
||||
{
|
||||
$sError = implode(', ', $this->m_aMailErrors);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sError = 'Unknown reason';
|
||||
}
|
||||
if ($this->IsBeingTested())
|
||||
{
|
||||
$oLog->Set('message', 'TEST - Notification was not sent: '.$sError);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oLog->Set('message', 'Notification was not sent: '.$sError);
|
||||
}
|
||||
}
|
||||
$oLog->Set('userinfo', UserRights::GetUser());
|
||||
$oLog->Set('trigger_id', $oTrigger->GetKey());
|
||||
$oLog->Set('action_id', $this->GetKey());
|
||||
$oLog->Set('object_id', $aContextArgs['this->object()']->GetKey());
|
||||
|
||||
// Note: we have to secure this because those values are calculated
|
||||
// inside the try statement, and we would like to keep track of as
|
||||
// many data as we could while some variables may still be undefined
|
||||
if (isset($sTo)) $oLog->Set('to', $sTo);
|
||||
if (isset($sCC)) $oLog->Set('cc', $sCC);
|
||||
if (isset($sBCC)) $oLog->Set('bcc', $sBCC);
|
||||
if (isset($sFrom)) $oLog->Set('from', $sFrom);
|
||||
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
|
||||
if (isset($sBody)) $oLog->Set('body', $sBody);
|
||||
$oLog->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
334
core/archive.class.inc.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Utility to import/export the DB from/to a ZIP file
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* iTopArchive a class to manipulate (read/write) iTop archives with their catalog
|
||||
* Each iTop archive is a zip file that contains (at the root of the archive)
|
||||
* a file called catalog.xml holding the description of the archive
|
||||
*/
|
||||
class iTopArchive
|
||||
{
|
||||
const read = 0;
|
||||
const create = ZipArchive::CREATE;
|
||||
|
||||
protected $m_sZipPath;
|
||||
protected $m_oZip;
|
||||
protected $m_sVersion;
|
||||
protected $m_sTitle;
|
||||
protected $m_sDescription;
|
||||
protected $m_aPackages;
|
||||
protected $m_aErrorMessages;
|
||||
|
||||
/**
|
||||
* Construct an iTopArchive object
|
||||
* @param $sArchivePath string The full path the archive file
|
||||
* @param $iMode integrer Either iTopArchive::read for reading an existing archive or iTopArchive::create for creating a new one. Updating is not supported (yet)
|
||||
*/
|
||||
public function __construct($sArchivePath, $iMode = iTopArchive::read)
|
||||
{
|
||||
$this->m_sZipPath = $sArchivePath;
|
||||
$this->m_oZip = new ZipArchive();
|
||||
$this->m_oZip->open($this->m_sZipPath, $iMode);
|
||||
$this->m_aErrorMessages = array();
|
||||
$this->m_sVersion = '1.0';
|
||||
$this->m_sTitle = '';
|
||||
$this->m_sDescription = '';
|
||||
$this->m_aPackages = array();
|
||||
}
|
||||
|
||||
public function SetTitle($sTitle)
|
||||
{
|
||||
$this->m_sTitle = $sTitle;
|
||||
}
|
||||
|
||||
public function SetDescription($sDescription)
|
||||
{
|
||||
$this->m_sDescription = $sDescription;
|
||||
}
|
||||
|
||||
public function GetTitle()
|
||||
{
|
||||
return $this->m_sTitle;
|
||||
}
|
||||
|
||||
public function GetDescription()
|
||||
{
|
||||
return $this->m_sDescription;
|
||||
}
|
||||
|
||||
public function GetPackages()
|
||||
{
|
||||
return $this->m_aPackages;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->m_oZip->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error message explaining the latest error encountered
|
||||
* @return array All the error messages encountered during the validation
|
||||
*/
|
||||
public function GetErrors()
|
||||
{
|
||||
return $this->m_aErrorMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the catalog from the archive (zip) file
|
||||
* @param sPath string Path the the zip file
|
||||
* @return boolean True in case of success, false otherwise
|
||||
*/
|
||||
public function ReadCatalog()
|
||||
{
|
||||
if ($this->IsValid())
|
||||
{
|
||||
$sXmlCatalog = $this->m_oZip->getFromName('catalog.xml');
|
||||
$oParser = xml_parser_create();
|
||||
xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes);
|
||||
xml_parser_free($oParser);
|
||||
|
||||
$iIndex = $aIndexes['ARCHIVE'][0];
|
||||
$this->m_sVersion = $aValues[$iIndex]['attributes']['VERSION'];
|
||||
$iIndex = $aIndexes['TITLE'][0];
|
||||
$this->m_sTitle = $aValues[$iIndex]['value'];
|
||||
$iIndex = $aIndexes['DESCRIPTION'][0];
|
||||
if (array_key_exists('value', $aValues[$iIndex]))
|
||||
{
|
||||
// #@# implement a get_array_value(array, key, default) ?
|
||||
$this->m_sDescription = $aValues[$iIndex]['value'];
|
||||
}
|
||||
|
||||
foreach($aIndexes['PACKAGE'] as $iIndex)
|
||||
{
|
||||
$this->m_aPackages[$aValues[$iIndex]['attributes']['HREF']] = array( 'type' => $aValues[$iIndex]['attributes']['TYPE'], 'title'=> $aValues[$iIndex]['attributes']['TITLE'], 'description' => $aValues[$iIndex]['value']);
|
||||
}
|
||||
|
||||
//echo "Archive path: {$this->m_sZipPath}<br/>\n";
|
||||
//echo "Archive format version: {$this->m_sVersion}<br/>\n";
|
||||
//echo "Title: {$this->m_sTitle}<br/>\n";
|
||||
//echo "Description: {$this->m_sDescription}<br/>\n";
|
||||
//foreach($this->m_aPackages as $aFile)
|
||||
//{
|
||||
// echo "{$aFile['title']} ({$aFile['type']}): {$aFile['description']}<br/>\n";
|
||||
//}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function WriteCatalog()
|
||||
{
|
||||
$sXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?".">\n"; // split the XML closing tag that disturbs PSPad's syntax coloring
|
||||
$sXml .= "<archive version=\"1.0\">\n";
|
||||
$sXml .= "<title>{$this->m_sTitle}</title>\n";
|
||||
$sXml .= "<description>{$this->m_sDescription}</description>\n";
|
||||
foreach($this->m_aPackages as $sFileName => $aFile)
|
||||
{
|
||||
$sXml .= "<package title=\"{$aFile['title']}\" type=\"{$aFile['type']}\" href=\"$sFileName\">{$aFile['description']}</package>\n";
|
||||
}
|
||||
$sXml .= "</archive>";
|
||||
$this->m_oZip->addFromString('catalog.xml', $sXml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a package to the archive
|
||||
* @param string $sExternalFilePath The path to the file to be added to the archive as a package (directories are not yet implemented)
|
||||
* @param string $sFilePath The name of the file inside the archive
|
||||
* @param string $sTitle A short title for this package
|
||||
* @param string $sType Type of the package. SQL scripts must be of type 'text/sql'
|
||||
* @param string $sDescription A longer description of the purpose of this package
|
||||
* @return none
|
||||
*/
|
||||
public function AddPackage($sExternalFilePath, $sFilePath, $sTitle, $sType, $sDescription)
|
||||
{
|
||||
$this->m_aPackages[$sFilePath] = array('title' => $sTitle, 'type' => $sType, 'description' => $sDescription);
|
||||
$this->m_oZip->addFile($sExternalFilePath, $sFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of the given file from the archive
|
||||
* @param string $sFileName The path to the file inside the archive
|
||||
* @return string The content of the file read from the archive
|
||||
*/
|
||||
public function GetFileContents($sFileName)
|
||||
{
|
||||
return $this->m_oZip->getFromName($sFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the contents of the given file from the archive
|
||||
* @param string $sFileName The path to the file inside the archive
|
||||
* @param string $sDestinationFileName The path of the file to write
|
||||
* @return none
|
||||
*/
|
||||
public function ExtractToFile($sFileName, $sDestinationFileName)
|
||||
{
|
||||
$iBufferSize = 64 * 1024; // Read 64K at a time
|
||||
$oZipStream = $this->m_oZip->getStream($sFileName);
|
||||
$oDestinationStream = fopen($sDestinationFileName, 'wb');
|
||||
while (!feof($oZipStream)) {
|
||||
$sContents = fread($oZipStream, $iBufferSize);
|
||||
fwrite($oDestinationStream, $sContents);
|
||||
}
|
||||
fclose($oZipStream);
|
||||
fclose($oDestinationStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a SQL script taken from the archive. The package must be listed in the catalog and of type text/sql
|
||||
* @param string $sFileName The path to the SQL package inside the archive
|
||||
* @return boolean false in case of error, true otherwise
|
||||
*/
|
||||
public function ImportSql($sFileName, $sDatabase = 'itop')
|
||||
{
|
||||
if ( ($this->m_oZip->locateName($sFileName) == false) || (!isset($this->m_aPackages[$sFileName])) || ($this->m_aPackages[$sFileName]['type'] != 'text/sql'))
|
||||
{
|
||||
// invalid type or not listed in the catalog
|
||||
return false;
|
||||
}
|
||||
$sTempName = tempnam("../tmp/", "sql");
|
||||
//echo "Extracting to: '$sTempName'<br/>\n";
|
||||
$this->ExtractToFile($sFileName, $sTempName);
|
||||
// Note: the command line below works on Windows with the right path to mysql !!!
|
||||
$sCommandLine = 'type "'.$sTempName.'" | "/iTop/MySQL Server 5.0/bin/mysql.exe" -u root '.$sDatabase;
|
||||
//echo "Executing: '$sCommandLine'<br/>\n";
|
||||
exec($sCommandLine, $aOutput, $iRet);
|
||||
//echo "Return code: $iRet<br/>\n";
|
||||
//echo "Output:<br/><pre>\n";
|
||||
//print_r($aOutput);
|
||||
//echo "</pre><br/>\n";
|
||||
unlink($sTempName);
|
||||
return ($iRet == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps some part of the specified MySQL database into the archive as a text/sql package
|
||||
* @param $sTitle string A short title for this SQL script
|
||||
* @param $sDescription string A longer description of the purpose of this SQL script
|
||||
* @param $sFileName string The name of the package inside the archive
|
||||
* @param $sDatabase string name of the database
|
||||
* @param $aTables array array or table names. If empty, all tables are dumped
|
||||
* @param $bStructureOnly boolean Whether or not to dump the data or just the schema
|
||||
* @return boolean False in case of error, true otherwise
|
||||
*/
|
||||
public function AddDatabaseDump($sTitle, $sDescription, $sFileName, $sDatabase = 'itop', $aTables = array(), $bStructureOnly = true)
|
||||
{
|
||||
$sTempName = tempnam("../tmp/", "sql");
|
||||
$sNoData = $bStructureOnly ? "--no-data" : "";
|
||||
$sCommandLine = "\"/iTop/MySQL Server 5.0/bin/mysqldump.exe\" --user=root --opt $sNoData --result-file=$sTempName $sDatabase ".implode(" ", $aTables);
|
||||
//echo "Executing command: '$sCommandLine'<br/>\n";
|
||||
exec($sCommandLine, $aOutput, $iRet);
|
||||
//echo "Return code: $iRet<br/>\n";
|
||||
//echo "Output:<br/><pre>\n";
|
||||
//print_r($aOutput);
|
||||
//echo "</pre><br/>\n";
|
||||
if ($iRet == 0)
|
||||
{
|
||||
$this->AddPackage($sTempName, $sFileName, $sTitle, 'text/sql', $sDescription);
|
||||
}
|
||||
//unlink($sTempName);
|
||||
return ($iRet == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the consistency of the archive
|
||||
* @return boolean True if the archive file is consistent
|
||||
*/
|
||||
public function IsValid()
|
||||
{
|
||||
// TO DO: use a DTD to validate the XML instead of this hand-made validation
|
||||
$bResult = true;
|
||||
$aMandatoryTags = array('ARCHIVE' => array('VERSION'),
|
||||
'TITLE' => array(),
|
||||
'DESCRIPTION' => array(),
|
||||
'PACKAGE' => array('TYPE', 'HREF', 'TITLE'));
|
||||
|
||||
$sXmlCatalog = $this->m_oZip->getFromName('catalog.xml');
|
||||
$oParser = xml_parser_create();
|
||||
xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes);
|
||||
xml_parser_free($oParser);
|
||||
|
||||
foreach($aMandatoryTags as $sTag => $aAttributes)
|
||||
{
|
||||
// Check that all the required tags are present
|
||||
if (!isset($aIndexes[$sTag]))
|
||||
{
|
||||
$this->m_aErrorMessages[] = "The XML catalog does not contain the mandatory tag $sTag.";
|
||||
$bResult = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($aIndexes[$sTag] as $iIndex)
|
||||
{
|
||||
switch($aValues[$iIndex]['type'])
|
||||
{
|
||||
case 'complete':
|
||||
case 'open':
|
||||
// Check that all the required attributes are present
|
||||
foreach($aAttributes as $sAttribute)
|
||||
{
|
||||
if (!isset($aValues[$iIndex]['attributes'][$sAttribute]))
|
||||
{
|
||||
$this->m_aErrorMessages[] = "The tag $sTag ($iIndex) does not contain the required attribute $sAttribute.";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// ignore other type of tags: close or cdata
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
}
|
||||
/*
|
||||
// Unit test - reading an archive
|
||||
$sArchivePath = '../tmp/archive.zip';
|
||||
$oArchive = new iTopArchive($sArchivePath, iTopArchive::read);
|
||||
$oArchive->ReadCatalog();
|
||||
$oArchive->ImportSql('full_backup.sql');
|
||||
|
||||
// Writing an archive --
|
||||
|
||||
$sArchivePath = '../tmp/archive2.zip';
|
||||
$oArchive = new iTopArchive($sArchivePath, iTopArchive::create);
|
||||
$oArchive->SetTitle('First Archive !');
|
||||
$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.');
|
||||
$oArchive->AddPackage('../tmp/schema.sql', 'test.sql', 'this is just a test', 'text/sql', 'My first attempt at creating an archive from PHP...');
|
||||
$oArchive->WriteCatalog();
|
||||
|
||||
|
||||
$sArchivePath = '../tmp/archive2.zip';
|
||||
$oArchive = new iTopArchive($sArchivePath, iTopArchive::create);
|
||||
$oArchive->SetTitle('First Archive !');
|
||||
$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.');
|
||||
$oArchive->AddDatabaseDump('Test', 'This is my first automatic dump', 'schema.sql', 'itop', array('objects'));
|
||||
$oArchive->WriteCatalog();
|
||||
*/
|
||||
?>
|
||||
2380
core/attributedef.class.inc.php
Normal file
1063
core/bulkchange.class.inc.php
Normal file
82
core/cmdbchange.class.inc.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Persistent class (internal) cmdbChange
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A change as requested/validated at once by user, may groups many atomic changes
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChange extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "date",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_change",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
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())));
|
||||
}
|
||||
|
||||
// Helper to keep track of the author of a given change,
|
||||
// taking into account a variety of cases (contact attached or not, impersonation)
|
||||
static public function GetCurrentUserName()
|
||||
{
|
||||
if (UserRights::IsImpersonated())
|
||||
{
|
||||
$sUserString = Dict::Format('UI:Archive_User_OnBehalfOf_User', UserRights::GetRealUserFriendlyName(), UserRights::GetUserFriendlyName());
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUserString = UserRights::GetUserFriendlyName();
|
||||
}
|
||||
return $sUserString;
|
||||
}
|
||||
|
||||
public function GetUserName()
|
||||
{
|
||||
if (preg_match('/^(.*)\\(CSV\\)$/i', $this->Get('userinfo'), $aMatches))
|
||||
{
|
||||
$sUser = $aMatches[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sUser = $this->Get('userinfo');
|
||||
}
|
||||
return $sUser;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
478
core/cmdbchangeop.class.inc.php
Normal file
@@ -0,0 +1,478 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Persistent classes (internal) : cmdbChangeOp and derived
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Various atomic change operations, to be tracked
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
|
||||
class CMDBChangeOp extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "optype",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("change", array("allowed_values"=>null, "sql"=>"changeid", "targetclass"=>"CMDBChange", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
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 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
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Record the creation of an object
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpCreate extends CMDBChangeOp
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_create",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
return Dict::S('Change:ObjectCreated');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Record the deletion of an object
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpDelete extends CMDBChangeOp
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_delete",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
}
|
||||
/**
|
||||
* Describe (as a text string) the modifications corresponding to this change
|
||||
*/
|
||||
public function GetDescription()
|
||||
{
|
||||
return Dict::S('Change:ObjectDeleted');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Record the modification of an attribute (abstract)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpSetAttribute extends CMDBChangeOp
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_changeop_setatt",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>"", "is_null_allowed"=>false, "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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the modification of a scalar attribute
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeScalar 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_scalar",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("oldvalue", array("allowed_values"=>null, "sql"=>"oldvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("newvalue", array("allowed_values"=>null, "sql"=>"newvalue", "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()
|
||||
{
|
||||
// Temporary, until we change the options of GetDescription() -needs a more global revision
|
||||
$bIsHtml = true;
|
||||
|
||||
$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)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
$sNewValue = $this->Get('newvalue');
|
||||
$sOldValue = $this->Get('oldvalue');
|
||||
if ( (($oAttDef->GetType() == 'String') || ($oAttDef->GetType() == 'Text')) &&
|
||||
(strlen($sNewValue) > strlen($sOldValue)) )
|
||||
{
|
||||
// Check if some text was not appended to the field
|
||||
if (substr($sNewValue,0, strlen($sOldValue)) == $sOldValue) // Text added at the end
|
||||
{
|
||||
$sDelta = substr($sNewValue, strlen($sOldValue));
|
||||
$sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sAttName);
|
||||
}
|
||||
else if (substr($sNewValue, -strlen($sOldValue)) == $sOldValue) // Text added at the beginning
|
||||
{
|
||||
$sDelta = substr($sNewValue, 0, strlen($sNewValue) - strlen($sOldValue));
|
||||
$sResult = Dict::Format('Change:Text_AppendedTo_AttName', $sDelta, $sAttName);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sAttName, $sNewValue, $sOldValue);
|
||||
}
|
||||
}
|
||||
elseif($bIsHtml && $oAttDef->IsExternalKey())
|
||||
{
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$sFrom = MetaModel::GetHyperLink($sTargetClass, $sOldValue);
|
||||
$sTo = MetaModel::GetHyperLink($sTargetClass, $sNewValue);
|
||||
$sResult = "$sAttName set to $sTo (previous: $sFrom)";
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBlob)
|
||||
{
|
||||
$sResult = "#@# Issue... found an attribute for which other type of tracking should be made";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sResult = Dict::Format('Change:AttName_SetTo_NewValue_PreviousValue_OldValue', $sAttName, $sNewValue, $sOldValue);
|
||||
}
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the modification of a blob
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeBlob 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_data",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeBlob("prevdata", array("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()
|
||||
{
|
||||
// Temporary, until we change the options of GetDescription() -needs a more global revision
|
||||
$bIsHtml = true;
|
||||
|
||||
$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)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
$oPrevDoc = $this->Get('prevdata');
|
||||
$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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Safely record the modification of one way encrypted password
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeOneWayPassword 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_pwd",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("prev_pwd", array("sql" => 'data', "default_value" => '', "is_null_allowed"=> true, "allowed_values" => null, "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()
|
||||
{
|
||||
// Temporary, until we change the options of GetDescription() -needs a more global revision
|
||||
$bIsHtml = true;
|
||||
|
||||
$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)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
$sResult = Dict::Format('Change:AttName_Changed', $sAttName);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely record the modification of an encrypted field
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeEncrypted 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_encrypted",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeEncryptedString("prevstring", array("sql" => 'data', "default_value" => '', "is_null_allowed"=> true, "allowed_values" => null, "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()
|
||||
{
|
||||
// Temporary, until we change the options of GetDescription() -needs a more global revision
|
||||
$bIsHtml = true;
|
||||
|
||||
$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)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the modification of a multiline string (text)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBChangeOpSetAttributeText 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_text",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeText("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()
|
||||
{
|
||||
// Temporary, until we change the options of GetDescription() -needs a more global revision
|
||||
$bIsHtml = true;
|
||||
|
||||
$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)
|
||||
{
|
||||
$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');
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
533
core/cmdbobject.class.inc.php
Normal file
@@ -0,0 +1,533 @@
|
||||
<?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 cmdbObject
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* cmdbObjectClass
|
||||
* the file to include, then the core is yours
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
|
||||
require_once('coreexception.class.inc.php');
|
||||
|
||||
require_once('config.class.inc.php');
|
||||
require_once('log.class.inc.php');
|
||||
require_once('kpi.class.inc.php');
|
||||
|
||||
require_once('dict.class.inc.php');
|
||||
|
||||
require_once('attributedef.class.inc.php');
|
||||
require_once('filterdef.class.inc.php');
|
||||
require_once('stimulus.class.inc.php');
|
||||
require_once('valuesetdef.class.inc.php');
|
||||
require_once('MyHelpers.class.inc.php');
|
||||
|
||||
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('dbobject.class.php');
|
||||
require_once('dbobjectsearch.class.php');
|
||||
require_once('dbobjectset.class.php');
|
||||
|
||||
require_once('dbproperty.class.inc.php');
|
||||
|
||||
// db change tracking data model
|
||||
require_once('cmdbchange.class.inc.php');
|
||||
require_once('cmdbchangeop.class.inc.php');
|
||||
|
||||
// customization data model
|
||||
// Romain: temporary moved into application.inc.php (see explanations there)
|
||||
//require_once('trigger.class.inc.php');
|
||||
//require_once('action.class.inc.php');
|
||||
|
||||
// application log
|
||||
// Romain: temporary moved into application.inc.php (see explanations there)
|
||||
//require_once('event.class.inc.php');
|
||||
|
||||
require_once('csvparser.class.inc.php');
|
||||
require_once('bulkchange.class.inc.php');
|
||||
|
||||
/**
|
||||
* A persistent object, which changes are accurately recorded
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class CMDBObject extends DBObject
|
||||
{
|
||||
protected $m_datCreated;
|
||||
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;
|
||||
|
||||
|
||||
private function RecordObjCreation(CMDBChange $oChange)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
private function RecordObjDeletion(CMDBChange $oChange, $objkey)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpDelete");
|
||||
$oMyChangeOp->Set("change", $oChange->GetKey());
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $objkey);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
private function RecordAttChanges(CMDBChange $oChange, array $aValues, array $aOrigValues)
|
||||
{
|
||||
// $aValues is an array of $sAttCode => $value
|
||||
//
|
||||
foreach ($aValues as $sAttCode=> $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsLinkSet()) continue; // #@# temporary
|
||||
|
||||
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 (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
$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 (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
$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 (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
$original = new ormDocument();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$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 (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
$original = null;
|
||||
}
|
||||
$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);
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$sOriginalValue = $aOrigValues[$sAttCode];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOriginalValue = 'undefined';
|
||||
}
|
||||
$oMyChangeOp->Set("oldvalue", $sOriginalValue);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to ultimately check user rights before writing (Insert, Update or Delete)
|
||||
* The check should never fail, because the UI should prevent from such a usage
|
||||
* Anyhow, if the user has found a workaround... the security gets enforced here
|
||||
*/
|
||||
protected function CheckUserRights($bSkipStrongSecurity, $iActionCode)
|
||||
{
|
||||
if (is_null($bSkipStrongSecurity))
|
||||
{
|
||||
// This is temporary
|
||||
// We have implemented this safety net right before releasing iTop 1.0
|
||||
// and we decided that it was too risky to activate it
|
||||
// Anyhow, users willing to have a very strong security could set
|
||||
// skip_strong_security = 0, in the config file
|
||||
$bSkipStrongSecurity = MetaModel::GetConfig()->Get('skip_strong_security');
|
||||
}
|
||||
if (!$bSkipStrongSecurity)
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
$oSet = DBObjectSet::FromObject($this);
|
||||
if (!UserRights::IsActionAllowed($sClass, $iActionCode, $oSet))
|
||||
{
|
||||
// Intrusion detected
|
||||
throw new SecurityException('You are not allowed to modify objects of class: '.$sClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function 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)
|
||||
{
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
|
||||
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$ret = $this->DBInsertTracked_Internal();
|
||||
self::$m_oCurrChange = null;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function DBInsertTrackedNoReload(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
||||
{
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
|
||||
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$ret = $this->DBInsertTracked_Internal(true);
|
||||
self::$m_oCurrChange = null;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
protected function DBInsertTracked_Internal($bDoNotReload = false)
|
||||
{
|
||||
if ($bDoNotReload)
|
||||
{
|
||||
$ret = parent::DBInsertNoReload();
|
||||
}
|
||||
else
|
||||
{
|
||||
$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::$m_oCurrChange = $oChange;
|
||||
$this->DBCloneTracked_Internal($newKey);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected function DBCloneTracked_Internal($newKey = null)
|
||||
{
|
||||
$newKey = parent::DBClone($newKey);
|
||||
$oClone = MetaModel::GetObject(get_class($this), $newKey);
|
||||
|
||||
$oClone->RecordObjCreation(self::$m_oCurrChange);
|
||||
return $newKey;
|
||||
}
|
||||
|
||||
public function DBUpdate()
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
throw new CoreException("DBUpdate() could not be used here, please use DBUpdateTracked() instead");
|
||||
}
|
||||
return $this->DBUpdateTracked_internal();
|
||||
}
|
||||
|
||||
public function DBUpdateTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
||||
{
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_MODIFY);
|
||||
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBUpdateTracked_Internal();
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
if(!self::$m_oCurrChange)
|
||||
{
|
||||
throw new CoreException("DBDelete() could not be used here, please use DBDeleteTracked() instead");
|
||||
}
|
||||
return $this->DBDeleteTracked_Internal();
|
||||
}
|
||||
|
||||
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null)
|
||||
{
|
||||
$this->CheckUserRights($bSkipStrongSecurity, UR_ACTION_DELETE);
|
||||
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->DBDeleteTracked_Internal();
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected function DBDeleteTracked_Internal()
|
||||
{
|
||||
$prevkey = $this->GetKey();
|
||||
$ret = parent::DBDelete();
|
||||
$this->RecordObjDeletion(self::$m_oCurrChange, $prevkey);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public static function BulkDelete(DBObjectSearch $oFilter)
|
||||
{
|
||||
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 BulkDeleteTracked(CMDBChange $oChange, DBObjectSearch $oFilter)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->BulkDeleteTracked_Internal($oFilter);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
self::$m_oCurrChange = $oChange;
|
||||
$this->BulkUpdateTracked_Internal($oFilter, $aValues);
|
||||
self::$m_oCurrChange = null;
|
||||
}
|
||||
|
||||
protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues)
|
||||
{
|
||||
// $aValues is an array of $sAttCode => $value
|
||||
|
||||
// Get the list of objects to update (and load it before doing the change)
|
||||
$oObjSet = new CMDBObjectSet($oFilter);
|
||||
$oObjSet->Load();
|
||||
|
||||
// Keep track of the previous values (will be overwritten when the objects are synchronized with the DB)
|
||||
$aOriginalValues = array();
|
||||
$oObjSet->Rewind();
|
||||
while ($oItem = $oObjSet->Fetch())
|
||||
{
|
||||
$aOriginalValues[$oItem->GetKey()] = $oItem->m_aOrigValues;
|
||||
}
|
||||
|
||||
// Update in one single efficient query
|
||||
$ret = parent::BulkUpdate($oFilter, $aValues);
|
||||
|
||||
// Record... in many queries !!!
|
||||
$oObjSet->Rewind();
|
||||
while ($oItem = $oObjSet->Fetch())
|
||||
{
|
||||
$aChangedValues = $oItem->ListChangedValues($aValues);
|
||||
$oItem->RecordAttChanges(self::$m_oCurrChange, $aChangedValues, $aOriginalValues[$oItem->GetKey()]);
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design!
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBObjectSet extends DBObjectSet
|
||||
{
|
||||
// this is the public interface (?)
|
||||
|
||||
// I have to define those constructors here... :-(
|
||||
// just to get the right object class in return.
|
||||
// I have to think again to those things: maybe it will work fine if a have a constructor define here (?)
|
||||
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE
|
||||
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private...
|
||||
// BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!!
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
static public function FromArray($sClass, $aObjects)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE
|
||||
// NOTE: THIS DOES NOT WORK IF m_bLoaded is private...
|
||||
// BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!!
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
$oRetSet->AddObjectArray($aObjects);
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
static public function FromArrayAssoc($aClasses, $aObjects)
|
||||
{
|
||||
// In a perfect world, we should create a complete tree of DBObjectSearch,
|
||||
// but as we lack most of the information related to the objects,
|
||||
// let's create one search definition
|
||||
$sClass = reset($aClasses);
|
||||
$sAlias = key($aClasses);
|
||||
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
|
||||
|
||||
$oRetSet = new CMDBObjectSet($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
|
||||
foreach($aObjects as $rowIndex => $aObjectsByClassAlias)
|
||||
{
|
||||
$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 (?)
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
584
core/cmdbsource.class.inc.php
Normal file
@@ -0,0 +1,584 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* DB Server abstraction
|
||||
*
|
||||
* @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('MyHelpers.class.inc.php');
|
||||
|
||||
class MySQLException extends CoreException
|
||||
{
|
||||
public function __construct($sIssue, $aContext)
|
||||
{
|
||||
$aContext['mysql_error'] = mysql_error();
|
||||
$aContext['mysql_errno'] = mysql_errno();
|
||||
parent::__construct($sIssue, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CMDBSource
|
||||
* database access wrapper
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CMDBSource
|
||||
{
|
||||
protected static $m_sDBHost;
|
||||
protected static $m_sDBUser;
|
||||
protected static $m_sDBPwd;
|
||||
protected static $m_sDBName;
|
||||
protected static $m_resDBLink;
|
||||
|
||||
public static function Init($sServer, $sUser, $sPwd, $sSource = '')
|
||||
{
|
||||
self::$m_sDBHost = $sServer;
|
||||
self::$m_sDBUser = $sUser;
|
||||
self::$m_sDBPwd = $sPwd;
|
||||
self::$m_sDBName = $sSource;
|
||||
if (!self::$m_resDBLink = @mysql_pconnect($sServer, $sUser, $sPwd))
|
||||
{
|
||||
throw new MySQLException('Could not connect to the DB server', array('host'=>$sServer, 'user'=>$sUser));
|
||||
}
|
||||
if (!empty($sSource))
|
||||
{
|
||||
if (!mysql_select_db($sSource, self::$m_resDBLink))
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('host'=>$sServer, 'user'=>$sUser, 'db_name'=>$sSource));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function SetCharacterSet($sCharset = 'utf8', $sCollation = 'utf8_general_ci')
|
||||
{
|
||||
if (strlen($sCharset) > 0)
|
||||
{
|
||||
if (strlen($sCollation) > 0)
|
||||
{
|
||||
self::Query("SET NAMES '$sCharset' COLLATE '$sCollation'");
|
||||
}
|
||||
else
|
||||
{
|
||||
self::Query("SET NAMES '$sCharset'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function ListDB()
|
||||
{
|
||||
$aDBs = self::QueryToCol('SHOW DATABASES', 'Database');
|
||||
// Show Database does return the DB names in lower case
|
||||
return $aDBs;
|
||||
}
|
||||
|
||||
public static function IsDB($sSource)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aDBs = self::ListDB();
|
||||
foreach($aDBs as $sDBName)
|
||||
{
|
||||
// perform a case insensitive test because on Windows the table names become lowercase :-(
|
||||
if (strtolower($sDBName) == strtolower($sSource)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
// In case we don't have rights to enumerate the databases
|
||||
// Let's try to connect directly
|
||||
return @mysql_select_db($sSource, self::$m_resDBLink);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static function GetDBVersion()
|
||||
{
|
||||
$aVersions = self::QueryToCol('SELECT Version() as version', 'version');
|
||||
return $aVersions[0];
|
||||
}
|
||||
|
||||
public static function SelectDB($sSource)
|
||||
{
|
||||
if (!mysql_select_db($sSource, self::$m_resDBLink))
|
||||
{
|
||||
throw new MySQLException('Could not select DB', array('db_name'=>$sSource));
|
||||
}
|
||||
self::$m_sDBName = $sSource;
|
||||
}
|
||||
|
||||
public static function CreateDB($sSource)
|
||||
{
|
||||
self::Query("CREATE DATABASE `$sSource` CHARACTER SET utf8 COLLATE utf8_unicode_ci");
|
||||
self::SelectDB($sSource);
|
||||
}
|
||||
|
||||
public static function DropDB($sDBToDrop = '')
|
||||
{
|
||||
if (empty($sDBToDrop))
|
||||
{
|
||||
$sDBToDrop = self::$m_sDBName;
|
||||
}
|
||||
self::Query("DROP DATABASE `$sDBToDrop`");
|
||||
if ($sDBToDrop == self::$m_sDBName)
|
||||
{
|
||||
self::$m_sDBName = '';
|
||||
}
|
||||
}
|
||||
|
||||
public static function CreateTable($sQuery)
|
||||
{
|
||||
$res = self::Query($sQuery);
|
||||
self::_TablesInfoCacheReset(); // reset the table info cache!
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function DropTable($sTable)
|
||||
{
|
||||
$res = self::Query("DROP TABLE `$sTable`");
|
||||
self::_TablesInfoCacheReset(true); // reset the table info cache!
|
||||
return $res;
|
||||
}
|
||||
|
||||
public static function DBHost() {return self::$m_sDBHost;}
|
||||
public static function DBUser() {return self::$m_sDBUser;}
|
||||
public static function DBPwd() {return self::$m_sDBPwd;}
|
||||
public static function DBName() {return self::$m_sDBName;}
|
||||
|
||||
public static function Quote($value, $bAlways = false, $cQuoteStyle = "'")
|
||||
{
|
||||
// Quote variable and protect against SQL injection attacks
|
||||
// Code found in the PHP documentation: quote_smart($value)
|
||||
|
||||
// bAlways should be set to true when the purpose is to create a IN clause,
|
||||
// otherwise and if there is a mix of strings and numbers, the clause
|
||||
// would always be false
|
||||
|
||||
if (is_null($value))
|
||||
{
|
||||
return 'NULL';
|
||||
}
|
||||
|
||||
if (is_array($value))
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($value as $key => $itemvalue)
|
||||
{
|
||||
$aRes[$key] = self::Quote($itemvalue, $bAlways, $cQuoteStyle);
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
// Stripslashes
|
||||
if (get_magic_quotes_gpc())
|
||||
{
|
||||
$value = stripslashes($value);
|
||||
}
|
||||
// Quote if not a number or a numeric string
|
||||
if ($bAlways || is_string($value))
|
||||
{
|
||||
$value = $cQuoteStyle . mysql_real_escape_string($value, self::$m_resDBLink) . $cQuoteStyle;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
public static function Query($sSQLQuery)
|
||||
{
|
||||
// Add info into the query as a comment, for easier error tracking
|
||||
// disabled until we need it really!
|
||||
//
|
||||
//$aTraceInf['file'] = __FILE__;
|
||||
// $sSQLQuery .= MyHelpers::MakeSQLComment($aTraceInf);
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
$result = mysql_query($sSQLQuery, self::$m_resDBLink);
|
||||
if (!$result)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery));
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSQLQuery);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public static function GetNextInsertId($sTable)
|
||||
{
|
||||
$sSQL = "SHOW TABLE STATUS LIKE '$sTable'";
|
||||
$result = self::Query($sSQL);
|
||||
$aRow = mysql_fetch_assoc($result);
|
||||
$iNextInsertId = $aRow['Auto_increment'];
|
||||
return $iNextInsertId;
|
||||
}
|
||||
|
||||
public static function GetInsertId()
|
||||
{
|
||||
return mysql_insert_id(self::$m_resDBLink);
|
||||
}
|
||||
public static function InsertInto($sSQLQuery)
|
||||
{
|
||||
if (self::Query($sSQLQuery))
|
||||
{
|
||||
return self::GetInsertId();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function QueryToArray($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
$result = mysql_query($sSql, self::$m_resDBLink);
|
||||
if (!$result)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
while ($aRow = mysql_fetch_array($result, MYSQL_BOTH))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
mysql_free_result($result);
|
||||
return $aData;
|
||||
}
|
||||
|
||||
public static function QueryToCol($sSql, $col)
|
||||
{
|
||||
$aColumn = array();
|
||||
$aData = self::QueryToArray($sSql);
|
||||
foreach($aData as $aRow)
|
||||
{
|
||||
@$aColumn[] = $aRow[$col];
|
||||
}
|
||||
return $aColumn;
|
||||
}
|
||||
|
||||
public static function ExplainQuery($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
$result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink);
|
||||
if (!$result)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
$aNames = self::GetColumns($result);
|
||||
|
||||
$aData[] = $aNames;
|
||||
while ($aRow = mysql_fetch_array($result, MYSQL_ASSOC))
|
||||
{
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
mysql_free_result($result);
|
||||
return $aData;
|
||||
}
|
||||
|
||||
public static function TestQuery($sSql)
|
||||
{
|
||||
$result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink);
|
||||
if (!$result)
|
||||
{
|
||||
return mysql_error();
|
||||
}
|
||||
|
||||
mysql_free_result($result);
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function NbRows($result)
|
||||
{
|
||||
return mysql_num_rows($result);
|
||||
}
|
||||
|
||||
public static function FetchArray($result)
|
||||
{
|
||||
return mysql_fetch_array($result, MYSQL_ASSOC);
|
||||
}
|
||||
|
||||
public static function GetColumns($result)
|
||||
{
|
||||
$aNames = array();
|
||||
for ($i = 0; $i < mysql_num_fields($result) ; $i++)
|
||||
{
|
||||
$meta = mysql_fetch_field($result, $i);
|
||||
if (!$meta)
|
||||
{
|
||||
throw new MySQLException('mysql_fetch_field: No information available', array('query'=>$sSql, 'i'=>$i));
|
||||
}
|
||||
else
|
||||
{
|
||||
$aNames[] = $meta->name;
|
||||
}
|
||||
}
|
||||
return $aNames;
|
||||
}
|
||||
|
||||
public static function Seek($result, $iRow)
|
||||
{
|
||||
return mysql_data_seek($result, $iRow);
|
||||
}
|
||||
|
||||
public static function FreeResult($result)
|
||||
{
|
||||
return mysql_free_result($result);
|
||||
}
|
||||
|
||||
public static function IsTable($sTable)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
return (!empty($aTableInfo));
|
||||
}
|
||||
|
||||
public static function IsKey($sTable, $iKey)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($iKey, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$iKey];
|
||||
if (!array_key_exists("Key", $aFieldData)) return false;
|
||||
return ($aFieldData["Key"] == "PRI");
|
||||
}
|
||||
|
||||
public static function IsAutoIncrement($sTable, $sField)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
||||
if (!array_key_exists("Extra", $aFieldData)) return false;
|
||||
//MyHelpers::debug_breakpoint($aFieldData);
|
||||
return (strstr($aFieldData["Extra"], "auto_increment"));
|
||||
}
|
||||
|
||||
public static function IsField($sTable, $sField)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function IsNullAllowed($sTable, $sField)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
||||
return (strtolower($aFieldData["Null"]) == "yes");
|
||||
}
|
||||
|
||||
public static function GetFieldType($sTable, $sField)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
||||
return ($aFieldData["Type"]);
|
||||
}
|
||||
|
||||
public static function HasIndex($sTable, $sField)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
||||
// $aFieldData could be 'PRI' for the primary key, or 'MUL', or ?
|
||||
return (strlen($aFieldData["Key"]) > 0);
|
||||
}
|
||||
|
||||
// Returns an array of (fieldname => array of field info)
|
||||
public static function GetTableFieldsList($sTable)
|
||||
{
|
||||
assert(!empty($sTable));
|
||||
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return array(); // #@# or an error ?
|
||||
|
||||
return array_keys($aTableInfo["Fields"]);
|
||||
}
|
||||
|
||||
// Cache the information about existing tables, and their fields
|
||||
private static $m_aTablesInfo = array();
|
||||
private static function _TablesInfoCacheReset()
|
||||
{
|
||||
self::$m_aTablesInfo = array();
|
||||
}
|
||||
private static function _TableInfoCacheInit($sTableName)
|
||||
{
|
||||
if (isset(self::$m_aTablesInfo[strtolower($sTableName)])
|
||||
&& (self::$m_aTablesInfo[strtolower($sTableName)] != null)) return;
|
||||
|
||||
try
|
||||
{
|
||||
// Check if the table exists
|
||||
$aFields = self::QueryToArray("SHOW COLUMNS FROM `$sTableName`");
|
||||
// Note: without backticks, you get an error with some table names (e.g. "group")
|
||||
foreach ($aFields as $aFieldData)
|
||||
{
|
||||
$sFieldName = $aFieldData["Field"];
|
||||
self::$m_aTablesInfo[strtolower($sTableName)]["Fields"][$sFieldName] =
|
||||
array
|
||||
(
|
||||
"Name"=>$aFieldData["Field"],
|
||||
"Type"=>$aFieldData["Type"],
|
||||
"Null"=>$aFieldData["Null"],
|
||||
"Key"=>$aFieldData["Key"],
|
||||
"Default"=>$aFieldData["Default"],
|
||||
"Extra"=>$aFieldData["Extra"]
|
||||
);
|
||||
}
|
||||
}
|
||||
catch(MySQLException $e)
|
||||
{
|
||||
// Table does not exist
|
||||
self::$m_aTablesInfo[strtolower($sTableName)] = null;
|
||||
}
|
||||
}
|
||||
//public static function EnumTables()
|
||||
//{
|
||||
// self::_TablesInfoCacheInit();
|
||||
// return array_keys(self::$m_aTablesInfo);
|
||||
//}
|
||||
public static function GetTableInfo($sTable)
|
||||
{
|
||||
self::_TableInfoCacheInit($sTable);
|
||||
|
||||
// perform a case insensitive match because on Windows the table names become lowercase :-(
|
||||
//foreach(self::$m_aTablesInfo as $sTableName => $aInfo)
|
||||
//{
|
||||
// if (strtolower($sTableName) == strtolower($sTable))
|
||||
// {
|
||||
// return $aInfo;
|
||||
// }
|
||||
//}
|
||||
return self::$m_aTablesInfo[strtolower($sTable)];
|
||||
//return null;
|
||||
}
|
||||
|
||||
public static function DumpTable($sTable)
|
||||
{
|
||||
$sSql = "SELECT * FROM `$sTable`";
|
||||
$result = mysql_query($sSql, self::$m_resDBLink);
|
||||
if (!$result)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
$aRows = array();
|
||||
while ($aRow = mysql_fetch_array($result, MYSQL_ASSOC))
|
||||
{
|
||||
$aRows[] = $aRow;
|
||||
}
|
||||
mysql_free_result($result);
|
||||
return $aRows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the specified server variable
|
||||
* @param string $sVarName Name of the server variable
|
||||
* @return mixed Current value of the variable
|
||||
*/
|
||||
public static function GetServerVariable($sVarName)
|
||||
{
|
||||
$result = '';
|
||||
$sSql = "SELECT @@$sVarName as theVar";
|
||||
$aRows = self::QueryToArray($sSql);
|
||||
if (count($aRows) > 0)
|
||||
{
|
||||
$result = $aRows[0]['theVar'];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the privileges of the current user
|
||||
* @return string privileges in a raw format
|
||||
*/
|
||||
public static function GetRawPrivileges()
|
||||
{
|
||||
try
|
||||
{
|
||||
$result = self::Query('SHOW GRANTS'); // [ FOR CURRENT_USER()]
|
||||
}
|
||||
catch(MySQLException $e)
|
||||
{
|
||||
return "Current user not allowed to see his own privileges (could not access to the database 'mysql' - $iCode)";
|
||||
}
|
||||
|
||||
$aRes = array();
|
||||
while ($aRow = mysql_fetch_array($result, MYSQL_NUM))
|
||||
{
|
||||
// so far, only one column...
|
||||
$aRes[] = implode('/', $aRow);
|
||||
}
|
||||
mysql_free_result($result);
|
||||
// so far, only one line...
|
||||
return implode(', ', $aRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the slave status of the server
|
||||
* @return bool true if the server is slave
|
||||
*/
|
||||
public static function IsSlaveServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
$result = self::Query('SHOW SLAVE STATUS');
|
||||
}
|
||||
catch(MySQLException $e)
|
||||
{
|
||||
throw new CoreException("Current user not allowed to check the status", array('mysql_error' => $e->getMessage()));
|
||||
}
|
||||
|
||||
if (mysql_num_rows($result) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns one single row anytime
|
||||
$aRow = mysql_fetch_array($result, MYSQL_ASSOC);
|
||||
mysql_free_result($result);
|
||||
|
||||
if (!isset($aRow['Slave_IO_Running']))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!isset($aRow['Slave_SQL_Running']))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If at least one slave thread is running, then we consider that the slave is enabled
|
||||
if ($aRow['Slave_IO_Running'] == 'Yes')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if ($aRow['Slave_SQL_Running'] == 'Yes')
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
1037
core/config.class.inc.php
Normal file
114
core/coreexception.class.inc.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Exception management
|
||||
*
|
||||
* @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 CoreException extends Exception
|
||||
{
|
||||
public function __construct($sIssue, $aContextData = null, $sImpact = '')
|
||||
{
|
||||
$this->m_sIssue = $sIssue;
|
||||
$this->m_sImpact = $sImpact;
|
||||
$this->m_aContextData = $aContextData ? $aContextData : array();
|
||||
|
||||
$sMessage = $sIssue;
|
||||
if (!empty($sImpact)) $sMessage .= "($sImpact)";
|
||||
if (count($this->m_aContextData) > 0)
|
||||
{
|
||||
$sMessage .= ": ";
|
||||
$aContextItems = array();
|
||||
foreach($this->m_aContextData as $sKey => $value)
|
||||
{
|
||||
if (is_array($value))
|
||||
{
|
||||
$aPairs = array();
|
||||
foreach($value as $key => $val)
|
||||
{
|
||||
if (is_array($val))
|
||||
{
|
||||
$aPairs[] = $key.'=>('.implode(', ', $val).')';
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPairs[] = $key.'=>'.$val;
|
||||
}
|
||||
}
|
||||
$sValue = '{'.implode('; ', $aPairs).'}';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sValue = $value;
|
||||
}
|
||||
$aContextItems[] = "$sKey = $sValue";
|
||||
}
|
||||
$sMessage .= implode(', ', $aContextItems);
|
||||
}
|
||||
parent::__construct($sMessage, 0);
|
||||
}
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
|
||||
{
|
||||
return $this->getMessage();
|
||||
}
|
||||
|
||||
public function getTraceAsHtml()
|
||||
{
|
||||
$aBackTrace = $this->getTrace();
|
||||
return MyHelpers::get_callstack_html(0, $this->getTrace());
|
||||
// return "<pre>\n".$this->getTraceAsString()."</pre>\n";
|
||||
}
|
||||
|
||||
public function addInfo($sKey, $value)
|
||||
{
|
||||
$this->m_aContextData[$sKey] = $value;
|
||||
}
|
||||
|
||||
public function getIssue()
|
||||
{
|
||||
return $this->m_sIssue;
|
||||
}
|
||||
public function getImpact()
|
||||
{
|
||||
return $this->m_sImpact;
|
||||
}
|
||||
public function getContextData()
|
||||
{
|
||||
return $this->m_aContextData;
|
||||
}
|
||||
}
|
||||
|
||||
class CoreWarning extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
class CoreUnexpectedValue extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
class SecurityException extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
243
core/csvparser.class.inc.php
Normal file
@@ -0,0 +1,243 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* CSV parser
|
||||
*
|
||||
* @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 CSVParserException extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
define('stSTARTING', 1); //grey zone: the type is undetermined
|
||||
define('stRAW', 2); //building a non-qualified string
|
||||
define('stQUALIFIED', 3); //building qualified string
|
||||
define('stESCAPED', 4); //just encountered an escape char
|
||||
|
||||
define('evBLANK', 0);
|
||||
define('evSEPARATOR', 1);
|
||||
define('evNEWLINE', 2);
|
||||
define('evTEXTQUAL', 3); // used for escaping as well
|
||||
define('evOTHERCHAR', 4);
|
||||
define('evEND', 5);
|
||||
|
||||
|
||||
/**
|
||||
* CSVParser
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class CSVParser
|
||||
{
|
||||
private $m_sCSVData;
|
||||
private $m_sSep;
|
||||
private $m_sTextQualifier;
|
||||
|
||||
public function __construct($sTxt, $sSep = ',', $sTextQualifier = '"')
|
||||
{
|
||||
$this->m_sCSVData = str_replace("\r\n", "\n", $sTxt);
|
||||
$this->m_sSep = $sSep;
|
||||
$this->m_sTextQualifier = $sTextQualifier;
|
||||
}
|
||||
|
||||
protected $m_sCurrCell = '';
|
||||
protected $m_aCurrRow = array();
|
||||
protected $m_iToSkip = 0;
|
||||
protected $m_aDataSet = array();
|
||||
|
||||
protected function __AddChar($c)
|
||||
{
|
||||
$this->m_sCurrCell .= $c;
|
||||
}
|
||||
protected function __ClearCell()
|
||||
{
|
||||
$this->m_sCurrCell = '';
|
||||
}
|
||||
protected function __AddCell($c = null, $aFieldMap = null, $bTrimSpaces = false)
|
||||
{
|
||||
if ($bTrimSpaces)
|
||||
{
|
||||
$sCell = trim($this->m_sCurrCell);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sCell = $this->m_sCurrCell;
|
||||
}
|
||||
|
||||
if (!is_null($aFieldMap))
|
||||
{
|
||||
$iNextCol = count($this->m_aCurrRow);
|
||||
$iNextName = $aFieldMap[$iNextCol];
|
||||
$this->m_aCurrRow[$iNextName] = $sCell;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aCurrRow[] = $sCell;
|
||||
}
|
||||
$this->m_sCurrCell = '';
|
||||
}
|
||||
protected function __AddRow($c = null, $aFieldMap = null, $bTrimSpaces = false)
|
||||
{
|
||||
$this->__AddCell($c, $aFieldMap, $bTrimSpaces);
|
||||
|
||||
if ($this->m_iToSkip > 0)
|
||||
{
|
||||
$this->m_iToSkip--;
|
||||
}
|
||||
elseif (count($this->m_aCurrRow) > 1)
|
||||
{
|
||||
$this->m_aDataSet[] = $this->m_aCurrRow;
|
||||
}
|
||||
elseif (count($this->m_aCurrRow) == 1)
|
||||
{
|
||||
// Get the unique value
|
||||
$aValues = array_values($this->m_aCurrRow);
|
||||
$sValue = $aValues[0];
|
||||
if (strlen($sValue) > 0)
|
||||
{
|
||||
$this->m_aDataSet[] = $this->m_aCurrRow;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// blank line, skip silently
|
||||
}
|
||||
$this->m_aCurrRow = array();
|
||||
}
|
||||
protected function __AddCellTrimmed($c = null, $aFieldMap = null)
|
||||
{
|
||||
$this->__AddCell($c, $aFieldMap, true);
|
||||
}
|
||||
|
||||
protected function __AddRowTrimmed($c = null, $aFieldMap = null)
|
||||
{
|
||||
$this->__AddRow($c, $aFieldMap, true);
|
||||
}
|
||||
|
||||
function ToArray($iToSkip = 1, $aFieldMap = null, $iMax = 0)
|
||||
{
|
||||
$aTransitions = array();
|
||||
|
||||
$aTransitions[stSTARTING][evBLANK] = array('', stSTARTING);
|
||||
$aTransitions[stSTARTING][evSEPARATOR] = array('__AddCell', stSTARTING);
|
||||
$aTransitions[stSTARTING][evNEWLINE] = array('__AddRow', stSTARTING);
|
||||
$aTransitions[stSTARTING][evTEXTQUAL] = array('', stQUALIFIED);
|
||||
$aTransitions[stSTARTING][evOTHERCHAR] = array('__AddChar', stRAW);
|
||||
$aTransitions[stSTARTING][evEND] = array('__AddRow', stSTARTING);
|
||||
|
||||
$aTransitions[stRAW][evBLANK] = array('__AddChar', stRAW);
|
||||
$aTransitions[stRAW][evSEPARATOR] = array('__AddCellTrimmed', stSTARTING);
|
||||
$aTransitions[stRAW][evNEWLINE] = array('__AddRowTrimmed', stSTARTING);
|
||||
$aTransitions[stRAW][evTEXTQUAL] = array('__AddChar', stRAW);
|
||||
$aTransitions[stRAW][evOTHERCHAR] = array('__AddChar', stRAW);
|
||||
$aTransitions[stRAW][evEND] = array('__AddRowTrimmed', stSTARTING);
|
||||
|
||||
$aTransitions[stQUALIFIED][evBLANK] = array('__AddChar', stQUALIFIED);
|
||||
$aTransitions[stQUALIFIED][evSEPARATOR] = array('__AddChar', stQUALIFIED);
|
||||
$aTransitions[stQUALIFIED][evNEWLINE] = array('__AddChar', stQUALIFIED);
|
||||
$aTransitions[stQUALIFIED][evTEXTQUAL] = array('', stESCAPED);
|
||||
$aTransitions[stQUALIFIED][evOTHERCHAR] = array('__AddChar', stQUALIFIED);
|
||||
$aTransitions[stQUALIFIED][evEND] = array('__AddRow', stSTARTING);
|
||||
|
||||
$aTransitions[stESCAPED][evBLANK] = array('', stESCAPED);
|
||||
$aTransitions[stESCAPED][evSEPARATOR] = array('__AddCell', stSTARTING);
|
||||
$aTransitions[stESCAPED][evNEWLINE] = array('__AddRow', stSTARTING);
|
||||
$aTransitions[stESCAPED][evTEXTQUAL] = array('__AddChar', stQUALIFIED);
|
||||
$aTransitions[stESCAPED][evOTHERCHAR] = array('__AddChar', stSTARTING);
|
||||
$aTransitions[stESCAPED][evEND] = array('__AddRow', stSTARTING);
|
||||
|
||||
// Reset parser variables
|
||||
$this->m_sCurrCell = '';
|
||||
$this->m_aCurrRow = array();
|
||||
$this->m_iToSkip = $iToSkip;
|
||||
$this->m_aDataSet = array();
|
||||
|
||||
$iDataLength = strlen($this->m_sCSVData);
|
||||
|
||||
$iState = stSTARTING;
|
||||
for($i = 0; $i <= $iDataLength ; $i++)
|
||||
{
|
||||
if ($i == $iDataLength)
|
||||
{
|
||||
$iEvent = evEND;
|
||||
}
|
||||
else
|
||||
{
|
||||
$c = $this->m_sCSVData[$i];
|
||||
|
||||
if ($c == $this->m_sSep)
|
||||
{
|
||||
$iEvent = evSEPARATOR;
|
||||
}
|
||||
elseif ($c == ' ')
|
||||
{
|
||||
$iEvent = evBLANK;
|
||||
}
|
||||
elseif ($c == "\t")
|
||||
{
|
||||
$iEvent = evBLANK;
|
||||
}
|
||||
elseif ($c == "\n")
|
||||
{
|
||||
$iEvent = evNEWLINE;
|
||||
}
|
||||
elseif ($c == $this->m_sTextQualifier)
|
||||
{
|
||||
$iEvent = evTEXTQUAL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iEvent = evOTHERCHAR;
|
||||
}
|
||||
}
|
||||
|
||||
$sAction = $aTransitions[$iState][$iEvent][0];
|
||||
$iState = $aTransitions[$iState][$iEvent][1];
|
||||
|
||||
if (!empty($sAction))
|
||||
{
|
||||
$aCallSpec = array($this, $sAction);
|
||||
if (is_callable($aCallSpec))
|
||||
{
|
||||
call_user_func($aCallSpec, $c, $aFieldMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CSVParserException("CSVParser: unknown verb '$sAction'");
|
||||
}
|
||||
}
|
||||
|
||||
$iLineCount = count($this->m_aDataSet);
|
||||
if (($iMax > 0) && ($iLineCount >= $iMax)) break;
|
||||
}
|
||||
return $this->m_aDataSet;
|
||||
}
|
||||
|
||||
public function ListFields()
|
||||
{
|
||||
$aHeader = $this->ToArray(0, null, 1);
|
||||
return $aHeader[0];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
373
core/data.generator.class.inc.php
Normal file
@@ -0,0 +1,373 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* data generator
|
||||
* helps the consultants in creating dummy data sets, for various test purposes (validation, usability, scalability)
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data Generator helper class
|
||||
*
|
||||
* This class is useful to generate a lot of sample data that look consistent
|
||||
* for a given organization in order to simulate a real CMDB
|
||||
*/
|
||||
class cmdbDataGenerator
|
||||
{
|
||||
protected $m_sOrganizationKey;
|
||||
protected $m_sOrganizationCode;
|
||||
protected $m_sOrganizationName;
|
||||
protected $m_OrganizationDomains;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($sOrganizationId = "")
|
||||
{
|
||||
global $aCompanies, $aCompaniesCode;
|
||||
if ($sOrganizationId == '')
|
||||
{
|
||||
// No organization provided, pick a random and unused one from our predefined list
|
||||
$retries = 5*count($aCompanies);
|
||||
while ( ($retries > 0) && !isset($this->m_sOrganizationCode)) // Stupid algorithm, but I'm too lazy to do something bulletproof tonight
|
||||
{
|
||||
$index = rand(0, count($aCompanies) - 1);
|
||||
if (!$this->OrganizationExists($aCompanies[$index]['code']))
|
||||
{
|
||||
$this->m_sOrganizationCode = $aCompanies[$index]['code'];
|
||||
$this->m_sOrganizationName = $aCompanies[$index]['name'];
|
||||
$this->m_OrganizationDomains = $aCompanies[$index]['domain'];
|
||||
}
|
||||
$retries--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// A code has been provided, let's take the information we need from the organization itself
|
||||
$this->m_sOrganizationId = $sOrganizationId;
|
||||
$oOrg = $this->GetOrganization($sOrganizationId);
|
||||
if ($oOrg == null)
|
||||
{
|
||||
echo "Unable to find the organization '$sOrganisationCode' in the database... can not add objects into this organization.<br/>\n";
|
||||
exit();
|
||||
}
|
||||
$this->m_sOrganizationCode = $oOrg->Get('code');
|
||||
$this->m_sOrganizationName = $oOrg->Get('name');
|
||||
if (!isset($aCompaniesCode[$this->m_sOrganizationCode]['domain']))
|
||||
{
|
||||
// Generate some probable domain names for this organization
|
||||
$this->m_OrganizationDomains = array(strtolower($this->m_sOrganizationCode).".com", strtolower($this->m_sOrganizationCode).".org", strtolower($this->m_sOrganizationCode)."corp.net",);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pick the domain names for this organization from the predefined list
|
||||
$this->m_OrganizationDomains = $aCompaniesCode[$this->m_sOrganizationCode]['domain'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($this->m_sOrganizationCode))
|
||||
{
|
||||
echo "Unable to find an organization code which is not already used... can not create a new organization. Enhance the list of fake organizations (\$aCompanies in data_sample.inc.php).<br/>\n";
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current organization id used by the generator
|
||||
*
|
||||
* @return string The organization id
|
||||
*/
|
||||
public function GetOrganizationId()
|
||||
{
|
||||
return $this->m_sOrganizationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current organization id used by the generator
|
||||
*
|
||||
* @param string The organization id
|
||||
* @return none
|
||||
*/
|
||||
public function SetOrganizationId($sId)
|
||||
{
|
||||
$this->m_sOrganizationId = $sId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current organization code used by the generator
|
||||
*
|
||||
* @return string The organization code
|
||||
*/
|
||||
public function GetOrganizationCode()
|
||||
{
|
||||
return $this->m_sOrganizationCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current organization name used by the generator
|
||||
*
|
||||
* @return string The organization name
|
||||
*/
|
||||
function GetOrganizationName()
|
||||
{
|
||||
return $this->m_sOrganizationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random first name taken from a (big) prefedined list
|
||||
*
|
||||
* @return string A random first name
|
||||
*/
|
||||
function GenerateFirstName()
|
||||
{
|
||||
global $aFirstNames;
|
||||
return $aFirstNames[rand(0, count($aFirstNames) - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random last name taken from a (big) prefedined list
|
||||
*
|
||||
* @return string A random last name
|
||||
*/
|
||||
function GenerateLastName()
|
||||
{
|
||||
global $aNames;
|
||||
return $aNames[rand(0, count($aNames) - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random country name taken from a prefedined list
|
||||
*
|
||||
* @return string A random city name
|
||||
*/
|
||||
function GenerateCountryName()
|
||||
{
|
||||
global $aCountries;
|
||||
return $aCountries[rand(0, count($aCountries) - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random city name taken from a (big) prefedined list
|
||||
*
|
||||
* @return string A random city name
|
||||
*/
|
||||
function GenerateCityName()
|
||||
{
|
||||
global $aCities;
|
||||
return $aCities[rand(0, count($aCities) - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a pseudo random email address made of the first name, last name and organization's domain
|
||||
*
|
||||
* @return string A random email address
|
||||
*/
|
||||
function GenerateEmail($sFirstName, $sLastName)
|
||||
{
|
||||
if (rand(1, 20) > 18)
|
||||
{
|
||||
// some people (let's say 5~10%) have an irregular email address
|
||||
$sEmail = strtolower($this->CleanForEmail($sLastName))."@".strtolower($this->GenerateDomain());
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEmail = strtolower($this->CleanForEmail($sFirstName)).".".strtolower($this->CleanForEmail($sLastName))."@".strtolower($this->GenerateDomain());
|
||||
}
|
||||
return $sEmail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate (pseudo) random strings that follow a given pattern
|
||||
*
|
||||
* The template is made of any number of 'parts' separated by pipes '|'
|
||||
* Each part is either:
|
||||
* - domain() => returns a domain name for the current organization
|
||||
* - enum(aaa,bb,c,dddd) => returns randomly one of aaa,bb,c or dddd with the same
|
||||
* probability of occurence. If you want to change the probability you can repeat some values
|
||||
* i.e enum(most probable,most probable,most probable,most probable,most probable,rare)
|
||||
* - number(xxx-yyy) => a random number between xxx and yyy (bounds included)
|
||||
* note that if the first number (xxx) begins with a zero, then the result will zero padded
|
||||
* to the same number of digits as xxx.
|
||||
* All other 'part' that does not follow one of the above mentioned pattern is returned as is
|
||||
*
|
||||
* Example: GenerateString("enum(sw,rtr,gw)|number(00-99)|.|domain()")
|
||||
* will produce strings like "sw01.netcmdb.com" or "rtr45.itop.org"
|
||||
*
|
||||
* @param string $sTemplate The template used for generating the string
|
||||
* @return string The generated pseudo random the string
|
||||
*/
|
||||
function GenerateString($sTemplate)
|
||||
{
|
||||
$sResult = "";
|
||||
$aParts = explode("\|", $sTemplate);
|
||||
foreach($aParts as $sPart)
|
||||
{
|
||||
if (preg_match("/domain\(\)/", $sPart, $aMatches))
|
||||
{
|
||||
$sResult .= strtolower($this->GenerateDomain());
|
||||
}
|
||||
elseif (preg_match("/enum\((.+)\)/", $sPart, $aMatches))
|
||||
{
|
||||
$sEnumValues = $aMatches[1];
|
||||
$aEnumValues = explode(",", $sEnumValues);
|
||||
$sResult .= $aEnumValues[rand(0, count($aEnumValues) - 1)];
|
||||
}
|
||||
elseif (preg_match("/number\((\d+)-(\d+)\)/", $sPart, $aMatches))
|
||||
{
|
||||
$sStartNumber = $aMatches[1];
|
||||
if ($sStartNumber[0] == '0')
|
||||
{
|
||||
// number must be zero padded
|
||||
$sFormat = "%0".strlen($sStartNumber)."d";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sFormat = "%d";
|
||||
}
|
||||
$sEndNumber = $aMatches[2];
|
||||
$sResult .= sprintf($sFormat, rand($sStartNumber, $sEndNumber));
|
||||
}
|
||||
else
|
||||
{
|
||||
$sResult .= $sPart;
|
||||
}
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a foreign key by picking a random element of the given class in a set limited by the given search criteria
|
||||
*
|
||||
* Example: GenerateKey("bizLocation", array('org_id', $oGenerator->GetOrganizationId());
|
||||
* will produce the foreign key of a Location object picked at random in the same organization
|
||||
*
|
||||
* @param string $sClass The name of the class to search for
|
||||
* @param string $aFilterCriteria A hash array of filterCOde => FilterValue (the strict operator '=' is used )
|
||||
* @return mixed The key to an object of the given class, or null if none are found
|
||||
*/
|
||||
function GenerateKey($sClass, $aFilterCriteria)
|
||||
{
|
||||
$retKey = null;
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
foreach($aFilterCriteria as $sFilterCode => $filterValue)
|
||||
{
|
||||
$oFilter->AddCondition($sFilterCode, $filterValue, '=');
|
||||
}
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
if ($oSet->Count() > 0)
|
||||
{
|
||||
$max_count = $index = rand(1, $oSet->Count());
|
||||
do
|
||||
{
|
||||
$oObj = $oSet->Fetch();
|
||||
$index--;
|
||||
}
|
||||
while($index > 0);
|
||||
|
||||
if (!is_object($oObj))
|
||||
{
|
||||
echo "<pre>";
|
||||
echo "ERROR: non empty set, but invalid object picked! class='$sClass'\n";
|
||||
echo "Index chosen: $max_count\n";
|
||||
echo "The set is supposed to contain ".$oSet->Count()." object(s)\n";
|
||||
echo "Filter criteria:\n";
|
||||
print_r($aFilterCriteria);
|
||||
echo "</pre>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$retKey = $oObj->GetKey();
|
||||
}
|
||||
}
|
||||
return $retKey;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Protected methods
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Generate a (random) domain name consistent with the organization name & code
|
||||
*
|
||||
* The values are pulled from a (limited) predefined list. Note that a given
|
||||
* organization may have several domain names, so the result may be random
|
||||
*
|
||||
* @return string A domain name (like netcnmdb.com)
|
||||
*/
|
||||
protected function GenerateDomain()
|
||||
{
|
||||
if (is_array($this->m_OrganizationDomains))
|
||||
{
|
||||
$sDomain = $this->m_OrganizationDomains[rand(0, count($this->m_OrganizationDomains)-1)];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDomain = $this->m_OrganizationDomains;
|
||||
}
|
||||
return $sDomain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips accented characters from a string in order to produce a suitable email address
|
||||
*
|
||||
* @param string The text string to clean
|
||||
* @return string The cleanified text string
|
||||
*/
|
||||
protected function CleanForEmail($sText)
|
||||
{
|
||||
return str_replace(array("'", "é", "è", "ê", "ç", "à", "â", "ñ", "ö", "ä"), array("", "e", "e", "e", "c", "a", "a", "n", "oe", "ae"), $sText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an organization with the given code already exists in the database
|
||||
*
|
||||
* @param string $sCode The code to look for
|
||||
* @return boolean true if the given organization exists, false otherwise
|
||||
*/
|
||||
protected function OrganizationExists($sCode)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter('bizOrganization');
|
||||
$oFilter->AddCondition('code', $sCode, '=');
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
return ($oSet->Count() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for an organization with the given code in the database
|
||||
*
|
||||
* @param string $Id The organization Id to look for
|
||||
* @return cmdbOrganization the organization if it exists, null otherwise
|
||||
*/
|
||||
protected function GetOrganization($sId)
|
||||
{
|
||||
$oOrg = null;
|
||||
$oFilter = new CMDBSearchFilter('bizOrganization');
|
||||
$oFilter->AddCondition('id', $sId, '=');
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
if ($oSet->Count() > 0)
|
||||
{
|
||||
$oOrg = $oSet->Fetch(); // Let's take the first one found
|
||||
}
|
||||
return $oOrg;
|
||||
}
|
||||
}
|
||||
?>
|
||||
1352
core/dbobject.class.php
Normal file
910
core/dbobjectsearch.class.php
Normal file
@@ -0,0 +1,910 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Define filters for a given class of objects (formerly named "filter")
|
||||
*
|
||||
* @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 DBObjectSearch
|
||||
{
|
||||
private $m_aClasses; // queried classes (alias => class name), the first item is the class corresponding to this filter (the rest is coming from subfilters)
|
||||
private $m_aSelectedClasses; // selected for the output (alias => class name)
|
||||
private $m_oSearchCondition;
|
||||
private $m_aParams;
|
||||
private $m_aFullText;
|
||||
private $m_aPointingTo;
|
||||
private $m_aReferencedBy;
|
||||
private $m_aRelatedTo;
|
||||
|
||||
// By default, some information may be hidden to the current user
|
||||
// But it may happen that we need to disable that feature
|
||||
private $m_bAllowAllData = false;
|
||||
|
||||
public function __construct($sClass, $sClassAlias = null)
|
||||
{
|
||||
if (is_null($sClassAlias)) $sClassAlias = $sClass;
|
||||
assert('is_string($sClass)');
|
||||
assert('MetaModel::IsValidClass($sClass)'); // #@# could do better than an assert, or at least give the caller's reference
|
||||
// => idee d'un assert avec call stack (autre utilisation = echec sur query SQL)
|
||||
|
||||
$this->m_aSelectedClasses = array($sClassAlias => $sClass);
|
||||
$this->m_aClasses = array($sClassAlias => $sClass);
|
||||
$this->m_oSearchCondition = new TrueExpression;
|
||||
$this->m_aParams = array();
|
||||
$this->m_aFullText = array();
|
||||
$this->m_aPointingTo = array();
|
||||
$this->m_aReferencedBy = array();
|
||||
$this->m_aRelatedTo = array();
|
||||
}
|
||||
|
||||
public function AllowAllData() {$this->m_bAllowAllData = true;}
|
||||
public function IsAllDataAllowed() {return $this->m_bAllowAllData;}
|
||||
|
||||
public function GetClassName($sAlias) {return $this->m_aClasses[$sAlias];}
|
||||
public function GetJoinedClasses() {return $this->m_aClasses;}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return reset($this->m_aSelectedClasses);
|
||||
}
|
||||
public function GetClassAlias()
|
||||
{
|
||||
reset($this->m_aSelectedClasses);
|
||||
return key($this->m_aSelectedClasses);
|
||||
}
|
||||
|
||||
public function GetFirstJoinedClass()
|
||||
{
|
||||
return reset($this->m_aClasses);
|
||||
}
|
||||
public function GetFirstJoinedClassAlias()
|
||||
{
|
||||
reset($this->m_aClasses);
|
||||
return key($this->m_aClasses);
|
||||
}
|
||||
|
||||
public function SetSelectedClasses($aNewSet)
|
||||
{
|
||||
$this->m_aSelectedClasses = array();
|
||||
foreach ($aNewSet as $sAlias => $sClass)
|
||||
{
|
||||
if (!array_key_exists($sAlias, $this->m_aClasses))
|
||||
{
|
||||
throw new CoreException('Unexpected class alias', array('alias'=>$sAlias, 'expected'=>$this->m_aClasses));
|
||||
}
|
||||
$this->m_aSelectedClasses[$sAlias] = $sClass;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetSelectedClasses()
|
||||
{
|
||||
return $this->m_aSelectedClasses;
|
||||
}
|
||||
|
||||
|
||||
public function IsAny()
|
||||
{
|
||||
// #@# todo - if (!$this->m_oSearchCondition->IsTrue()) return false;
|
||||
if (count($this->m_aFullText) > 0) return false;
|
||||
if (count($this->m_aPointingTo) > 0) return false;
|
||||
if (count($this->m_aReferencedBy) > 0) return false;
|
||||
if (count($this->m_aRelatedTo) > 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function Describe()
|
||||
{
|
||||
// To replace __Describe
|
||||
}
|
||||
|
||||
public function DescribeConditionPointTo($sExtKeyAttCode)
|
||||
{
|
||||
if (!isset($this->m_aPointingTo[$sExtKeyAttCode])) return "";
|
||||
$oFilter = $this->m_aPointingTo[$sExtKeyAttCode];
|
||||
if ($oFilter->IsAny()) return "";
|
||||
$oAtt = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode);
|
||||
return $oAtt->GetLabel()." having ({$oFilter->DescribeConditions()})";
|
||||
}
|
||||
|
||||
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode)
|
||||
{
|
||||
if (!isset($this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode])) return "";
|
||||
$oFilter = $this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode];
|
||||
if ($oFilter->IsAny()) return "";
|
||||
$oAtt = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
||||
return "being ".$oAtt->GetLabel()." for ".$sForeignClass."s in ({$oFilter->DescribeConditions()})";
|
||||
}
|
||||
|
||||
public function DescribeConditionRelTo($aRelInfo)
|
||||
{
|
||||
$oFilter = $aRelInfo['flt'];
|
||||
$sRelCode = $aRelInfo['relcode'];
|
||||
$iMaxDepth = $aRelInfo['maxdepth'];
|
||||
return "related ($sRelCode... peut mieux faire !, $iMaxDepth dig depth) to a {$oFilter->GetClass()} ({$oFilter->DescribeConditions()})";
|
||||
}
|
||||
|
||||
public function DescribeConditions()
|
||||
{
|
||||
$aConditions = array();
|
||||
|
||||
$aCondFT = array();
|
||||
foreach($this->m_aFullText as $sFullText)
|
||||
{
|
||||
$aCondFT[] = " contain word(s) '$sFullText'";
|
||||
}
|
||||
if (count($aCondFT) > 0)
|
||||
{
|
||||
$aConditions[] = "which ".implode(" and ", $aCondFT);
|
||||
}
|
||||
|
||||
// #@# todo - review textual description of the JOIN and search condition (is that still feasible?)
|
||||
$aConditions[] = $this->RenderCondition();
|
||||
|
||||
$aCondPoint = array();
|
||||
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$oFilter)
|
||||
{
|
||||
if ($oFilter->IsAny()) continue;
|
||||
$aCondPoint[] = $this->DescribeConditionPointTo($sExtKeyAttCode);
|
||||
}
|
||||
if (count($aCondPoint) > 0)
|
||||
{
|
||||
$aConditions[] = implode(" and ", $aCondPoint);
|
||||
}
|
||||
|
||||
$aCondReferred= array();
|
||||
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter)
|
||||
{
|
||||
if ($oForeignFilter->IsAny()) continue;
|
||||
$aCondReferred[] = $this->DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode);
|
||||
}
|
||||
}
|
||||
foreach ($this->m_aRelatedTo as $aRelInfo)
|
||||
{
|
||||
$aCondReferred[] = $this->DescribeConditionRelTo($aRelInfo);
|
||||
}
|
||||
if (count($aCondReferred) > 0)
|
||||
{
|
||||
$aConditions[] = implode(" and ", $aCondReferred);
|
||||
}
|
||||
|
||||
return implode(" and ", $aConditions);
|
||||
}
|
||||
|
||||
public function __DescribeHTML()
|
||||
{
|
||||
try
|
||||
{
|
||||
$sConditionDesc = $this->DescribeConditions();
|
||||
}
|
||||
catch (MissingQueryArgument $e)
|
||||
{
|
||||
$sConditionDesc = '?missing query argument?';
|
||||
}
|
||||
if (!empty($sConditionDesc))
|
||||
{
|
||||
return "Objects of class '".$this->GetClass()."', $sConditionDesc";
|
||||
}
|
||||
return "Any object of class '".$this->GetClass()."'";
|
||||
}
|
||||
|
||||
protected function TransferConditionExpression($oFilter, $aTranslation)
|
||||
{
|
||||
$oTranslated = $oFilter->GetCriteria()->Translate($aTranslation, false);
|
||||
$this->AddConditionExpression($oTranslated);
|
||||
// #@# what about collisions in parameter names ???
|
||||
$this->m_aParams = array_merge($this->m_aParams, $oFilter->m_aParams);
|
||||
}
|
||||
|
||||
public function ResetCondition()
|
||||
{
|
||||
$this->m_oSearchCondition = new TrueExpression();
|
||||
// ? is that usefull/enough, do I need to rebuild the list after the subqueries ?
|
||||
}
|
||||
|
||||
public function AddConditionExpression($oExpression)
|
||||
{
|
||||
$this->m_oSearchCondition = $this->m_oSearchCondition->LogAnd($oExpression);
|
||||
}
|
||||
|
||||
public function AddCondition($sFilterCode, $value, $sOpCode = null)
|
||||
{
|
||||
MyHelpers::CheckKeyInArray('filter code', $sFilterCode, MetaModel::GetClassFilterDefs($this->GetClass()));
|
||||
$oFilterDef = MetaModel::GetClassFilterDef($this->GetClass(), $sFilterCode);
|
||||
|
||||
$oField = new FieldExpression($sFilterCode, $this->GetClassAlias());
|
||||
if (empty($sOpCode))
|
||||
{
|
||||
if ($sFilterCode == 'id')
|
||||
{
|
||||
$sOpCode = '=';
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->GetClass(), $sFilterCode);
|
||||
$oNewCondition = $oAttDef->GetSmartConditionExpression($value, $oField, $this->m_aParams);
|
||||
$this->AddConditionExpression($oNewCondition);
|
||||
return;
|
||||
}
|
||||
}
|
||||
MyHelpers::CheckKeyInArray('operator', $sOpCode, $oFilterDef->GetOperators());
|
||||
|
||||
// Preserve backward compatibility - quick n'dirty way to change that API semantic
|
||||
//
|
||||
switch($sOpCode)
|
||||
{
|
||||
case 'SameDay':
|
||||
case 'SameMonth':
|
||||
case 'SameYear':
|
||||
case 'Today':
|
||||
case '>|':
|
||||
case '<|':
|
||||
case '=|':
|
||||
throw new CoreException('Deprecated operator, please consider using OQL (SQL) expressions like "(TO_DAYS(NOW()) - TO_DAYS(x)) AS AgeDays"', array('operator' => $sOpCode));
|
||||
break;
|
||||
|
||||
case "IN":
|
||||
if (!is_array($value)) $value = array($value);
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
|
||||
$sOQLCondition = $oField->Render()." IN $sListExpr";
|
||||
break;
|
||||
|
||||
case "NOTIN":
|
||||
if (!is_array($value)) $value = array($value);
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
|
||||
$sOQLCondition = $oField->Render()." NOT IN $sListExpr";
|
||||
break;
|
||||
|
||||
case 'Contains':
|
||||
$this->m_aParams[$sFilterCode] = "%$value%";
|
||||
$sOperator = 'LIKE';
|
||||
break;
|
||||
|
||||
case 'Begins with':
|
||||
$this->m_aParams[$sFilterCode] = "$value%";
|
||||
$sOperator = 'LIKE';
|
||||
break;
|
||||
|
||||
case 'Finishes with':
|
||||
$this->m_aParams[$sFilterCode] = "%$value";
|
||||
$sOperator = 'LIKE';
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->m_aParams[$sFilterCode] = $value;
|
||||
$sOperator = $sOpCode;
|
||||
}
|
||||
|
||||
switch($sOpCode)
|
||||
{
|
||||
case "IN":
|
||||
case "NOTIN":
|
||||
$oNewCondition = Expression::FromOQL($sOQLCondition);
|
||||
break;
|
||||
|
||||
case 'Contains':
|
||||
case 'Begins with':
|
||||
case 'Finishes with':
|
||||
default:
|
||||
$oRightExpr = new VariableExpression($sFilterCode);
|
||||
$oNewCondition = new BinaryExpression($oField, $sOperator, $oRightExpr);
|
||||
}
|
||||
|
||||
$this->AddConditionExpression($oNewCondition);
|
||||
}
|
||||
|
||||
public function AddCondition_FullText($sFullText)
|
||||
{
|
||||
$this->m_aFullText[] = $sFullText;
|
||||
}
|
||||
|
||||
protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
$sOrigAlias = $this->GetClassAlias();
|
||||
if (array_key_exists($sOrigAlias, $aClassAliases))
|
||||
{
|
||||
$sNewAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $this->GetClass());
|
||||
$this->m_aSelectedClasses[$sNewAlias] = $this->GetClass();
|
||||
unset($this->m_aSelectedClasses[$sOrigAlias]);
|
||||
|
||||
// Translate the condition expression with the new alias
|
||||
$aAliasTranslation[$sOrigAlias]['*'] = $sNewAlias;
|
||||
}
|
||||
|
||||
// add the alias into the filter aliases list
|
||||
$aClassAliases[$this->GetClassAlias()] = $this->GetClass();
|
||||
|
||||
foreach($this->m_aPointingTo as $sExtKeyAttCode=>$oFilter)
|
||||
{
|
||||
$oFilter->AddToNameSpace($aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
|
||||
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter)
|
||||
{
|
||||
$oForeignFilter->AddToNameSpace($aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode)
|
||||
{
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->AddCondition_PointingTo_InNameSpace($oFilter, $sExtKeyAttCode, $this->m_aClasses, $aAliasTranslation);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode))
|
||||
{
|
||||
throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored");
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($oFilter->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
throw new CoreException("The specified filter (pointing to {$oFilter->GetClass()}) is not compatible with the key '{$this->GetClass()}::$sExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}");
|
||||
}
|
||||
|
||||
if (array_key_exists($sExtKeyAttCode, $this->m_aPointingTo))
|
||||
{
|
||||
$this->m_aPointingTo[$sExtKeyAttCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
|
||||
|
||||
// #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!!
|
||||
// $oNewFilter = clone $oFilter;
|
||||
// $oNewFilter->ResetCondition();
|
||||
|
||||
$this->m_aPointingTo[$sExtKeyAttCode] = $oFilter;
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode)
|
||||
{
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->AddCondition_ReferencedBy_InNameSpace($oFilter, $sForeignExtKeyAttCode, $this->m_aClasses, $aAliasTranslation);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
$sForeignClass = $oFilter->GetClass();
|
||||
$sForeignClassAlias = $oFilter->GetClassAlias();
|
||||
if (!MetaModel::IsValidKeyAttCode($sForeignClass, $sForeignExtKeyAttCode))
|
||||
{
|
||||
throw new CoreException("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}' - the condition will be ignored");
|
||||
}
|
||||
$oAttExtKey = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode);
|
||||
if(!MetaModel::IsSameFamilyBranch($this->GetClass(), $oAttExtKey->GetTargetClass()))
|
||||
{
|
||||
throw new CoreException("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}");
|
||||
}
|
||||
if (array_key_exists($sForeignClass, $this->m_aReferencedBy) && array_key_exists($sForeignExtKeyAttCode, $this->m_aReferencedBy[$sForeignClass]))
|
||||
{
|
||||
$this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter->AddToNamespace($aClassAliases, $aAliasTranslation);
|
||||
|
||||
// #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!!
|
||||
//$oNewFilter = clone $oFilter;
|
||||
//$oNewFilter->ResetCondition();
|
||||
|
||||
$this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]= $oFilter;
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_LinkedTo(DBObjectSearch $oLinkFilter, $sExtKeyAttCodeToMe, $sExtKeyAttCodeTarget, DBObjectSearch $oFilterTarget)
|
||||
{
|
||||
$oLinkFilterFinal = clone $oLinkFilter;
|
||||
$oLinkFilterFinal->AddCondition_PointingTo($sExtKeyAttCodeToMe);
|
||||
|
||||
$this->AddCondition_ReferencedBy($oLinkFilterFinal, $sExtKeyAttCodeToMe);
|
||||
}
|
||||
|
||||
public function AddCondition_RelatedTo(DBObjectSearch $oFilter, $sRelCode, $iMaxDepth)
|
||||
{
|
||||
MyHelpers::CheckValueInArray('relation code', $sRelCode, MetaModel::EnumRelations());
|
||||
$this->m_aRelatedTo[] = array('flt'=>$oFilter, 'relcode'=>$sRelCode, 'maxdepth'=>$iMaxDepth);
|
||||
}
|
||||
|
||||
public function MergeWith($oFilter)
|
||||
{
|
||||
$aAliasTranslation = array();
|
||||
$res = $this->MergeWith_InNamespace($oFilter, $this->m_aClasses, $aAliasTranslation);
|
||||
$this->TransferConditionExpression($oFilter, $aAliasTranslation);
|
||||
return $res;
|
||||
}
|
||||
|
||||
protected function MergeWith_InNamespace($oFilter, &$aClassAliases, &$aAliasTranslation)
|
||||
{
|
||||
if ($this->GetClass() != $oFilter->GetClass())
|
||||
{
|
||||
throw new CoreException("Attempting to merge a filter of class '{$this->GetClass()}' with a filter of class '{$oFilter->GetClass()}'");
|
||||
}
|
||||
|
||||
// Translate search condition into our aliasing scheme
|
||||
$aAliasTranslation[$oFilter->GetClassAlias()]['*'] = $this->GetClassAlias();
|
||||
|
||||
$this->m_aFullText = array_merge($this->m_aFullText, $oFilter->m_aFullText);
|
||||
$this->m_aRelatedTo = array_merge($this->m_aRelatedTo, $oFilter->m_aRelatedTo);
|
||||
|
||||
foreach($oFilter->m_aPointingTo as $sExtKeyAttCode=>$oExtFilter)
|
||||
{
|
||||
$this->AddCondition_PointingTo_InNamespace($oExtFilter, $sExtKeyAttCode, $aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
foreach($oFilter->m_aReferencedBy as $sForeignClass => $aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode => $oForeignFilter)
|
||||
{
|
||||
$this->AddCondition_ReferencedBy_InNamespace($oForeignFilter, $sForeignExtKeyAttCode, $aClassAliases, $aAliasTranslation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function GetCriteria() {return $this->m_oSearchCondition;}
|
||||
public function GetCriteria_FullText() {return $this->m_aFullText;}
|
||||
public function GetCriteria_PointingTo($sKeyAttCode = "")
|
||||
{
|
||||
if (empty($sKeyAttCode))
|
||||
{
|
||||
return $this->m_aPointingTo;
|
||||
}
|
||||
if (!array_key_exists($sKeyAttCode, $this->m_aPointingTo)) return null;
|
||||
return $this->m_aPointingTo[$sKeyAttCode];
|
||||
}
|
||||
public function GetCriteria_ReferencedBy($sRemoteClass = "", $sForeignExtKeyAttCode = "")
|
||||
{
|
||||
if (empty($sRemoteClass))
|
||||
{
|
||||
return $this->m_aReferencedBy;
|
||||
}
|
||||
if (!array_key_exists($sRemoteClass, $this->m_aReferencedBy)) return null;
|
||||
if (empty($sForeignExtKeyAttCode))
|
||||
{
|
||||
return $this->m_aReferencedBy[$sRemoteClass];
|
||||
}
|
||||
if (!array_key_exists($sForeignExtKeyAttCode, $this->m_aReferencedBy[$sRemoteClass])) return null;
|
||||
return $this->m_aReferencedBy[$sRemoteClass][$sForeignExtKeyAttCode];
|
||||
}
|
||||
public function GetCriteria_RelatedTo()
|
||||
{
|
||||
return $this->m_aRelatedTo;
|
||||
}
|
||||
public function GetInternalParams()
|
||||
{
|
||||
return $this->m_aParams;
|
||||
}
|
||||
|
||||
public function RenderCondition()
|
||||
{
|
||||
return $this->m_oSearchCondition->Render($this->m_aParams, false);
|
||||
}
|
||||
|
||||
public function serialize($bDevelopParams = false, $aContextParams = null)
|
||||
{
|
||||
$sOql = $this->ToOql($bDevelopParams, $aContextParams);
|
||||
return base64_encode(serialize(array($sOql, $this->m_aParams)));
|
||||
}
|
||||
|
||||
static public function unserialize($sValue)
|
||||
{
|
||||
$aData = unserialize(base64_decode($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
|
||||
return self::FromOQL($sOql, $aParams);
|
||||
}
|
||||
|
||||
// SImple BUt Structured Query Languag - SubuSQL
|
||||
//
|
||||
static private function Value2Expression($value)
|
||||
{
|
||||
$sRet = $value;
|
||||
if (is_array($value))
|
||||
{
|
||||
$sRet = VS_START.implode(', ', $value).VS_END;
|
||||
}
|
||||
else if (!is_numeric($value))
|
||||
{
|
||||
$sRet = "'".addslashes($value)."'";
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
static private function Expression2Value($sExpr)
|
||||
{
|
||||
$retValue = $sExpr;
|
||||
if ((substr($sExpr, 0, 1) == "'") && (substr($sExpr, -1, 1) == "'"))
|
||||
{
|
||||
$sNoQuotes = substr($sExpr, 1, -1);
|
||||
return stripslashes($sNoQuotes);
|
||||
}
|
||||
if ((substr($sExpr, 0, 1) == VS_START) && (substr($sExpr, -1, 1) == VS_END))
|
||||
{
|
||||
$sNoBracket = substr($sExpr, 1, -1);
|
||||
$aRetValue = array();
|
||||
foreach (explode(",", $sNoBracket) as $sItem)
|
||||
{
|
||||
$aRetValue[] = self::Expression2Value(trim($sItem));
|
||||
}
|
||||
return $aRetValue;
|
||||
}
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
// 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
|
||||
public function ToDataArray($aColumns = array(), $aOrderBy = array(), $aArgs = array())
|
||||
{
|
||||
$sSQL = MetaModel::MakeSelectQuery($this, $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);
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
public function ToOQL($bDevelopParams = false, $aContextParams = null)
|
||||
{
|
||||
// Currently unused, but could be useful later
|
||||
$bRetrofitParams = false;
|
||||
|
||||
if ($bDevelopParams)
|
||||
{
|
||||
if (is_null($aContextParams))
|
||||
{
|
||||
$aParams = array_merge($this->m_aParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aParams = array_merge($aContextParams, $this->m_aParams);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave it as is, the rendering will be made with parameters in clear
|
||||
$aParams = null;
|
||||
}
|
||||
|
||||
$sSelectedClasses = implode(', ', array_keys($this->m_aSelectedClasses));
|
||||
$sRes = 'SELECT '.$sSelectedClasses.' FROM';
|
||||
|
||||
$sRes .= ' '.$this->GetClass().' AS '.$this->GetClassAlias();
|
||||
$sRes .= $this->ToOQL_Joins();
|
||||
$sRes .= " WHERE ".$this->m_oSearchCondition->Render($aParams, $bRetrofitParams);
|
||||
|
||||
// Temporary: add more info about other conditions, necessary to avoid strange behaviors with the cache
|
||||
foreach($this->m_aFullText as $sFullText)
|
||||
{
|
||||
$sRes .= " AND MATCHES '$sFullText'";
|
||||
}
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
protected function ToOQL_Joins()
|
||||
{
|
||||
$sRes = '';
|
||||
foreach($this->m_aPointingTo as $sExtKey=>$oFilter)
|
||||
{
|
||||
$sRes .= ' JOIN '.$oFilter->GetClass().' AS '.$oFilter->GetClassAlias().' ON '.$this->GetClassAlias().'.'.$sExtKey.' = '.$oFilter->GetClassAlias().'.id';
|
||||
$sRes .= $oFilter->ToOQL_Joins();
|
||||
}
|
||||
foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences)
|
||||
{
|
||||
foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter)
|
||||
{
|
||||
$sRes .= ' JOIN '.$oForeignFilter->GetClass().' AS '.$oForeignFilter->GetClassAlias().' ON '.$oForeignFilter->GetClassAlias().'.'.$sForeignExtKeyAttCode.' = '.$this->GetClassAlias().'.id';
|
||||
$sRes .= $oForeignFilter->ToOQL_Joins();
|
||||
}
|
||||
}
|
||||
return $sRes;
|
||||
}
|
||||
|
||||
protected function OQLExpressionToCondition($sQuery, $oExpression, $aClassAliases)
|
||||
{
|
||||
if ($oExpression instanceof BinaryOqlExpression)
|
||||
{
|
||||
$sOperator = $oExpression->GetOperator();
|
||||
$oLeft = $this->OQLExpressionToCondition($sQuery, $oExpression->GetLeftExpr(), $aClassAliases);
|
||||
$oRight = $this->OQLExpressionToCondition($sQuery, $oExpression->GetRightExpr(), $aClassAliases);
|
||||
return new BinaryExpression($oLeft, $sOperator, $oRight);
|
||||
}
|
||||
elseif ($oExpression instanceof FieldOqlExpression)
|
||||
{
|
||||
$sClassAlias = $oExpression->GetParent();
|
||||
$sFltCode = $oExpression->GetName();
|
||||
if (empty($sClassAlias))
|
||||
{
|
||||
// Try to find an alias
|
||||
// Build an array of field => array of aliases
|
||||
$aFieldClasses = array();
|
||||
foreach($aClassAliases as $sAlias => $sReal)
|
||||
{
|
||||
foreach(MetaModel::GetFiltersList($sReal) as $sAnFltCode)
|
||||
{
|
||||
$aFieldClasses[$sAnFltCode][] = $sAlias;
|
||||
}
|
||||
}
|
||||
if (!array_key_exists($sFltCode, $aFieldClasses))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), array_keys($aFieldClasses));
|
||||
}
|
||||
if (count($aFieldClasses[$sFltCode]) > 1)
|
||||
{
|
||||
throw new OqlNormalizeException('Ambiguous filter code', $sQuery, $oExpression->GetNameDetails());
|
||||
}
|
||||
$sClassAlias = $aFieldClasses[$sFltCode][0];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($sClassAlias, $aClassAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sQuery, $oExpression->GetParentDetails(), array_keys($aClassAliases));
|
||||
}
|
||||
$sClass = $aClassAliases[$sClassAlias];
|
||||
if (!MetaModel::IsValidFilterCode($sClass, $sFltCode))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), MetaModel::GetFiltersList($sClass));
|
||||
}
|
||||
}
|
||||
|
||||
return new FieldExpression($sFltCode, $sClassAlias);
|
||||
}
|
||||
elseif ($oExpression instanceof VariableOqlExpression)
|
||||
{
|
||||
return new VariableExpression($oExpression->GetName());
|
||||
}
|
||||
elseif ($oExpression instanceof TrueOqlExpression)
|
||||
{
|
||||
return new TrueExpression;
|
||||
}
|
||||
elseif ($oExpression instanceof ScalarOqlExpression)
|
||||
{
|
||||
return new ScalarExpression($oExpression->GetValue());
|
||||
}
|
||||
elseif ($oExpression instanceof ListOqlExpression)
|
||||
{
|
||||
$aItems = array();
|
||||
foreach ($oExpression->GetItems() as $oItemExpression)
|
||||
{
|
||||
$aItems[] = $this->OQLExpressionToCondition($sQuery, $oItemExpression, $aClassAliases);
|
||||
}
|
||||
return new ListExpression($aItems);
|
||||
}
|
||||
elseif ($oExpression instanceof FunctionOqlExpression)
|
||||
{
|
||||
$aArgs = array();
|
||||
foreach ($oExpression->GetArgs() as $oArgExpression)
|
||||
{
|
||||
$aArgs[] = $this->OQLExpressionToCondition($sQuery, $oArgExpression, $aClassAliases);
|
||||
}
|
||||
return new FunctionExpression($oExpression->GetVerb(), $aArgs);
|
||||
}
|
||||
elseif ($oExpression instanceof IntervalOqlExpression)
|
||||
{
|
||||
return new IntervalExpression($oExpression->GetValue(), $oExpression->GetUnit());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CoreException('Unknown expression type', array('class'=>get_class($oExpression), 'query'=>$sQuery));
|
||||
}
|
||||
}
|
||||
|
||||
// Create a search definition that leads to 0 result, still a valid search object
|
||||
static public function FromEmptySet($sClass)
|
||||
{
|
||||
$oResultFilter = new DBObjectSearch($sClass);
|
||||
$oResultFilter->m_oSearchCondition = new FalseExpression;
|
||||
return $oResultFilter;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static public function FromOQL($sQuery, $aParams = null)
|
||||
{
|
||||
if (empty($sQuery)) return null;
|
||||
|
||||
// Query caching
|
||||
$bOQLCacheEnabled = true;
|
||||
if ($bOQLCacheEnabled && array_key_exists($sQuery, self::$m_aOQLQueries))
|
||||
{
|
||||
// hit!
|
||||
return clone self::$m_aOQLQueries[$sQuery];
|
||||
}
|
||||
|
||||
$oOql = new OqlInterpreter($sQuery);
|
||||
$oOqlQuery = $oOql->ParseObjectQuery();
|
||||
|
||||
$sClass = $oOqlQuery->GetClass();
|
||||
$sClassAlias = $oOqlQuery->GetClassAlias();
|
||||
|
||||
if (!MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, $oOqlQuery->GetClassDetails(), MetaModel::GetClasses());
|
||||
}
|
||||
|
||||
$oResultFilter = new DBObjectSearch($sClass, $sClassAlias);
|
||||
$aAliases = array($sClassAlias => $sClass);
|
||||
|
||||
// Maintain an array of filters, because the flat list is in fact referring to a tree
|
||||
// And this will be an easy way to dispatch the conditions
|
||||
// $oResultFilter will be referenced by the other filters, or the other way around...
|
||||
$aJoinItems = array($sClassAlias => $oResultFilter);
|
||||
|
||||
$aJoinSpecs = $oOqlQuery->GetJoins();
|
||||
if (is_array($aJoinSpecs))
|
||||
{
|
||||
foreach ($aJoinSpecs as $oJoinSpec)
|
||||
{
|
||||
$sJoinClass = $oJoinSpec->GetClass();
|
||||
$sJoinClassAlias = $oJoinSpec->GetClassAlias();
|
||||
if (!MetaModel::IsValidClass($sJoinClass))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class', $sQuery, $oJoinSpec->GetClassDetails(), MetaModel::GetClasses());
|
||||
}
|
||||
if (array_key_exists($sJoinClassAlias, $aAliases))
|
||||
{
|
||||
if ($sJoinClassAlias != $sJoinClass)
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class alias', $sQuery, $oJoinSpec->GetClassAliasDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new OqlNormalizeException('Duplicate class name', $sQuery, $oJoinSpec->GetClassDetails());
|
||||
}
|
||||
}
|
||||
|
||||
// Assumption: ext key on the left only !!!
|
||||
// normalization should take care of this
|
||||
$oLeftField = $oJoinSpec->GetLeftField();
|
||||
$sFromClass = $oLeftField->GetParent();
|
||||
$sExtKeyAttCode = $oLeftField->GetName();
|
||||
|
||||
$oRightField = $oJoinSpec->GetRightField();
|
||||
$sToClass = $oRightField->GetParent();
|
||||
$sPKeyDescriptor = $oRightField->GetName();
|
||||
if ($sPKeyDescriptor != 'id')
|
||||
{
|
||||
throw new OqlNormalizeException('Wrong format for Join clause (right hand), expecting an id', $sQuery, $oRightField->GetNameDetails(), array('id'));
|
||||
}
|
||||
|
||||
$aAliases[$sJoinClassAlias] = $sJoinClass;
|
||||
$aJoinItems[$sJoinClassAlias] = new DBObjectSearch($sJoinClass, $sJoinClassAlias);
|
||||
|
||||
if (!array_key_exists($sFromClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (left expression)', $sQuery, $oLeftField->GetParentDetails(), array_keys($aJoinItems));
|
||||
}
|
||||
if (!array_key_exists($sToClass, $aJoinItems))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sQuery, $oRightField->GetParentDetails(), array_keys($aJoinItems));
|
||||
}
|
||||
$aExtKeys = array_keys(MetaModel::GetExternalKeys($aAliases[$sFromClass]));
|
||||
if (!in_array($sExtKeyAttCode, $aExtKeys))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sQuery, $oLeftField->GetNameDetails(), $aExtKeys);
|
||||
}
|
||||
|
||||
if ($sFromClass == $sJoinClassAlias)
|
||||
{
|
||||
$aJoinItems[$sToClass]->AddCondition_ReferencedBy($aJoinItems[$sFromClass], $sExtKeyAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aJoinItems[$sFromClass]->AddCondition_PointingTo($aJoinItems[$sToClass], $sExtKeyAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check and prepare the select information
|
||||
$aSelected = array();
|
||||
foreach ($oOqlQuery->GetSelectedClasses() as $oClassDetails)
|
||||
{
|
||||
$sClassToSelect = $oClassDetails->GetValue();
|
||||
if (!array_key_exists($sClassToSelect, $aAliases))
|
||||
{
|
||||
throw new OqlNormalizeException('Unknown class [alias]', $sQuery, $oClassDetails, array_keys($aAliases));
|
||||
}
|
||||
$aSelected[$sClassToSelect] = $aAliases[$sClassToSelect];
|
||||
}
|
||||
$oResultFilter->m_aClasses = $aAliases;
|
||||
$oResultFilter->SetSelectedClasses($aSelected);
|
||||
|
||||
$oConditionTree = $oOqlQuery->GetCondition();
|
||||
if ($oConditionTree instanceof Expression)
|
||||
{
|
||||
$oResultFilter->m_oSearchCondition = $oResultFilter->OQLExpressionToCondition($sQuery, $oConditionTree, $aAliases);
|
||||
}
|
||||
|
||||
if (!is_null($aParams))
|
||||
{
|
||||
$oResultFilter->m_aParams = $aParams;
|
||||
}
|
||||
|
||||
if ($bOQLCacheEnabled)
|
||||
{
|
||||
self::$m_aOQLQueries[$sQuery] = clone $oResultFilter;
|
||||
}
|
||||
|
||||
return $oResultFilter;
|
||||
}
|
||||
|
||||
public function toxpath()
|
||||
{
|
||||
// #@# a voir...
|
||||
}
|
||||
static public function fromxpath()
|
||||
{
|
||||
// #@# a voir...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
442
core/dbobjectset.class.php
Normal file
@@ -0,0 +1,442 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Object set management
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A set of persistent objects, could be heterogeneous
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class DBObjectSet
|
||||
{
|
||||
private $m_oFilter;
|
||||
private $m_aOrderBy;
|
||||
public $m_bLoaded;
|
||||
private $m_aData;
|
||||
private $m_aId2Row;
|
||||
private $m_iCurrRow;
|
||||
|
||||
public function __construct(DBObjectSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $iLimitCount = 0, $iLimitStart = 0)
|
||||
{
|
||||
$this->m_oFilter = $oFilter;
|
||||
$this->m_aOrderBy = $aOrderBy;
|
||||
$this->m_aArgs = $aArgs;
|
||||
$this->m_iLimitCount = $iLimitCount;
|
||||
$this->m_iLimitStart = $iLimitStart;
|
||||
|
||||
$this->m_bLoaded = false;
|
||||
$this->m_aData = array(); // array of (row => array of (classalias) => object)
|
||||
$this->m_aId2Row = array();
|
||||
$this->m_iCurrRow = 0;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$sRet = '';
|
||||
$this->Rewind();
|
||||
$sRet .= "Set (".$this->m_oFilter->ToOQL().")<br/>\n";
|
||||
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".MetaModel::MakeSelectQuery($this->m_oFilter, array()).")</pre>\n";
|
||||
|
||||
$sRet .= $this->Count()." records<br/>\n";
|
||||
if ($this->Count() > 0)
|
||||
{
|
||||
$sRet .= "<ul class=\"treeview\">\n";
|
||||
while ($oObj = $this->Fetch())
|
||||
{
|
||||
$sRet .= "<li>".$oObj->__toString()."</li>\n";
|
||||
}
|
||||
$sRet .= "</ul>\n";
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
static public function FromObject($oObject)
|
||||
{
|
||||
$oRetSet = self::FromScratch(get_class($oObject));
|
||||
$oRetSet->AddObject($oObject);
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new self($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
// create an object set ex nihilo
|
||||
// input = array of objects
|
||||
static public function FromArray($sClass, $aObjects)
|
||||
{
|
||||
$oFilter = new CMDBSearchFilter($sClass);
|
||||
$oRetSet = new self($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
$oRetSet->AddObjectArray($aObjects, $sClass);
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
// create an object set ex nihilo
|
||||
// aClasses = array of (alias => class)
|
||||
// input = array of (array of (classalias => object))
|
||||
static public function FromArrayAssoc($aClasses, $aObjects)
|
||||
{
|
||||
// In a perfect world, we should create a complete tree of DBObjectSearch,
|
||||
// but as we lack most of the information related to the objects,
|
||||
// let's create one search definition
|
||||
$sClass = reset($aClasses);
|
||||
$sAlias = key($aClasses);
|
||||
$oFilter = new CMDBSearchFilter($sClass, $sAlias);
|
||||
|
||||
$oRetSet = new self($oFilter);
|
||||
$oRetSet->m_bLoaded = true; // no DB load
|
||||
|
||||
foreach($aObjects as $rowIndex => $aObjectsByClassAlias)
|
||||
{
|
||||
$oRetSet->AddObjectExtended($aObjectsByClassAlias);
|
||||
}
|
||||
return $oRetSet;
|
||||
}
|
||||
|
||||
static public function FromLinkSet($oObject, $sLinkSetAttCode, $sExtKeyToRemote)
|
||||
{
|
||||
$oLinkAttCode = MetaModel::GetAttributeDef(get_class($oObject), $sLinkSetAttCode);
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($oLinkAttCode->GetLinkedClass(), $sExtKeyToRemote);
|
||||
$sTargetClass = $oExtKeyAttDef->GetTargetClass();
|
||||
|
||||
$oLinkSet = $oObject->Get($sLinkSetAttCode);
|
||||
$aTargets = array();
|
||||
while ($oLink = $oLinkSet->Fetch())
|
||||
{
|
||||
$aTargets[] = MetaModel::GetObject($sTargetClass, $oLink->Get($sExtKeyToRemote));
|
||||
}
|
||||
|
||||
return self::FromArray($sTargetClass, $aTargets);
|
||||
}
|
||||
|
||||
public function ToArray($bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
$this->Rewind();
|
||||
while ($oObject = $this->Fetch())
|
||||
{
|
||||
if ($bWithId)
|
||||
{
|
||||
$aRet[$oObject->GetKey()] = $oObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRet[] = $oObject;
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
public function ToArrayOfValues()
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$aRet = array();
|
||||
foreach($this->m_aData as $iRow => $aObjects)
|
||||
{
|
||||
foreach($aObjects as $sClassAlias => $oObject)
|
||||
{
|
||||
$aRet[$iRow][$sClassAlias.'.'.'id'] = $oObject->GetKey();
|
||||
$sClass = get_class($oObject);
|
||||
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($oAttDef->IsScalar())
|
||||
{
|
||||
$sAttName = $sClassAlias.'.'.$sAttCode;
|
||||
$aRet[$iRow][$sAttName] = $oObject->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
public function GetColumnAsArray($sAttCode, $bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
$this->Rewind();
|
||||
while ($oObject = $this->Fetch())
|
||||
{
|
||||
if ($bWithId)
|
||||
{
|
||||
$aRet[$oObject->GetKey()] = $oObject->Get($sAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRet[] = $oObject->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
public function GetFilter()
|
||||
{
|
||||
return $this->m_oFilter;
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_oFilter->GetClass();
|
||||
}
|
||||
|
||||
public function GetSelectedClasses()
|
||||
{
|
||||
return $this->m_oFilter->GetSelectedClasses();
|
||||
}
|
||||
|
||||
public function GetRootClass()
|
||||
{
|
||||
return MetaModel::GetRootClass($this->GetClass());
|
||||
}
|
||||
|
||||
public function SetLimit($iLimitCount, $iLimitStart = 0)
|
||||
{
|
||||
$this->m_iLimitCount = $iLimitCount;
|
||||
$this->m_iLimitStart = $iLimitStart;
|
||||
}
|
||||
|
||||
public function GetLimitCount()
|
||||
{
|
||||
return $this->m_iLimitCount;
|
||||
}
|
||||
|
||||
public function GetLimitStart()
|
||||
{
|
||||
return $this->m_iLimitStart;
|
||||
}
|
||||
|
||||
public function Load()
|
||||
{
|
||||
if ($this->m_bLoaded) return;
|
||||
if ($this->m_iLimitCount > 0)
|
||||
{
|
||||
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->m_aOrderBy, $this->m_aArgs, $this->m_iLimitCount, $this->m_iLimitStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->m_aOrderBy, $this->m_aArgs);
|
||||
}
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery) return;
|
||||
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
while ($aRow = CMDBSource::FetchArray($resQuery))
|
||||
{
|
||||
$aObjects = array();
|
||||
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
|
||||
{
|
||||
$oObject = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias);
|
||||
$aObjects[$sClassAlias] = $oObject;
|
||||
}
|
||||
$this->AddObjectExtended($aObjects);
|
||||
}
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
|
||||
$this->m_bLoaded = true;
|
||||
}
|
||||
|
||||
public function Count()
|
||||
{
|
||||
$sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->m_aOrderBy, $this->m_aArgs, 0, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery) return 0;
|
||||
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
return $aRow['COUNT'];
|
||||
}
|
||||
|
||||
public function Fetch($sClassAlias = '')
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
if ($this->m_iCurrRow >= count($this->m_aData))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (strlen($sClassAlias) == 0)
|
||||
{
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
}
|
||||
$oRetObj = $this->m_aData[$this->m_iCurrRow][$sClassAlias];
|
||||
$this->m_iCurrRow++;
|
||||
return $oRetObj;
|
||||
}
|
||||
|
||||
// Return the whole line if several classes have been specified in the query
|
||||
//
|
||||
public function FetchAssoc()
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
if ($this->m_iCurrRow >= count($this->m_aData))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
$aRetObjects = $this->m_aData[$this->m_iCurrRow];
|
||||
$this->m_iCurrRow++;
|
||||
return $aRetObjects;
|
||||
}
|
||||
|
||||
public function Rewind()
|
||||
{
|
||||
$this->Seek(0);
|
||||
}
|
||||
|
||||
public function Seek($iRow)
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$this->m_iCurrRow = min($iRow, count($this->m_aData));
|
||||
return $this->m_iCurrRow;
|
||||
}
|
||||
|
||||
public function AddObject($oObject, $sClassAlias = '')
|
||||
{
|
||||
if (strlen($sClassAlias) == 0)
|
||||
{
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
}
|
||||
|
||||
$iNextPos = count($this->m_aData);
|
||||
$this->m_aData[$iNextPos][$sClassAlias] = $oObject;
|
||||
$this->m_aId2Row[$sClassAlias][$oObject->GetKey()] = $iNextPos;
|
||||
}
|
||||
|
||||
protected function AddObjectExtended($aObjectArray)
|
||||
{
|
||||
$iNextPos = count($this->m_aData);
|
||||
|
||||
foreach ($aObjectArray as $sClassAlias => $oObject)
|
||||
{
|
||||
$this->m_aData[$iNextPos][$sClassAlias] = $oObject;
|
||||
$this->m_aId2Row[$sClassAlias][$oObject->GetKey()] = $iNextPos;
|
||||
}
|
||||
}
|
||||
|
||||
public function AddObjectArray($aObjects, $sClassAlias = '')
|
||||
{
|
||||
// #@# todo - add a check on the object class ?
|
||||
foreach ($aObjects as $oObj)
|
||||
{
|
||||
$this->AddObject($oObj, $sClassAlias);
|
||||
}
|
||||
}
|
||||
|
||||
public function Merge($oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
throw new CoreException("Could not merge two objects sets if they don't have the same root class");
|
||||
}
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$oObjectSet->Seek(0);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
$this->AddObject($oObject);
|
||||
}
|
||||
}
|
||||
|
||||
public function CreateIntersect($oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
throw new CoreException("Could not 'intersect' two objects sets if they don't have the same root class");
|
||||
}
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$oNewSet = DBObjectSet::FromScratch($this->GetClass());
|
||||
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
$oObjectSet->Seek(0);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
if (array_key_exists($oObject->GetKey(), $this->m_aId2Row[$sClassAlias]))
|
||||
{
|
||||
$oNewSet->AddObject($oObject);
|
||||
}
|
||||
}
|
||||
return $oNewSet;
|
||||
}
|
||||
|
||||
public function CreateDelta($oObjectSet)
|
||||
{
|
||||
if ($this->GetRootClass() != $oObjectSet->GetRootClass())
|
||||
{
|
||||
throw new CoreException("Could not 'delta' two objects sets if they don't have the same root class");
|
||||
}
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
$oNewSet = DBObjectSet::FromScratch($this->GetClass());
|
||||
|
||||
$sClassAlias = $this->m_oFilter->GetClassAlias();
|
||||
$oObjectSet->Seek(0);
|
||||
while ($oObject = $oObjectSet->Fetch())
|
||||
{
|
||||
if (!array_key_exists($oObject->GetKey(), $this->m_aId2Row[$sClassAlias]))
|
||||
{
|
||||
$oNewSet->AddObject($oObject);
|
||||
}
|
||||
}
|
||||
return $oNewSet;
|
||||
}
|
||||
|
||||
public function GetRelatedObjects($sRelCode, $iMaxDepth = 99)
|
||||
{
|
||||
$aRelatedObjs = array();
|
||||
|
||||
$aVisited = array(); // optimization for consecutive calls of MetaModel::GetRelatedObjects
|
||||
$this->Seek(0);
|
||||
while ($oObject = $this->Fetch())
|
||||
{
|
||||
$aMore = $oObject->GetRelatedObjects($sRelCode, $iMaxDepth, $aVisited);
|
||||
foreach ($aMore as $sClass => $aRelated)
|
||||
{
|
||||
foreach ($aRelated as $iObj => $oObj)
|
||||
{
|
||||
if (!isset($aRelatedObjs[$sClass][$iObj]))
|
||||
{
|
||||
$aRelatedObjs[$sClass][$iObj] = $oObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aRelatedObjs;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
159
core/dbproperty.class.inc.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Database properties - manage database instances in a complex installation
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A database property
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class DBProperty extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "cloud",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_db_properties",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("value", array("allowed_values"=>null, "sql"=>"value", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", array("allowed_values"=>null, "sql"=>"change_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("change_comment", array("allowed_values"=>null, "sql"=>"change_comment", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to check wether the table has been created into the DB
|
||||
* (this table did not exist in 1.0.1 and older versions)
|
||||
*/
|
||||
public static function IsInstalled()
|
||||
{
|
||||
$sTable = MetaModel::DBGetTable(__CLASS__);
|
||||
if (CMDBSource::IsTable($sTable))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function SetProperty($sName, $sValue, $sComment = '', $sDescription = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL('SELECT DBProperty WHERE name = :name');
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('name' => $sName));
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
$oProp = new DBProperty();
|
||||
$oProp->Set('name', $sName);
|
||||
$oProp->Set('description', $sDescription);
|
||||
$oProp->Set('value', $sValue);
|
||||
$oProp->Set('change_date', time());
|
||||
$oProp->Set('change_comment', $sComment);
|
||||
$oProp->DBInsert();
|
||||
}
|
||||
elseif ($oSet->Count() == 1)
|
||||
{
|
||||
$oProp = $oSet->fetch();
|
||||
if (!is_null($sDescription))
|
||||
{
|
||||
$oProp->Set('description', $sDescription);
|
||||
}
|
||||
$oProp->Set('value', $sValue);
|
||||
$oProp->Set('change_date', time());
|
||||
$oProp->Set('change_comment', $sComment);
|
||||
$oProp->DBUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Houston...
|
||||
throw new CoreException('duplicate db property');
|
||||
}
|
||||
}
|
||||
catch (MySQLException $e)
|
||||
{
|
||||
// This might be because the table could not be found,
|
||||
// let's check it and discard silently if this is really the case
|
||||
if (self::IsInstalled())
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
IssueLog::Error('Attempting to write a DBProperty while the module has not been installed');
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetProperty($sName, $default = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL('SELECT DBProperty WHERE name = :name');
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('name' => $sName));
|
||||
$iCount = $oSet->Count();
|
||||
if ($iCount == 0)
|
||||
{
|
||||
//throw new CoreException('unknown db property', array('name' => $sName));
|
||||
$sValue = $default;
|
||||
}
|
||||
elseif ($iCount == 1)
|
||||
{
|
||||
$oProp = $oSet->fetch();
|
||||
$sValue = $oProp->Get('value');
|
||||
}
|
||||
else
|
||||
{
|
||||
// $iCount > 1
|
||||
// Houston...
|
||||
throw new CoreException('duplicate db property', array('name' => $sName, 'count' => $iCount));
|
||||
}
|
||||
}
|
||||
catch (MySQLException $e)
|
||||
{
|
||||
// This might be because the table could not be found,
|
||||
// let's check it and discard silently if this is really the case
|
||||
if (self::IsInstalled())
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
$sValue = $default;
|
||||
}
|
||||
return $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
235
core/dict.class.inc.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?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 Dict
|
||||
* Management of localizable strings
|
||||
*
|
||||
* @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
|
||||
{
|
||||
}
|
||||
|
||||
class DictExceptionUnknownLanguage extends DictException
|
||||
{
|
||||
public function __construct($sLanguageCode)
|
||||
{
|
||||
$aContext = array();
|
||||
$aContext['language_code'] = $sLanguageCode;
|
||||
parent::__construct('Unknown localization language', $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
class DictExceptionMissingString extends DictException
|
||||
{
|
||||
public function __construct($sLanguageCode, $sStringCode)
|
||||
{
|
||||
$aContext = array();
|
||||
$aContext['language_code'] = $sLanguageCode;
|
||||
$aContext['string_code'] = $sStringCode;
|
||||
parent::__construct('Missing localized string', $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
define('DICT_ERR_STRING', 1); // when a string is missing, return the identifier
|
||||
define('DICT_ERR_EXCEPTION', 2); // when a string is missing, throw an exception
|
||||
//define('DICT_ERR_LOG', 3); // when a string is missing, log an error
|
||||
|
||||
|
||||
class Dict
|
||||
{
|
||||
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();
|
||||
|
||||
|
||||
public static function SetDefaultLanguage($sLanguageCode)
|
||||
{
|
||||
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
{
|
||||
throw new DictExceptionUnknownLanguage($sLanguageCode);
|
||||
}
|
||||
self::$m_sDefaultLanguage = $sLanguageCode;
|
||||
}
|
||||
|
||||
public static function SetUserLanguage($sLanguageCode)
|
||||
{
|
||||
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
{
|
||||
throw new DictExceptionUnknownLanguage($sLanguageCode);
|
||||
}
|
||||
self::$m_sCurrentLanguage = $sLanguageCode;
|
||||
}
|
||||
|
||||
|
||||
public static function GetCurrentLanguage()
|
||||
{
|
||||
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e login screen, non authentifed page)
|
||||
{
|
||||
// In which case let's use the default language
|
||||
return self::$m_sDefaultLanguage;
|
||||
}
|
||||
return self::$m_sCurrentLanguage;
|
||||
}
|
||||
|
||||
//returns a hash array( code => array( 'description' => '...', 'localized_description' => '...') ...)
|
||||
public static function GetLanguages()
|
||||
{
|
||||
return self::$m_aLanguages;
|
||||
}
|
||||
|
||||
// iErrorMode from {DICT_ERR_STRING, DICT_ERR_EXCEPTION}
|
||||
public static function SetErrorMode($iErrorMode)
|
||||
{
|
||||
self::$m_iErrorMode = $iErrorMode;
|
||||
}
|
||||
|
||||
|
||||
public static function S($sStringCode, $sDefault = null)
|
||||
{
|
||||
// Attempt to find the string in the user language
|
||||
//
|
||||
if (!array_key_exists(self::GetCurrentLanguage(), self::$m_aData))
|
||||
{
|
||||
// It may happen, when something happens before the dictionnaries get loaded
|
||||
return $sStringCode;
|
||||
}
|
||||
$aCurrentDictionary = self::$m_aData[self::GetCurrentLanguage()];
|
||||
if (array_key_exists($sStringCode, $aCurrentDictionary))
|
||||
{
|
||||
return $aCurrentDictionary[$sStringCode];
|
||||
}
|
||||
// Attempt to find the string in the default language
|
||||
//
|
||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
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...
|
||||
//
|
||||
switch (self::$m_iErrorMode)
|
||||
{
|
||||
case DICT_ERR_STRING:
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
return $sStringCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sDefault;
|
||||
}
|
||||
break;
|
||||
|
||||
case DICT_ERR_EXCEPTION:
|
||||
default:
|
||||
throw new DictExceptionMissingString(self::$m_sCurrentLanguage, $sStringCode);
|
||||
break;
|
||||
}
|
||||
return 'bug!';
|
||||
}
|
||||
|
||||
|
||||
public static function Format($sFormatCode /*, ... arguments ....*/)
|
||||
{
|
||||
$sLocalizedFormat = self::S($sFormatCode);
|
||||
$aArguments = func_get_args();
|
||||
|
||||
if ($sLocalizedFormat == $sFormatCode)
|
||||
{
|
||||
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
}
|
||||
|
||||
array_shift($aArguments);
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
public static function Add($sLanguageCode, $sEnglishLanguageDesc, $sLocalizedLanguageDesc, $aEntries)
|
||||
{
|
||||
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
{
|
||||
self::$m_aLanguages[$sLanguageCode] = array('description' => $sEnglishLanguageDesc, 'localized_description' => $sLocalizedLanguageDesc);
|
||||
self::$m_aData[$sLanguageCode] = array();
|
||||
}
|
||||
self::$m_aData[$sLanguageCode] = array_merge(self::$m_aData[$sLanguageCode], $aEntries);
|
||||
}
|
||||
|
||||
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]))
|
||||
{
|
||||
$aMissing[$sStringCode] = $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
|
||||
{
|
||||
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
|
||||
{
|
||||
$aUnexpected[$sStringCode] = $sValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The value exists in the reference
|
||||
$sRefValue = self::$m_aData[$sLanguageRef][$sStringCode];
|
||||
if ($sValue == $sRefValue)
|
||||
{
|
||||
$aNotTranslated[$sStringCode] = $sValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aOK[$sStringCode] = $sValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
|
||||
}
|
||||
|
||||
public static function Dump()
|
||||
{
|
||||
MyHelpers::var_dump_html(self::$m_aData);
|
||||
}
|
||||
}
|
||||
?>
|
||||
140
core/email.class.inc.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Send an mail (for notification, testing,... purposes)
|
||||
* #@# TODO - replace by a more sophisticated mean (and update the prototype)
|
||||
*
|
||||
* @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 EMail
|
||||
{
|
||||
protected $m_sBody;
|
||||
protected $m_sSubject;
|
||||
protected $m_sTo;
|
||||
protected $m_aHeaders; // array of key=>value
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->m_sTo = '';
|
||||
$this->m_sSubject = '';
|
||||
$this->m_sBody = '';
|
||||
$this->m_aHeaders = array();
|
||||
}
|
||||
|
||||
|
||||
// Errors management : not that simple because we need that function to be
|
||||
// executed in the background, while making sure that any issue would be reported clearly
|
||||
protected $m_aMailErrors; //array of strings explaining the issues
|
||||
|
||||
public function mail_error_handler($errno, $errstr, $errfile, $errline)
|
||||
{
|
||||
$sCleanMessage= str_replace("mail() [<a href='function.mail'>function.mail</a>]: ", "", $errstr);
|
||||
$this->m_aMailErrors[] = $sCleanMessage;
|
||||
}
|
||||
|
||||
|
||||
// returns a list of issues if any
|
||||
public function Send()
|
||||
{
|
||||
$sHeaders = 'MIME-Version: 1.0' . "\r\n";
|
||||
// ! the case is important for MS-Outlook
|
||||
$sHeaders .= 'Content-Type: text/html; charset=UTF-8' . "\r\n";
|
||||
$sHeaders .= 'Content-Transfer-Encoding: 8bit' . "\r\n";
|
||||
foreach ($this->m_aHeaders as $sKey => $sValue)
|
||||
{
|
||||
$sHeaders .= "$sKey: $sValue\r\n";
|
||||
}
|
||||
|
||||
// Under Windows (not yet proven for Linux/PHP) mail may issue a warning
|
||||
// that I could not mask (tried error_reporting(), etc.)
|
||||
$this->m_aMailErrors = array();
|
||||
set_error_handler(array($this, 'mail_error_handler'));
|
||||
$bRes = mail
|
||||
(
|
||||
$this->m_sTo,
|
||||
$this->m_sSubject,
|
||||
$this->m_sBody,
|
||||
$sHeaders
|
||||
);
|
||||
restore_error_handler();
|
||||
if (!$bRes && empty($this->m_aMailErrors))
|
||||
{
|
||||
$this->m_aMailErrors[] = 'Unknown reason';
|
||||
}
|
||||
return $this->m_aMailErrors;
|
||||
}
|
||||
|
||||
protected function AddToHeader($sKey, $sValue)
|
||||
{
|
||||
if (strlen($sValue) > 0)
|
||||
{
|
||||
$this->m_aHeaders[$sKey] = $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
public function SetReferences($sReferences)
|
||||
{
|
||||
$this->AddToHeader('References', $sReferences);
|
||||
}
|
||||
|
||||
public function SetBody($sBody)
|
||||
{
|
||||
$this->m_sBody = $sBody;
|
||||
}
|
||||
|
||||
public function SetSubject($aSubject)
|
||||
{
|
||||
$this->m_sSubject = $aSubject;
|
||||
}
|
||||
|
||||
public function SetRecipientTO($sAddress)
|
||||
{
|
||||
$this->m_sTo = $sAddress;
|
||||
}
|
||||
|
||||
public function SetRecipientCC($sAddress)
|
||||
{
|
||||
$this->AddToHeader('Cc', $sAddress);
|
||||
}
|
||||
|
||||
public function SetRecipientBCC($sAddress)
|
||||
{
|
||||
$this->AddToHeader('Bcc', $sAddress);
|
||||
}
|
||||
|
||||
public function SetRecipientFrom($sAddress)
|
||||
{
|
||||
$this->AddToHeader('From', $sAddress);
|
||||
|
||||
// This is required on Windows because otherwise I would get the error
|
||||
// "sendmail_from" not set in php.ini" even if it is correctly working
|
||||
// (apparently, once it worked the SMTP server won't claim anymore for it)
|
||||
ini_set("sendmail_from", $sAddress);
|
||||
}
|
||||
|
||||
public function SetRecipientReplyTo($sAddress)
|
||||
{
|
||||
$this->AddToHeader('Reply-To', $sAddress);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
261
core/event.class.inc.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Persistent class Event and derived
|
||||
* Application internal events
|
||||
* There is also a file log
|
||||
*
|
||||
* @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 cmdbAbstractObject
|
||||
{
|
||||
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",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new 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())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('message', 'date', 'userinfo')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'finalclass', '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 EventNotification 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_notification",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> "", "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "jointype"=> "", "allowed_values"=>null, "sql"=>"action_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values"=>null, "sql"=>"object_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'trigger_id', 'action_id', 'object_id')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo')); // 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 EventNotificationEmail extends EventNotification
|
||||
{
|
||||
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_email",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
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("cc", array("allowed_values"=>null, "sql"=>"cc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("bcc", array("allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
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 AttributeText("body", array("allowed_values"=>null, "sql"=>"body", "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')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', '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
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class EventIssue 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_issue",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("issue", array("allowed_values"=>null, "sql"=>"issue", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("impact", array("allowed_values"=>null, "sql"=>"impact", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("page", array("allowed_values"=>null, "sql"=>"page", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributePropertySet("arguments_post", array("allowed_values"=>null, "sql"=>"arguments_post", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributePropertySet("arguments_get", array("allowed_values"=>null, "sql"=>"arguments_get", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeTable("callstack", array("allowed_values"=>null, "sql"=>"callstack", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributePropertySet("data", array("allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'issue', 'impact', 'page', 'arguments_post', 'arguments_get', 'callstack', 'data')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'issue', 'impact')); // 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()
|
||||
{
|
||||
// Init page information: name, arguments
|
||||
//
|
||||
$this->Set('page', @$GLOBALS['_SERVER']['SCRIPT_NAME']);
|
||||
|
||||
if (array_key_exists('_GET', $GLOBALS) && is_array($GLOBALS['_GET']))
|
||||
{
|
||||
$this->Set('arguments_get', $GLOBALS['_GET']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->Set('arguments_get', array());
|
||||
}
|
||||
|
||||
if (array_key_exists('_POST', $GLOBALS) && is_array($GLOBALS['_POST']))
|
||||
{
|
||||
$aPost = array();
|
||||
foreach($GLOBALS['_POST'] as $sKey => $sValue)
|
||||
{
|
||||
if (is_string($sValue))
|
||||
{
|
||||
if (strlen($sValue) < 256)
|
||||
{
|
||||
$aPost[$sKey] = $sValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPost[$sKey] = "!long string: ".strlen($sValue). " chars";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a string
|
||||
$aPost[$sKey] = (string) $sValue;
|
||||
}
|
||||
}
|
||||
$this->Set('arguments_post', $aPost);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->Set('arguments_post', array());
|
||||
}
|
||||
|
||||
$sLength = strlen($this->Get('issue'));
|
||||
if ($sLength > 255)
|
||||
{
|
||||
$this->Set('issue', substr($this->Get('issue'), 0, 200)." -truncated ($sLength chars)");
|
||||
}
|
||||
|
||||
$sLength = strlen($this->Get('impact'));
|
||||
if ($sLength > 255)
|
||||
{
|
||||
$this->Set('impact', substr($this->Get('impact'), 0, 200)." -truncated ($sLength chars)");
|
||||
}
|
||||
|
||||
$sLength = strlen($this->Get('page'));
|
||||
if ($sLength > 255)
|
||||
{
|
||||
$this->Set('page', substr($this->Get('page'), 0, 200)." -truncated ($sLength chars)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class EventWebService 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_webservice",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("verb", array("allowed_values"=>null, "sql"=>"verb", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
//MetaModel::Init_AddAttribute(new AttributeStructure("arguments", array("allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeBoolean("result", array("allowed_values"=>null, "sql"=>"result", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("log_info", array("allowed_values"=>null, "sql"=>"log_info", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("log_warning", array("allowed_values"=>null, "sql"=>"log_warning", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("log_error", array("allowed_values"=>null, "sql"=>"log_error", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeText("data", array("allowed_values"=>null, "sql"=>"data", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'verb', 'result', 'log_info', 'log_warning', 'log_error', 'data')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'verb', 'result')); // 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
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
575
core/expression.class.inc.php
Normal file
@@ -0,0 +1,575 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* General definition of an expression tree (could be OQL, SQL or whatever)
|
||||
*
|
||||
* @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 MissingQueryArgument extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
abstract class Expression
|
||||
{
|
||||
// recursive translation of identifiers
|
||||
abstract public function Translate($aTranslationData, $bMatchAll = true);
|
||||
|
||||
// recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
|
||||
abstract public function Render(&$aArgs = null, $bRetrofitParams = false);
|
||||
|
||||
// recursively builds an array of class => fieldname
|
||||
abstract public function ListRequiredFields();
|
||||
|
||||
abstract public function IsTrue();
|
||||
|
||||
public function RequiresField($sClass, $sFieldName)
|
||||
{
|
||||
// #@# todo - optimize : this is called quite often when building a single query !
|
||||
$aRequired = $this->ListRequiredFields();
|
||||
if (!in_array($sClass.'.'.$sFieldName, $aRequired)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
{
|
||||
return base64_encode($this->Render());
|
||||
}
|
||||
|
||||
static public function unserialize($sValue)
|
||||
{
|
||||
return self::FromOQL(base64_decode($sValue));
|
||||
}
|
||||
|
||||
static public function FromOQL($sConditionExpr)
|
||||
{
|
||||
$oOql = new OqlInterpreter($sConditionExpr);
|
||||
$oExpression = $oOql->ParseExpression();
|
||||
|
||||
return $oExpression;
|
||||
}
|
||||
|
||||
public function LogAnd($oExpr)
|
||||
{
|
||||
if ($this->IsTrue()) return clone $oExpr;
|
||||
if ($oExpr->IsTrue()) return clone $this;
|
||||
return new BinaryExpression($this, 'AND', $oExpr);
|
||||
}
|
||||
|
||||
public function LogOr($oExpr)
|
||||
{
|
||||
return new BinaryExpression($this, 'OR', $oExpr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BinaryExpression extends Expression
|
||||
{
|
||||
protected $m_oLeftExpr; // filter code or an SQL expression (later?)
|
||||
protected $m_oRightExpr;
|
||||
protected $m_sOperator;
|
||||
|
||||
public function __construct($oLeftExpr, $sOperator, $oRightExpr)
|
||||
{
|
||||
if (!is_object($oLeftExpr))
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the left hand', array('found_type' => gettype($oLeftExpr)));
|
||||
}
|
||||
if (!is_object($oRightExpr))
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the right hand', array('found_type' => gettype($oRightExpr)));
|
||||
}
|
||||
if (!$oLeftExpr instanceof Expression)
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the left hand', array('found_class' => get_class($oLeftExpr)));
|
||||
}
|
||||
if (!$oRightExpr instanceof Expression)
|
||||
{
|
||||
throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr)));
|
||||
}
|
||||
$this->m_oLeftExpr = $oLeftExpr;
|
||||
$this->m_oRightExpr = $oRightExpr;
|
||||
$this->m_sOperator = $sOperator;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
if ($this->m_sOperator == 'AND')
|
||||
{
|
||||
if ($this->m_oLeftExpr->IsTrue() && $this->m_oLeftExpr->IsTrue()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetLeftExpr()
|
||||
{
|
||||
return $this->m_oLeftExpr;
|
||||
}
|
||||
|
||||
public function GetRightExpr()
|
||||
{
|
||||
return $this->m_oRightExpr;
|
||||
}
|
||||
|
||||
public function GetOperator()
|
||||
{
|
||||
return $this->m_sOperator;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$sOperator = $this->GetOperator();
|
||||
$sLeft = $this->GetLeftExpr()->Render($aArgs, $bRetrofitParams);
|
||||
$sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams);
|
||||
return "($sLeft $sOperator $sRight)";
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
$oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll);
|
||||
$oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll);
|
||||
return new BinaryExpression($oLeft, $this->GetOperator(), $oRight);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
$aLeft = $this->GetLeftExpr()->ListRequiredFields();
|
||||
$aRight = $this->GetRightExpr()->ListRequiredFields();
|
||||
return array_merge($aLeft, $aRight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class UnaryExpression extends Expression
|
||||
{
|
||||
protected $m_value;
|
||||
|
||||
public function __construct($value)
|
||||
{
|
||||
$this->m_value = $value;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return ($this->m_value == 1);
|
||||
}
|
||||
|
||||
public function GetValue()
|
||||
{
|
||||
return $this->m_value;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
if ($bRetrofitParams)
|
||||
{
|
||||
$iParamIndex = count($aArgs) + 1; // 1-based indexation
|
||||
$aArgs['param'.$iParamIndex] = $this->m_value;
|
||||
return ':param'.$iParamIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
return CMDBSource::Quote($this->m_value);
|
||||
}
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
class ScalarExpression extends UnaryExpression
|
||||
{
|
||||
public function __construct($value)
|
||||
{
|
||||
if (!is_scalar($value))
|
||||
{
|
||||
throw new CoreException('Attempt to create a scalar expression from a non scalar', array('var_type'=>gettype($value)));
|
||||
}
|
||||
parent::__construct($value);
|
||||
}
|
||||
}
|
||||
|
||||
class TrueExpression extends ScalarExpression
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(1);
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class FalseExpression extends ScalarExpression
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(0);
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class FieldExpression extends UnaryExpression
|
||||
{
|
||||
protected $m_sParent;
|
||||
protected $m_sName;
|
||||
|
||||
public function __construct($sName, $sParent = '')
|
||||
{
|
||||
parent::__construct("$sParent.$sName");
|
||||
|
||||
$this->m_sParent = $sParent;
|
||||
$this->m_sName = $sName;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetParent() {return $this->m_sParent;}
|
||||
public function GetName() {return $this->m_sName;}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
if (empty($this->m_sParent))
|
||||
{
|
||||
return "`{$this->m_sName}`";
|
||||
}
|
||||
return "`{$this->m_sParent}`.`{$this->m_sName}`";
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
if (!array_key_exists($this->m_sParent, $aTranslationData))
|
||||
{
|
||||
if ($bMatchAll) throw new CoreException('Unknown parent id in translation table', array('parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData)));
|
||||
return clone $this;
|
||||
}
|
||||
if (!array_key_exists($this->m_sName, $aTranslationData[$this->m_sParent]))
|
||||
{
|
||||
if (!array_key_exists('*', $aTranslationData[$this->m_sParent]))
|
||||
{
|
||||
// #@# debug - if ($bMatchAll) MyHelpers::var_dump_html($aTranslationData, true);
|
||||
if ($bMatchAll) throw new CoreException('Unknown name in translation table', array('name' => $this->m_sName, 'parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData[$this->m_sParent])));
|
||||
return clone $this;
|
||||
}
|
||||
$sNewParent = $aTranslationData[$this->m_sParent]['*'];
|
||||
$sNewName = $this->m_sName;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sNewParent = $aTranslationData[$this->m_sParent][$this->m_sName][0];
|
||||
$sNewName = $aTranslationData[$this->m_sParent][$this->m_sName][1];
|
||||
}
|
||||
return new FieldExpression($sNewName, $sNewParent);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
return array($this->m_sParent.'.'.$this->m_sName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VariableExpression extends UnaryExpression
|
||||
{
|
||||
protected $m_sName;
|
||||
|
||||
public function __construct($sName)
|
||||
{
|
||||
parent::__construct($sName);
|
||||
|
||||
$this->m_sName = $sName;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetName() {return $this->m_sName;}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
if (is_null($aArgs))
|
||||
{
|
||||
return ':'.$this->m_sName;
|
||||
}
|
||||
elseif (array_key_exists($this->m_sName, $aArgs))
|
||||
{
|
||||
return CMDBSource::Quote($aArgs[$this->m_sName]);
|
||||
}
|
||||
elseif ($bRetrofitParams)
|
||||
{
|
||||
//$aArgs[$this->m_sName] = null;
|
||||
return ':'.$this->m_sName;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Temporary, until we implement functions and expression casting!
|
||||
// ... or until we implement a real full text search based in the MATCH() expression
|
||||
class ListExpression extends Expression
|
||||
{
|
||||
protected $m_aExpressions;
|
||||
|
||||
public function __construct($aExpressions)
|
||||
{
|
||||
$this->m_aExpressions = $aExpressions;
|
||||
}
|
||||
|
||||
public static function FromScalars($aScalars)
|
||||
{
|
||||
$aExpressions = array();
|
||||
foreach($aScalars as $value)
|
||||
{
|
||||
$aExpressions[] = new ScalarExpression($value);
|
||||
}
|
||||
return new ListExpression($aExpressions);
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetItems()
|
||||
{
|
||||
return $this->m_aExpressions;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
|
||||
}
|
||||
return '('.implode(', ', $aRes).')';
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
|
||||
}
|
||||
return new ListExpression($aRes);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes = array_merge($aRes, $oExpr->ListRequiredFields());
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FunctionExpression extends Expression
|
||||
{
|
||||
protected $m_sVerb;
|
||||
protected $m_aArgs; // array of expressions
|
||||
|
||||
public function __construct($sVerb, $aArgExpressions)
|
||||
{
|
||||
$this->m_sVerb = $sVerb;
|
||||
$this->m_aArgs = $aArgExpressions;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetVerb()
|
||||
{
|
||||
return $this->m_sVerb;
|
||||
}
|
||||
|
||||
public function GetArgs()
|
||||
{
|
||||
return $this->m_aArgs;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Render($aArgs, $bRetrofitParams);
|
||||
}
|
||||
return $this->m_sVerb.'('.implode(', ', $aRes).')';
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
|
||||
}
|
||||
return new FunctionExpression($this->m_sVerb, $aRes);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aArgs as $oExpr)
|
||||
{
|
||||
$aRes = array_merge($aRes, $oExpr->ListRequiredFields());
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
}
|
||||
|
||||
class IntervalExpression extends Expression
|
||||
{
|
||||
protected $m_oValue; // expression
|
||||
protected $m_sUnit;
|
||||
|
||||
public function __construct($oValue, $sUnit)
|
||||
{
|
||||
$this->m_oValue = $oValue;
|
||||
$this->m_sUnit = $sUnit;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetValue()
|
||||
{
|
||||
return $this->m_oValue;
|
||||
}
|
||||
|
||||
public function GetUnit()
|
||||
{
|
||||
return $this->m_sUnit;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
return new IntervalExpression($this->m_oValue->Translate($aTranslationData, $bMatchAll), $this->m_sUnit);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
class CharConcatExpression extends Expression
|
||||
{
|
||||
protected $m_aExpressions;
|
||||
|
||||
public function __construct($aExpressions)
|
||||
{
|
||||
$this->m_aExpressions = $aExpressions;
|
||||
}
|
||||
|
||||
public function IsTrue()
|
||||
{
|
||||
// return true if we are certain that it will be true
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetItems()
|
||||
{
|
||||
return $this->m_aExpressions;
|
||||
}
|
||||
|
||||
// recursive rendering
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$sCol = $oExpr->Render($aArgs, $bRetrofitParams);
|
||||
// Concat will be globally NULL if one single argument is null !
|
||||
$aRes[] = "COALESCE($sCol, '')";
|
||||
}
|
||||
return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)";
|
||||
}
|
||||
|
||||
public function Translate($aTranslationData, $bMatchAll = true)
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll);
|
||||
}
|
||||
return new CharConcatExpression($aRes);
|
||||
}
|
||||
|
||||
public function ListRequiredFields()
|
||||
{
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
$aRes = array_merge($aRes, $oExpr->ListRequiredFields());
|
||||
}
|
||||
return $aRes;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
225
core/filterdef.class.inc.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Definition of a filter
|
||||
* Most of the time, a filter corresponds to an attribute, but we could imagine other search criteria
|
||||
*
|
||||
* @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('MyHelpers.class.inc.php');
|
||||
|
||||
|
||||
/**
|
||||
* Definition of a filter (could be made out of an existing attribute, or from an expression)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class FilterDefinition
|
||||
{
|
||||
abstract public function GetType();
|
||||
abstract public function GetTypeDesc();
|
||||
|
||||
protected $m_sCode;
|
||||
private $m_aParams = array();
|
||||
protected function Get($sParamName) {return $this->m_aParams[$sParamName];}
|
||||
|
||||
public function __construct($sCode, $aParams = array())
|
||||
{
|
||||
$this->m_sCode = $sCode;
|
||||
$this->m_aParams = $aParams;
|
||||
$this->ConsistencyCheck();
|
||||
}
|
||||
|
||||
public function OverloadParams($aParams)
|
||||
{
|
||||
foreach ($aParams as $sParam => $value)
|
||||
{
|
||||
if (!array_key_exists($sParam, $this->m_aParams))
|
||||
{
|
||||
throw new CoreException("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}");
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aParams[$sParam] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// to be overloaded
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
private function ConsistencyCheck()
|
||||
{
|
||||
// Check that any mandatory param has been specified
|
||||
//
|
||||
$aExpectedParams = $this->ListExpectedParams();
|
||||
foreach($aExpectedParams as $sParamName)
|
||||
{
|
||||
if (!array_key_exists($sParamName, $this->m_aParams))
|
||||
{
|
||||
$aBacktrace = debug_backtrace();
|
||||
$sTargetClass = $aBacktrace[2]["class"];
|
||||
$sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"];
|
||||
throw new CoreException("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function GetCode() {return $this->m_sCode;}
|
||||
abstract public function GetLabel();
|
||||
abstract public function GetValuesDef();
|
||||
|
||||
// returns an array of opcode=>oplabel (e.g. "differs from")
|
||||
abstract public function GetOperators();
|
||||
// returns an opcode
|
||||
abstract public function GetLooseOperator();
|
||||
abstract public function GetSQLExpressions();
|
||||
|
||||
// Wrapper - no need for overloading this one
|
||||
public function GetOpDescription($sOpCode)
|
||||
{
|
||||
$aOperators = $this->GetOperators();
|
||||
if (!array_key_exists($sOpCode, $aOperators))
|
||||
{
|
||||
throw new CoreException("Unknown operator '$sOpCode'");
|
||||
}
|
||||
|
||||
return $aOperators[$sOpCode];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match against the object unique identifier
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class FilterPrivateKey extends FilterDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("id_field"));
|
||||
}
|
||||
|
||||
public function GetType() {return "PrivateKey";}
|
||||
public function GetTypeDesc() {return "Match against object identifier";}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return "Object Private Key";
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetOperators()
|
||||
{
|
||||
return array(
|
||||
"="=>"equals",
|
||||
"!="=>"differs from",
|
||||
"IN"=>"in",
|
||||
"NOTIN"=>"not in"
|
||||
);
|
||||
}
|
||||
public function GetLooseOperator()
|
||||
{
|
||||
return "IN";
|
||||
}
|
||||
|
||||
public function GetSQLExpressions()
|
||||
{
|
||||
return array(
|
||||
'' => $this->Get("id_field"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match against an existing attribute (the attribute type will determine the available operators)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class FilterFromAttribute extends FilterDefinition
|
||||
{
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array_merge(parent::ListExpectedParams(), array("refattribute"));
|
||||
}
|
||||
|
||||
public function __construct($oRefAttribute, $aParam = array())
|
||||
{
|
||||
// In this very specific case, the code is the one of the attribute
|
||||
// (this to get a very very simple syntax upon declaration)
|
||||
$aParam["refattribute"] = $oRefAttribute;
|
||||
parent::__construct($oRefAttribute->GetCode(), $aParam);
|
||||
}
|
||||
|
||||
public function GetType() {return "Basic";}
|
||||
public function GetTypeDesc() {return "Match against field contents";}
|
||||
|
||||
public function __GetRefAttribute() // for checking purposes only !!!
|
||||
{
|
||||
return $oAttDef = $this->Get("refattribute");
|
||||
}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetLabel();
|
||||
}
|
||||
|
||||
public function GetValuesDef()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetValuesDef();
|
||||
}
|
||||
|
||||
public function GetAllowedValues($aArgs = array(), $sContains = '')
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetAllowedValues($aArgs, $sContains);
|
||||
}
|
||||
|
||||
public function GetOperators()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetBasicFilterOperators();
|
||||
}
|
||||
public function GetLooseOperator()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetBasicFilterLooseOperator();
|
||||
}
|
||||
|
||||
public function GetSQLExpressions()
|
||||
{
|
||||
$oAttDef = $this->Get("refattribute");
|
||||
return $oAttDef->GetSQLExpressions();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
196
core/kpi.class.inc.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Measures operations duration, memory usage, etc. (and some other KPIs)
|
||||
*
|
||||
* @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 ExecutionKPI
|
||||
{
|
||||
static protected $m_bEnabled_Duration = false;
|
||||
static protected $m_bEnabled_Memory = false;
|
||||
|
||||
static protected $m_aStats = array();
|
||||
|
||||
protected $m_fStarted = null;
|
||||
protected $m_iInitialMemory = null;
|
||||
|
||||
static public function EnableDuration()
|
||||
{
|
||||
self::$m_bEnabled_Duration = true;
|
||||
}
|
||||
|
||||
static public function EnableMemory()
|
||||
{
|
||||
self::$m_bEnabled_Memory = true;
|
||||
}
|
||||
|
||||
static public function ReportStats()
|
||||
{
|
||||
foreach (self::$m_aStats as $sOperation => $aOpStats)
|
||||
{
|
||||
echo "<h2>KPIs for $sOperation</h2>\n";
|
||||
$fTotalOp = 0;
|
||||
$iTotalOp = 0;
|
||||
$fMinOp = null;
|
||||
$fMaxOp = 0;
|
||||
echo "<ul>\n";
|
||||
foreach ($aOpStats as $sArguments => $aEvents)
|
||||
{
|
||||
$fTotalInter = 0;
|
||||
$fMinInter = null;
|
||||
$fMaxInter = 0;
|
||||
foreach ($aEvents as $fDuration)
|
||||
{
|
||||
$fTotalInter += $fDuration;
|
||||
$fMinInter = is_null($fMinInter) ? $fDuration : min($fMinInter, $fDuration);
|
||||
$fMaxInter = max($fMaxInter, $fDuration);
|
||||
|
||||
$fMinOp = is_null($fMinOp) ? $fDuration : min($fMinOp, $fDuration);
|
||||
$fMaxOp = max($fMaxOp, $fDuration);
|
||||
}
|
||||
$fTotalOp += $fTotalInter;
|
||||
$iTotalOp++;
|
||||
|
||||
$iCountInter = count($aEvents);
|
||||
$sTotalInter = round($fTotalInter, 3)."s";
|
||||
if ($iCountInter > 1)
|
||||
{
|
||||
$sMinInter = round($fMinInter, 3)."s";
|
||||
$sMaxInter = round($fMaxInter, 3)."s";
|
||||
$sTimeDesc = "$sTotalInter (from $sMinInter to $sMaxInter) in $iCountInter times";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sTimeDesc = "$sTotalInter";
|
||||
}
|
||||
echo "<li>Spent $sTimeDesc, on: <span style=\"font-size:60%\">$sArguments</span></li>\n";
|
||||
}
|
||||
echo "</ul>\n";
|
||||
echo "<ul>Sumary for $sOperation\n";
|
||||
echo "<li>Total: $iTotalOp (".round($fTotalOp, 3).")</li>\n";
|
||||
echo "<li>Min: ".round($fMinOp, 3)."</li>\n";
|
||||
echo "<li>Max: ".round($fMaxOp, 3)."</li>\n";
|
||||
echo "<li>Avg: ".round($fTotalOp / $iTotalOp, 3)."</li>\n";
|
||||
echo "</ul>\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ResetCounters();
|
||||
}
|
||||
|
||||
// Get the duration since startup, and reset the counter for the next measure
|
||||
//
|
||||
public function ComputeAndReport($sOperationDesc)
|
||||
{
|
||||
if (self::$m_bEnabled_Duration)
|
||||
{
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
$this->Report($sOperationDesc.' / duration: '.round($fDuration, 3));
|
||||
}
|
||||
|
||||
if (self::$m_bEnabled_Memory)
|
||||
{
|
||||
$iMemory = self::memory_get_usage();
|
||||
$iMemoryUsed = $iMemory - $this->m_iInitialMemory;
|
||||
$this->Report($sOperationDesc.' / memory: '.self::MemStr($iMemoryUsed).' (Total: '.self::MemStr($iMemory).')');
|
||||
if (function_exists('memory_get_peak_usage'))
|
||||
{
|
||||
$iMemoryPeak = memory_get_peak_usage();
|
||||
$this->Report($sOperationDesc.' / memory peak: '.self::MemStr($iMemoryPeak));
|
||||
}
|
||||
}
|
||||
|
||||
$this->ResetCounters();
|
||||
}
|
||||
|
||||
public function ComputeStats($sOperation, $sArguments)
|
||||
{
|
||||
if (self::$m_bEnabled_Duration)
|
||||
{
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
self::$m_aStats[$sOperation][$sArguments][] = $fDuration;
|
||||
}
|
||||
}
|
||||
|
||||
protected function ResetCounters()
|
||||
{
|
||||
if (self::$m_bEnabled_Duration)
|
||||
{
|
||||
$this->m_fStarted = MyHelpers::getmicrotime();
|
||||
}
|
||||
|
||||
if (self::$m_bEnabled_Memory)
|
||||
{
|
||||
$this->m_iInitialMemory = self::memory_get_usage();
|
||||
}
|
||||
}
|
||||
|
||||
protected function Report($sText)
|
||||
{
|
||||
echo "$sText<br/>\n";
|
||||
}
|
||||
|
||||
static protected function MemStr($iMemory)
|
||||
{
|
||||
return round($iMemory / 1024).' Kb';
|
||||
}
|
||||
|
||||
static protected function memory_get_usage()
|
||||
{
|
||||
if (function_exists('memory_get_usage'))
|
||||
{
|
||||
return memory_get_usage(true);
|
||||
}
|
||||
|
||||
// Copied from the PHP manual
|
||||
//
|
||||
//If its Windows
|
||||
//Tested on Win XP Pro SP2. Should work on Win 2003 Server too
|
||||
//Doesn't work for 2000
|
||||
//If you need it to work for 2000 look at http://us2.php.net/manual/en/function.memory-get-usage.php#54642
|
||||
if (substr(PHP_OS,0,3) == 'WIN')
|
||||
{
|
||||
$output = array();
|
||||
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST', $output);
|
||||
|
||||
return preg_replace( '/[\D]/', '', $output[5] ) * 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
//We now assume the OS is UNIX
|
||||
//Tested on Mac OS X 10.4.6 and Linux Red Hat Enterprise 4
|
||||
//This should work on most UNIX systems
|
||||
$pid = getmypid();
|
||||
exec("ps -eo%mem,rss,pid | grep $pid", $output);
|
||||
$output = explode(" ", $output[0]);
|
||||
//rss is given in 1024 byte units
|
||||
return $output[1] * 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
146
core/log.class.inc.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* File logging
|
||||
*
|
||||
* @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 FileLog
|
||||
{
|
||||
protected $m_sFile = ''; // log is disabled if this is empty
|
||||
|
||||
public function __construct($sFileName = '')
|
||||
{
|
||||
$this->m_sFile = $sFileName;
|
||||
}
|
||||
|
||||
public function Error($sText)
|
||||
{
|
||||
self::Write("Error | ".$sText);
|
||||
}
|
||||
|
||||
public function Warning($sText)
|
||||
{
|
||||
self::Write("Warning | ".$sText);
|
||||
}
|
||||
|
||||
public function Info($sText)
|
||||
{
|
||||
self::Write("Info | ".$sText);
|
||||
}
|
||||
|
||||
public function Ok($sText)
|
||||
{
|
||||
self::Write("Ok | ".$sText);
|
||||
}
|
||||
|
||||
protected function Write($sText)
|
||||
{
|
||||
if (strlen($this->m_sFile) == 0) return;
|
||||
|
||||
$hLogFile = @fopen($this->m_sFile, 'a');
|
||||
if ($hLogFile !== false)
|
||||
{
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
fclose($hLogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SetupLog
|
||||
{
|
||||
protected static $m_oFileLog;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
self::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
public static function Error($sText)
|
||||
{
|
||||
self::$m_oFileLog->Error($sText);
|
||||
}
|
||||
public static function Warning($sText)
|
||||
{
|
||||
self::$m_oFileLog->Warning($sText);
|
||||
}
|
||||
public static function Info($sText)
|
||||
{
|
||||
self::$m_oFileLog->Info($sText);
|
||||
}
|
||||
public static function Ok($sText)
|
||||
{
|
||||
self::$m_oFileLog->Ok($sText);
|
||||
}
|
||||
}
|
||||
|
||||
class IssueLog
|
||||
{
|
||||
protected static $m_oFileLog;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
self::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
public static function Error($sText)
|
||||
{
|
||||
self::$m_oFileLog->Error($sText);
|
||||
}
|
||||
public static function Warning($sText)
|
||||
{
|
||||
self::$m_oFileLog->Warning($sText);
|
||||
}
|
||||
public static function Info($sText)
|
||||
{
|
||||
self::$m_oFileLog->Info($sText);
|
||||
}
|
||||
public static function Ok($sText)
|
||||
{
|
||||
self::$m_oFileLog->Ok($sText);
|
||||
}
|
||||
}
|
||||
|
||||
class ToolsLog
|
||||
{
|
||||
protected static $m_oFileLog;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
self::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
public static function Error($sText)
|
||||
{
|
||||
self::$m_oFileLog->Error($sText);
|
||||
}
|
||||
public static function Warning($sText)
|
||||
{
|
||||
self::$m_oFileLog->Warning($sText);
|
||||
}
|
||||
public static function Info($sText)
|
||||
{
|
||||
self::$m_oFileLog->Info($sText);
|
||||
}
|
||||
public static function Ok($sText)
|
||||
{
|
||||
self::$m_oFileLog->Ok($sText);
|
||||
}
|
||||
}
|
||||
?>
|
||||
3766
core/metamodel.class.php
Normal file
37
core/modulehandler.class.inc.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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 ModuleHandler
|
||||
* Defines the API to implement module specific actions during page execution
|
||||
*
|
||||
* @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 ModuleHandlerAPI
|
||||
{
|
||||
public static function OnMetaModelStarted()
|
||||
{
|
||||
}
|
||||
|
||||
public static function OnMenuCreation()
|
||||
{
|
||||
}
|
||||
}
|
||||
?>
|
||||
3
core/oql/build.cmd
Normal file
@@ -0,0 +1,3 @@
|
||||
c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\LexerGenerator\cli.php" oql-lexer.plex
|
||||
c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\ParserGenerator\cli.php" oql-parser.y
|
||||
pause
|
||||
7
core/oql/exclude.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
#
|
||||
# The following source files are not re-distributed with the "build" of the application
|
||||
# since they are used solely for constructing other files during the build process
|
||||
#
|
||||
build.cmd
|
||||
oql-lexer.plex
|
||||
oql-parser.y
|
||||
564
core/oql/oql-lexer.php
Normal file
@@ -0,0 +1,564 @@
|
||||
<?php
|
||||
|
||||
// Notes (from the source file: oql-lexer.plex) - Romain
|
||||
//
|
||||
// The strval rule is a little bit cryptic.
|
||||
// This is due to both a bug in the lexer generator and the complexity of our need
|
||||
// The rule means: either a quoted string with ", or a quoted string with '
|
||||
// literal " (resp. ') must be escaped by a \
|
||||
// \ must be escaped by an additional \
|
||||
//
|
||||
// Here are the issues and limitation found in the lexer generator:
|
||||
// * Matching simple quotes is an issue, because regexp are not correctly escaped (and the ESC code is escaped itself)
|
||||
// Workaround: insert '.chr(39).' which will be a real ' in the end
|
||||
// * Matching an alternate regexp is an issue because you must specify "|^...."
|
||||
// and the regexp parser will not accept that syntax
|
||||
// Workaround: insert '.chr(94).' which will be a real ^
|
||||
//
|
||||
// Let's analyze an overview of the regexp, we have
|
||||
// 1) The strval rule in the lexer definition
|
||||
// /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
// 2) Becomes the php expression in the lexer
|
||||
// (note the escaped double quotes, hopefully having no effect, but showing where the issue is!)
|
||||
// $myRegexp = '/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/';
|
||||
//
|
||||
// To be fixed in LexerGenerator/Parser.y, in doLongestMatch (doFirstMatch is ok)
|
||||
//
|
||||
//
|
||||
// Now, let's explain how the regexp has been designed.
|
||||
// Here is a simplified version, dealing with simple quotes, and based on the assumption that the lexer generator has been fixed!
|
||||
// The strval rule in the lexer definition
|
||||
// /'([^\\']*(\\')*(\\\\)*)*'/
|
||||
// This means anything containing \\ or \' or any other char but a standalone ' or \
|
||||
// This means ' or \ could not be found without a preceding \
|
||||
//
|
||||
class OQLLexerRaw
|
||||
{
|
||||
protected $data; // input string
|
||||
public $token; // token id
|
||||
public $value; // token string representation
|
||||
protected $line; // current line
|
||||
protected $count; // current column
|
||||
|
||||
function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->count = 0;
|
||||
$this->line = 1;
|
||||
}
|
||||
|
||||
|
||||
private $_yy_state = 1;
|
||||
private $_yy_stack = array();
|
||||
|
||||
function yylex()
|
||||
{
|
||||
return $this->{'yylex' . $this->_yy_state}();
|
||||
}
|
||||
|
||||
function yypushstate($state)
|
||||
{
|
||||
array_push($this->_yy_stack, $this->_yy_state);
|
||||
$this->_yy_state = $state;
|
||||
}
|
||||
|
||||
function yypopstate()
|
||||
{
|
||||
$this->_yy_state = array_pop($this->_yy_stack);
|
||||
}
|
||||
|
||||
function yybegin($state)
|
||||
{
|
||||
$this->_yy_state = $state;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function yylex1()
|
||||
{
|
||||
if ($this->count >= strlen($this->data)) {
|
||||
return false; // end of input
|
||||
}
|
||||
do {
|
||||
$rules = array(
|
||||
'/^[ \t\n\r]+/',
|
||||
'/^SELECT/',
|
||||
'/^FROM/',
|
||||
'/^AS/',
|
||||
'/^WHERE/',
|
||||
'/^JOIN/',
|
||||
'/^ON/',
|
||||
'/^\//',
|
||||
'/^\\*/',
|
||||
'/^\\+/',
|
||||
'/^-/',
|
||||
'/^AND/',
|
||||
'/^OR/',
|
||||
'/^,/',
|
||||
'/^\\(/',
|
||||
'/^\\)/',
|
||||
'/^=/',
|
||||
'/^!=/',
|
||||
'/^>/',
|
||||
'/^</',
|
||||
'/^>=/',
|
||||
'/^<=/',
|
||||
'/^LIKE/',
|
||||
'/^NOT LIKE/',
|
||||
'/^IN/',
|
||||
'/^NOT IN/',
|
||||
'/^INTERVAL/',
|
||||
'/^IF/',
|
||||
'/^ELT/',
|
||||
'/^COALESCE/',
|
||||
'/^CONCAT/',
|
||||
'/^SUBSTR/',
|
||||
'/^TRIM/',
|
||||
'/^DATE/',
|
||||
'/^DATE_FORMAT/',
|
||||
'/^CURRENT_DATE/',
|
||||
'/^NOW/',
|
||||
'/^TIME/',
|
||||
'/^TO_DAYS/',
|
||||
'/^FROM_DAYS/',
|
||||
'/^YEAR/',
|
||||
'/^MONTH/',
|
||||
'/^DAY/',
|
||||
'/^HOUR/',
|
||||
'/^MINUTE/',
|
||||
'/^SECOND/',
|
||||
'/^DATE_ADD/',
|
||||
'/^DATE_SUB/',
|
||||
'/^ROUND/',
|
||||
'/^FLOOR/',
|
||||
'/^INET_ATON/',
|
||||
'/^INET_NTOA/',
|
||||
'/^[0-9]+|0x[0-9a-fA-F]+/',
|
||||
'/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/',
|
||||
'/^([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/',
|
||||
'/^:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/',
|
||||
'/^\\./',
|
||||
);
|
||||
$match = false;
|
||||
foreach ($rules as $index => $rule) {
|
||||
if (preg_match($rule, substr($this->data, $this->count), $yymatches)) {
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
$match = array($yymatches, $index); // matches, token
|
||||
}
|
||||
} else {
|
||||
$match = array($yymatches, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$match) {
|
||||
throw new Exception('Unexpected input at line' . $this->line .
|
||||
': ' . $this->data[$this->count]);
|
||||
}
|
||||
$this->token = $match[1];
|
||||
$this->value = $match[0][0];
|
||||
$yysubmatches = $match[0];
|
||||
array_shift($yysubmatches);
|
||||
if (!$yysubmatches) {
|
||||
$yysubmatches = array();
|
||||
}
|
||||
$r = $this->{'yy_r1_' . $this->token}($yysubmatches);
|
||||
if ($r === null) {
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
// accept this token
|
||||
return true;
|
||||
} elseif ($r === true) {
|
||||
// we have changed state
|
||||
// process this token in the new state
|
||||
return $this->yylex();
|
||||
} elseif ($r === false) {
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
if ($this->count >= strlen($this->data)) {
|
||||
return false; // end of input
|
||||
}
|
||||
// skip this token
|
||||
continue;
|
||||
} else {
|
||||
$yy_yymore_patterns = array_slice($rules, $this->token, true);
|
||||
// yymore is needed
|
||||
do {
|
||||
if (!isset($yy_yymore_patterns[$this->token])) {
|
||||
throw new Exception('cannot do yymore for the last token');
|
||||
}
|
||||
$match = false;
|
||||
foreach ($yy_yymore_patterns[$this->token] as $index => $rule) {
|
||||
if (preg_match('/' . $rule . '/',
|
||||
substr($this->data, $this->count), $yymatches)) {
|
||||
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
|
||||
if ($match) {
|
||||
if (strlen($yymatches[0]) > strlen($match[0][0])) {
|
||||
$match = array($yymatches, $index); // matches, token
|
||||
}
|
||||
} else {
|
||||
$match = array($yymatches, $index);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$match) {
|
||||
throw new Exception('Unexpected input at line' . $this->line .
|
||||
': ' . $this->data[$this->count]);
|
||||
}
|
||||
$this->token = $match[1];
|
||||
$this->value = $match[0][0];
|
||||
$yysubmatches = $match[0];
|
||||
array_shift($yysubmatches);
|
||||
if (!$yysubmatches) {
|
||||
$yysubmatches = array();
|
||||
}
|
||||
$this->line = substr_count($this->value, "\n");
|
||||
$r = $this->{'yy_r1_' . $this->token}();
|
||||
} while ($r !== null || !$r);
|
||||
if ($r === true) {
|
||||
// we have changed state
|
||||
// process this token in the new state
|
||||
return $this->yylex();
|
||||
} else {
|
||||
// accept
|
||||
$this->count += strlen($this->value);
|
||||
$this->line += substr_count($this->value, "\n");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while (true);
|
||||
|
||||
} // end function
|
||||
|
||||
function yy_r1_0($yy_subpatterns)
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
function yy_r1_1($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::SELECT;
|
||||
}
|
||||
function yy_r1_2($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::FROM;
|
||||
}
|
||||
function yy_r1_3($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::AS_ALIAS;
|
||||
}
|
||||
function yy_r1_4($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::WHERE;
|
||||
}
|
||||
function yy_r1_5($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::JOIN;
|
||||
}
|
||||
function yy_r1_6($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::ON;
|
||||
}
|
||||
function yy_r1_7($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
}
|
||||
function yy_r1_8($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
}
|
||||
function yy_r1_9($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
}
|
||||
function yy_r1_10($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
}
|
||||
function yy_r1_11($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
}
|
||||
function yy_r1_12($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
}
|
||||
function yy_r1_13($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
function yy_r1_14($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
}
|
||||
function yy_r1_15($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
}
|
||||
function yy_r1_16($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::EQ;
|
||||
}
|
||||
function yy_r1_17($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
}
|
||||
function yy_r1_18($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GT;
|
||||
}
|
||||
function yy_r1_19($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LT;
|
||||
}
|
||||
function yy_r1_20($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::GE;
|
||||
}
|
||||
function yy_r1_21($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LE;
|
||||
}
|
||||
function yy_r1_22($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::LIKE;
|
||||
}
|
||||
function yy_r1_23($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
}
|
||||
function yy_r1_24($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::IN;
|
||||
}
|
||||
function yy_r1_25($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
function yy_r1_26($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
function yy_r1_27($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
function yy_r1_28($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
function yy_r1_29($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
function yy_r1_30($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
function yy_r1_31($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
function yy_r1_32($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
function yy_r1_33($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
function yy_r1_34($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
function yy_r1_35($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
function yy_r1_36($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
function yy_r1_37($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
function yy_r1_38($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
function yy_r1_39($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
function yy_r1_40($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
function yy_r1_41($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
function yy_r1_42($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
function yy_r1_43($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
}
|
||||
function yy_r1_44($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
}
|
||||
function yy_r1_45($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
}
|
||||
function yy_r1_46($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
function yy_r1_47($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
function yy_r1_48($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
function yy_r1_49($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
function yy_r1_50($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
}
|
||||
function yy_r1_51($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
function yy_r1_52($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
function yy_r1_53($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
function yy_r1_54($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
function yy_r1_55($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::VARNAME;
|
||||
}
|
||||
function yy_r1_56($yy_subpatterns)
|
||||
{
|
||||
|
||||
$this->token = OQLParser::DOT;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
define('UNEXPECTED_INPUT_AT_LINE', 'Unexpected input at line');
|
||||
|
||||
class OQLLexerException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sUnexpected)
|
||||
{
|
||||
parent::__construct("Syntax error", $sInput, $iLine, $iCol, $sUnexpected);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLLexer extends OQLLexerRaw
|
||||
{
|
||||
public function getTokenPos()
|
||||
{
|
||||
return max(0, $this->count - strlen($this->value));
|
||||
}
|
||||
|
||||
function yylex()
|
||||
{
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
357
core/oql/oql-lexer.plex
Normal file
@@ -0,0 +1,357 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* OQL syntax analyzer, to be used prior to run the lexical analyzer
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
// Notes (from the source file: oql-lexer.plex) - Romain
|
||||
//
|
||||
// The strval rule is a little bit cryptic.
|
||||
// This is due to both a bug in the lexer generator and the complexity of our need
|
||||
// The rule means: either a quoted string with ", or a quoted string with '
|
||||
// literal " (resp. ') must be escaped by a \
|
||||
// \ must be escaped by an additional \
|
||||
//
|
||||
// Here are the issues and limitation found in the lexer generator:
|
||||
// * Matching simple quotes is an issue, because regexp are not correctly escaped (and the ESC code is escaped itself)
|
||||
// Workaround: insert '.chr(39).' which will be a real ' in the end
|
||||
// * Matching an alternate regexp is an issue because you must specify "|^...."
|
||||
// and the regexp parser will not accept that syntax
|
||||
// Workaround: insert '.chr(94).' which will be a real ^
|
||||
//
|
||||
// Let's analyze an overview of the regexp, we have
|
||||
// 1) The strval rule in the lexer definition
|
||||
// /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
// 2) Becomes the php expression in the lexer
|
||||
// (note the escaped double quotes, hopefully having no effect, but showing where the issue is!)
|
||||
// $myRegexp = '/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/';
|
||||
//
|
||||
// To be fixed in LexerGenerator/Parser.y, in doLongestMatch (doFirstMatch is ok)
|
||||
//
|
||||
//
|
||||
// Now, let's explain how the regexp has been designed.
|
||||
// Here is a simplified version, dealing with simple quotes, and based on the assumption that the lexer generator has been fixed!
|
||||
// The strval rule in the lexer definition
|
||||
// /'([^\\']*(\\')*(\\\\)*)*'/
|
||||
// This means anything containing \\ or \' or any other char but a standalone ' or \
|
||||
// This means ' or \ could not be found without a preceding \
|
||||
//
|
||||
class OQLLexerRaw
|
||||
{
|
||||
protected $data; // input string
|
||||
public $token; // token id
|
||||
public $value; // token string representation
|
||||
protected $line; // current line
|
||||
protected $count; // current column
|
||||
|
||||
function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->count = 0;
|
||||
$this->line = 1;
|
||||
}
|
||||
|
||||
/*!lex2php
|
||||
%input $this->data
|
||||
%counter $this->count
|
||||
%token $this->token
|
||||
%value $this->value
|
||||
%line $this->line
|
||||
%matchlongest 1
|
||||
whitespace = /[ \t\n\r]+/
|
||||
select = "SELECT"
|
||||
from = "FROM"
|
||||
as_alias = "AS"
|
||||
where = "WHERE"
|
||||
join = "JOIN"
|
||||
on = "ON"
|
||||
coma = ","
|
||||
par_open = "("
|
||||
par_close = ")"
|
||||
math_div = "/"
|
||||
math_mult = "*"
|
||||
math_plus = "+"
|
||||
math_minus = "-"
|
||||
log_and = "AND"
|
||||
log_or = "OR"
|
||||
eq = "="
|
||||
not_eq = "!="
|
||||
gt = ">"
|
||||
lt = "<"
|
||||
ge = ">="
|
||||
le = "<="
|
||||
like = "LIKE"
|
||||
not_like = "NOT LIKE"
|
||||
in = "IN"
|
||||
not_in = "NOT IN"
|
||||
interval = "INTERVAL"
|
||||
f_if = "IF"
|
||||
f_elt = "ELT"
|
||||
f_coalesce = "COALESCE"
|
||||
f_concat = "CONCAT"
|
||||
f_substr = "SUBSTR"
|
||||
f_trim = "TRIM"
|
||||
f_date = "DATE"
|
||||
f_date_format = "DATE_FORMAT"
|
||||
f_current_date = "CURRENT_DATE"
|
||||
f_now = "NOW"
|
||||
f_time = "TIME"
|
||||
f_to_days = "TO_DAYS"
|
||||
f_from_days = "FROM_DAYS"
|
||||
f_year = "YEAR"
|
||||
f_month = "MONTH"
|
||||
f_day = "DAY"
|
||||
f_hour = "HOUR"
|
||||
f_minute = "MINUTE"
|
||||
f_second = "SECOND"
|
||||
f_date_add = "DATE_ADD"
|
||||
f_date_sub = "DATE_SUB"
|
||||
f_round = "ROUND"
|
||||
f_floor = "FLOOR"
|
||||
f_inet_aton = "INET_ATON"
|
||||
f_inet_ntoa = "INET_NTOA"
|
||||
numval = /[0-9]+|0x[0-9a-fA-F]+/
|
||||
strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/
|
||||
name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/
|
||||
varname = /:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/
|
||||
dot = "."
|
||||
*/
|
||||
|
||||
/*!lex2php
|
||||
whitespace {
|
||||
return false;
|
||||
}
|
||||
select {
|
||||
$this->token = OQLParser::SELECT;
|
||||
}
|
||||
from {
|
||||
$this->token = OQLParser::FROM;
|
||||
}
|
||||
as_alias {
|
||||
$this->token = OQLParser::AS_ALIAS;
|
||||
}
|
||||
where {
|
||||
$this->token = OQLParser::WHERE;
|
||||
}
|
||||
join {
|
||||
$this->token = OQLParser::JOIN;
|
||||
}
|
||||
on {
|
||||
$this->token = OQLParser::ON;
|
||||
}
|
||||
math_div {
|
||||
$this->token = OQLParser::MATH_DIV;
|
||||
}
|
||||
math_mult {
|
||||
$this->token = OQLParser::MATH_MULT;
|
||||
}
|
||||
math_plus {
|
||||
$this->token = OQLParser::MATH_PLUS;
|
||||
}
|
||||
math_minus {
|
||||
$this->token = OQLParser::MATH_MINUS;
|
||||
}
|
||||
log_and {
|
||||
$this->token = OQLParser::LOG_AND;
|
||||
}
|
||||
log_or {
|
||||
$this->token = OQLParser::LOG_OR;
|
||||
}
|
||||
coma {
|
||||
$this->token = OQLParser::COMA;
|
||||
}
|
||||
par_open {
|
||||
$this->token = OQLParser::PAR_OPEN;
|
||||
}
|
||||
par_close {
|
||||
$this->token = OQLParser::PAR_CLOSE;
|
||||
}
|
||||
eq {
|
||||
$this->token = OQLParser::EQ;
|
||||
}
|
||||
not_eq {
|
||||
$this->token = OQLParser::NOT_EQ;
|
||||
}
|
||||
gt {
|
||||
$this->token = OQLParser::GT;
|
||||
}
|
||||
lt {
|
||||
$this->token = OQLParser::LT;
|
||||
}
|
||||
ge {
|
||||
$this->token = OQLParser::GE;
|
||||
}
|
||||
le {
|
||||
$this->token = OQLParser::LE;
|
||||
}
|
||||
like {
|
||||
$this->token = OQLParser::LIKE;
|
||||
}
|
||||
not_like {
|
||||
$this->token = OQLParser::NOT_LIKE;
|
||||
}
|
||||
in {
|
||||
$this->token = OQLParser::IN;
|
||||
}
|
||||
not_in {
|
||||
$this->token = OQLParser::NOT_IN;
|
||||
}
|
||||
interval {
|
||||
$this->token = OQLParser::INTERVAL;
|
||||
}
|
||||
f_if {
|
||||
$this->token = OQLParser::F_IF;
|
||||
}
|
||||
f_elt {
|
||||
$this->token = OQLParser::F_ELT;
|
||||
}
|
||||
f_coalesce {
|
||||
$this->token = OQLParser::F_COALESCE;
|
||||
}
|
||||
f_concat {
|
||||
$this->token = OQLParser::F_CONCAT;
|
||||
}
|
||||
f_substr {
|
||||
$this->token = OQLParser::F_SUBSTR;
|
||||
}
|
||||
f_trim {
|
||||
$this->token = OQLParser::F_TRIM;
|
||||
}
|
||||
f_date {
|
||||
$this->token = OQLParser::F_DATE;
|
||||
}
|
||||
f_date_format {
|
||||
$this->token = OQLParser::F_DATE_FORMAT;
|
||||
}
|
||||
f_current_date {
|
||||
$this->token = OQLParser::F_CURRENT_DATE;
|
||||
}
|
||||
f_now {
|
||||
$this->token = OQLParser::F_NOW;
|
||||
}
|
||||
f_time {
|
||||
$this->token = OQLParser::F_TIME;
|
||||
}
|
||||
f_to_days {
|
||||
$this->token = OQLParser::F_TO_DAYS;
|
||||
}
|
||||
f_from_days {
|
||||
$this->token = OQLParser::F_FROM_DAYS;
|
||||
}
|
||||
f_year {
|
||||
$this->token = OQLParser::F_YEAR;
|
||||
}
|
||||
f_month {
|
||||
$this->token = OQLParser::F_MONTH;
|
||||
}
|
||||
f_day {
|
||||
$this->token = OQLParser::F_DAY;
|
||||
}
|
||||
f_hour {
|
||||
$this->token = OQLParser::F_HOUR;
|
||||
}
|
||||
f_minute {
|
||||
$this->token = OQLParser::F_MINUTE;
|
||||
}
|
||||
f_second {
|
||||
$this->token = OQLParser::F_SECOND;
|
||||
}
|
||||
f_date_add {
|
||||
$this->token = OQLParser::F_DATE_ADD;
|
||||
}
|
||||
f_date_sub {
|
||||
$this->token = OQLParser::F_DATE_SUB;
|
||||
}
|
||||
f_round {
|
||||
$this->token = OQLParser::F_ROUND;
|
||||
}
|
||||
f_floor {
|
||||
$this->token = OQLParser::F_FLOOR;
|
||||
}
|
||||
f_inet_aton {
|
||||
$this->token = OQLParser::F_INET_ATON;
|
||||
}
|
||||
f_inet_ntoa {
|
||||
$this->token = OQLParser::F_INET_NTOA;
|
||||
}
|
||||
numval {
|
||||
$this->token = OQLParser::NUMVAL;
|
||||
}
|
||||
strval {
|
||||
$this->token = OQLParser::STRVAL;
|
||||
}
|
||||
name {
|
||||
$this->token = OQLParser::NAME;
|
||||
}
|
||||
varname {
|
||||
$this->token = OQLParser::VARNAME;
|
||||
}
|
||||
dot {
|
||||
$this->token = OQLParser::DOT;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
define('UNEXPECTED_INPUT_AT_LINE', 'Unexpected input at line');
|
||||
|
||||
class OQLLexerException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sUnexpected)
|
||||
{
|
||||
parent::__construct("Syntax error", $sInput, $iLine, $iCol, $sUnexpected);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLLexer extends OQLLexerRaw
|
||||
{
|
||||
public function getTokenPos()
|
||||
{
|
||||
return max(0, $this->count - strlen($this->value));
|
||||
}
|
||||
|
||||
function yylex()
|
||||
{
|
||||
try
|
||||
{
|
||||
return parent::yylex();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sMessage = $e->getMessage();
|
||||
if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE)
|
||||
{
|
||||
$sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE));
|
||||
if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches))
|
||||
{
|
||||
$iLine = $aMatches[1];
|
||||
$sUnexpected = $aMatches[2];
|
||||
throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected);
|
||||
}
|
||||
}
|
||||
// Default: forward the exception
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
1740
core/oql/oql-parser.php
Normal file
275
core/oql/oql-parser.y
Normal file
@@ -0,0 +1,275 @@
|
||||
|
||||
/*
|
||||
|
||||
This is a LALR(1) grammar
|
||||
(seek for Lemon grammar to get some documentation from the Net)
|
||||
That doc was helpful: http://www.hwaci.com/sw/lemon/lemon.html
|
||||
|
||||
To handle operators precedence we could have used the %left directive
|
||||
(we took another option, because that one was discovered right after...
|
||||
which option is the best for us?)
|
||||
Example:
|
||||
%left LOG_AND.
|
||||
%left LOG_OR.
|
||||
%nonassoc EQ NE GT GE LT LE.
|
||||
%left PLUS MINUS.
|
||||
%left TIMES DIVIDE MOD.
|
||||
%right EXP NOT.
|
||||
|
||||
TODO : solve the 2 remaining shift-reduce conflicts (JOIN)
|
||||
|
||||
*/
|
||||
|
||||
%name OQLParser_
|
||||
%declare_class {class OQLParserRaw}
|
||||
%syntax_error {
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
}
|
||||
|
||||
result ::= query(X). { $this->my_result = X; }
|
||||
result ::= condition(X). { $this->my_result = X; }
|
||||
|
||||
query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, X, W, J, array(X));
|
||||
}
|
||||
query(A) ::= SELECT class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, Y, W, J, array(Y));
|
||||
}
|
||||
|
||||
query(A) ::= SELECT class_list(E) FROM class_name(X) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, X, W, J, E);
|
||||
}
|
||||
query(A) ::= SELECT class_list(E) FROM class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). {
|
||||
A = new OqlObjectQuery(X, Y, W, J, E);
|
||||
}
|
||||
|
||||
|
||||
class_list(A) ::= class_name(X). {
|
||||
A = array(X);
|
||||
}
|
||||
class_list(A) ::= class_list(L) COMA class_name(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
|
||||
where_statement(A) ::= WHERE condition(C). { A = C;}
|
||||
where_statement(A) ::= . { A = null;}
|
||||
|
||||
join_statement(A) ::= join_item(J) join_statement(S). {
|
||||
// insert the join statement on top of the existing list
|
||||
array_unshift(S, J);
|
||||
// and return the updated array
|
||||
A = S;
|
||||
}
|
||||
join_statement(A) ::= join_item(J). {
|
||||
A = Array(J);
|
||||
}
|
||||
join_statement(A) ::= . { A = null;}
|
||||
|
||||
join_item(A) ::= JOIN class_name(X) AS_ALIAS class_name(Y) ON join_condition(C).
|
||||
{
|
||||
// create an array with one single item
|
||||
A = new OqlJoinSpec(X, Y, C);
|
||||
}
|
||||
join_item(A) ::= JOIN class_name(X) ON join_condition(C).
|
||||
{
|
||||
// create an array with one single item
|
||||
A = new OqlJoinSpec(X, X, C);
|
||||
}
|
||||
|
||||
join_condition(A) ::= field_id(X) EQ field_id(Y). { A = new BinaryOqlExpression(X, '=', Y); }
|
||||
|
||||
condition(A) ::= expression_prio4(X). { A = X; }
|
||||
|
||||
expression_basic(A) ::= scalar(X). { A = X; }
|
||||
expression_basic(A) ::= field_id(X). { A = X; }
|
||||
expression_basic(A) ::= var_name(X). { A = X; }
|
||||
expression_basic(A) ::= func_name(X) PAR_OPEN arg_list(Y) PAR_CLOSE. { A = new FunctionOqlExpression(X, Y); }
|
||||
expression_basic(A) ::= PAR_OPEN expression_prio4(X) PAR_CLOSE. { A = X; }
|
||||
expression_basic(A) ::= expression_basic(X) list_operator(Y) list(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio1(A) ::= expression_basic(X). { A = X; }
|
||||
expression_prio1(A) ::= expression_prio1(X) operator1(Y) expression_basic(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio2(A) ::= expression_prio1(X). { A = X; }
|
||||
expression_prio2(A) ::= expression_prio2(X) operator2(Y) expression_prio1(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio3(A) ::= expression_prio2(X). { A = X; }
|
||||
expression_prio3(A) ::= expression_prio3(X) operator3(Y) expression_prio2(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
expression_prio4(A) ::= expression_prio3(X). { A = X; }
|
||||
expression_prio4(A) ::= expression_prio4(X) operator4(Y) expression_prio3(Z). { A = new BinaryOqlExpression(X, Y, Z); }
|
||||
|
||||
|
||||
list(A) ::= PAR_OPEN scalar_list(X) PAR_CLOSE. {
|
||||
A = new ListOqlExpression(X);
|
||||
}
|
||||
scalar_list(A) ::= scalar(X). {
|
||||
A = array(X);
|
||||
}
|
||||
scalar_list(A) ::= scalar_list(L) COMA scalar(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
|
||||
arg_list(A) ::= . {
|
||||
A = array();
|
||||
}
|
||||
arg_list(A) ::= argument(X). {
|
||||
A = array(X);
|
||||
}
|
||||
arg_list(A) ::= arg_list(L) COMA argument(X). {
|
||||
array_push(L, X);
|
||||
A = L;
|
||||
}
|
||||
argument(A) ::= expression_prio4(X). { A = X; }
|
||||
argument(A) ::= INTERVAL expression_prio4(X) interval_unit(Y). { A = new IntervalOqlExpression(X, Y); }
|
||||
|
||||
interval_unit(A) ::= F_SECOND(X). { A = X; }
|
||||
interval_unit(A) ::= F_MINUTE(X). { A = X; }
|
||||
interval_unit(A) ::= F_HOUR(X). { A = X; }
|
||||
interval_unit(A) ::= F_DAY(X). { A = X; }
|
||||
interval_unit(A) ::= F_MONTH(X). { A = X; }
|
||||
interval_unit(A) ::= F_YEAR(X). { A = X; }
|
||||
|
||||
scalar(A) ::= num_scalar(X). { A = X; }
|
||||
scalar(A) ::= str_scalar(X). { A = X; }
|
||||
|
||||
num_scalar(A) ::= num_value(X). { A = new ScalarOqlExpression(X); }
|
||||
str_scalar(A) ::= str_value(X). { A = new ScalarOqlExpression(X); }
|
||||
|
||||
field_id(A) ::= name(X). { A = new FieldOqlExpression(X); }
|
||||
field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); }
|
||||
class_name(A) ::= name(X). { A=X; }
|
||||
|
||||
|
||||
var_name(A) ::= VARNAME(X). { A = new VariableOqlExpression(substr(X, 1)); }
|
||||
|
||||
name(A) ::= NAME(X). {
|
||||
if (X[0] == '`')
|
||||
{
|
||||
$name = substr(X, 1, strlen(X) - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
$name = X;
|
||||
}
|
||||
A = new OqlName($name, $this->m_iColPrev);
|
||||
}
|
||||
|
||||
num_value(A) ::= NUMVAL(X). {A=X;}
|
||||
str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));}
|
||||
|
||||
|
||||
operator1(A) ::= num_operator1(X). {A=X;}
|
||||
operator2(A) ::= num_operator2(X). {A=X;}
|
||||
operator2(A) ::= str_operator(X). {A=X;}
|
||||
operator2(A) ::= EQ(X). {A=X;}
|
||||
operator2(A) ::= NOT_EQ(X). {A=X;}
|
||||
operator3(A) ::= LOG_AND(X). {A=X;}
|
||||
operator4(A) ::= LOG_OR(X). {A=X;}
|
||||
|
||||
num_operator1(A) ::= MATH_DIV(X). {A=X;}
|
||||
num_operator1(A) ::= MATH_MULT(X). {A=X;}
|
||||
num_operator2(A) ::= MATH_PLUS(X). {A=X;}
|
||||
num_operator2(A) ::= MATH_MINUS(X). {A=X;}
|
||||
num_operator2(A) ::= GT(X). {A=X;}
|
||||
num_operator2(A) ::= LT(X). {A=X;}
|
||||
num_operator2(A) ::= GE(X). {A=X;}
|
||||
num_operator2(A) ::= LE(X). {A=X;}
|
||||
|
||||
str_operator(A) ::= LIKE(X). {A=X;}
|
||||
str_operator(A) ::= NOT_LIKE(X). {A=X;}
|
||||
|
||||
list_operator(A) ::= IN(X). {A=X;}
|
||||
list_operator(A) ::= NOT_IN(X). {A=X;}
|
||||
|
||||
func_name(A) ::= F_IF(X). { A=X; }
|
||||
func_name(A) ::= F_ELT(X). { A=X; }
|
||||
func_name(A) ::= F_COALESCE(X). { A=X; }
|
||||
func_name(A) ::= F_CONCAT(X). { A=X; }
|
||||
func_name(A) ::= F_SUBSTR(X). { A=X; }
|
||||
func_name(A) ::= F_TRIM(X). { A=X; }
|
||||
func_name(A) ::= F_DATE(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_FORMAT(X). { A=X; }
|
||||
func_name(A) ::= F_CURRENT_DATE(X). { A=X; }
|
||||
func_name(A) ::= F_NOW(X). { A=X; }
|
||||
func_name(A) ::= F_TIME(X). { A=X; }
|
||||
func_name(A) ::= F_TO_DAYS(X). { A=X; }
|
||||
func_name(A) ::= F_FROM_DAYS(X). { A=X; }
|
||||
func_name(A) ::= F_YEAR(X). { A=X; }
|
||||
func_name(A) ::= F_MONTH(X). { A=X; }
|
||||
func_name(A) ::= F_DAY(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_ADD(X). { A=X; }
|
||||
func_name(A) ::= F_DATE_SUB(X). { A=X; }
|
||||
func_name(A) ::= F_ROUND(X). { A=X; }
|
||||
func_name(A) ::= F_FLOOR(X). { A=X; }
|
||||
func_name(A) ::= F_INET_ATON(X). { A=X; }
|
||||
func_name(A) ::= F_INET_NTOA(X). { A=X; }
|
||||
|
||||
|
||||
%code {
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
protected $my_result;
|
||||
|
||||
public function GetResult()
|
||||
{
|
||||
return $this->my_result;
|
||||
}
|
||||
|
||||
// More info on the source query and the current position while parsing it
|
||||
// Data used when an exception is raised
|
||||
protected $m_iLine; // still not used
|
||||
protected $m_iCol;
|
||||
protected $m_iColPrev; // this is the interesting one, because the parser will reduce on the next token
|
||||
protected $m_sSourceQuery;
|
||||
|
||||
public function __construct($sQuery)
|
||||
{
|
||||
$this->m_iLine = 0;
|
||||
$this->m_iCol = 0;
|
||||
$this->m_iColPrev = 0;
|
||||
$this->m_sSourceQuery = $sQuery;
|
||||
// no constructor - parent::__construct();
|
||||
}
|
||||
|
||||
public function doParse($token, $value, $iCurrPosition = 0)
|
||||
{
|
||||
$this->m_iColPrev = $this->m_iCol;
|
||||
$this->m_iCol = $iCurrPosition;
|
||||
|
||||
return parent::DoParse($token, $value);
|
||||
}
|
||||
|
||||
public function doFinish()
|
||||
{
|
||||
$this->doParse(0, 0);
|
||||
return $this->my_result;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// Bug in the original destructor, causing an infinite loop !
|
||||
// This is a real issue when a fatal error occurs on the first token (the error could not be seen)
|
||||
if (is_null($this->yyidx))
|
||||
{
|
||||
$this->yyidx = -1;
|
||||
}
|
||||
parent::__destruct();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
102
core/oql/oqlexception.class.inc.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Special handling for OQL syntax errors
|
||||
*
|
||||
* @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 OQLException extends CoreException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, $iLine, $iCol, $sUnexpected, $aExpecting = null)
|
||||
{
|
||||
$this->m_MyIssue = $sIssue;
|
||||
$this->m_sInput = $sInput;
|
||||
$this->m_iLine = $iLine;
|
||||
$this->m_iCol = $iCol;
|
||||
$this->m_sUnexpected = $sUnexpected;
|
||||
$this->m_aExpecting = $aExpecting;
|
||||
|
||||
if (is_null($this->m_aExpecting) || (count($this->m_aExpecting) == 0))
|
||||
{
|
||||
$sMessage = "$sIssue - found '{$this->m_sUnexpected}' at $iCol in '$sInput'";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sExpectations = '{'.implode(', ', $this->m_aExpecting).'}';
|
||||
$sSuggest = self::FindClosestString($this->m_sUnexpected, $this->m_aExpecting);
|
||||
$sMessage = "$sIssue - found '{$this->m_sUnexpected}' at $iCol in '$sInput', expecting $sExpectations, I would suggest to use '$sSuggest'";
|
||||
}
|
||||
|
||||
// make sure everything is assigned properly
|
||||
parent::__construct($sMessage, 0);
|
||||
}
|
||||
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
|
||||
{
|
||||
$sRet = htmlentities($this->m_MyIssue.", found '".$this->m_sUnexpected."' in: ");
|
||||
$sRet .= htmlentities(substr($this->m_sInput, 0, $this->m_iCol));
|
||||
$sRet .= $sHighlightHtmlBegin.htmlentities(substr($this->m_sInput, $this->m_iCol, strlen($this->m_sUnexpected))).$sHighlightHtmlEnd;
|
||||
$sRet .= htmlentities(substr($this->m_sInput, $this->m_iCol + strlen($this->m_sUnexpected)));
|
||||
|
||||
if (!is_null($this->m_aExpecting) && (count($this->m_aExpecting) > 0))
|
||||
{
|
||||
$sExpectations = '{'.implode(', ', $this->m_aExpecting).'}';
|
||||
$sRet .= ", expecting ".htmlentities($sExpectations);
|
||||
$sSuggest = self::FindClosestString($this->m_sUnexpected, $this->m_aExpecting);
|
||||
if (strlen($sSuggest) > 0)
|
||||
{
|
||||
$sRet .= ", I would suggest to use '$sHighlightHtmlBegin".htmlentities($sSuggest)."$sHighlightHtmlEnd'";
|
||||
}
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
static protected function FindClosestString($sInput, $aDictionary)
|
||||
{
|
||||
// no shortest distance found, yet
|
||||
$fShortest = -1;
|
||||
$sRet = '';
|
||||
|
||||
// loop through words to find the closest
|
||||
foreach ($aDictionary as $sSuggestion)
|
||||
{
|
||||
// calculate the distance between the input string and the suggested one
|
||||
$fDist = levenshtein($sInput, $sSuggestion);
|
||||
if ($fDist == 0)
|
||||
{
|
||||
// Exact match
|
||||
return $sSuggestion;
|
||||
}
|
||||
|
||||
if ($fShortest < 0 || ($fDist < 4 && $fDist <= $fShortest))
|
||||
{
|
||||
// set the closest match, and shortest distance
|
||||
$sRet = $sSuggestion;
|
||||
$fShortest = $fDist;
|
||||
}
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
84
core/oql/oqlinterpreter.class.inc.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Wrapper to execute the parser, lexical analyzer and normalization of an OQL query
|
||||
*
|
||||
* @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 OqlNormalizeException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, OqlName $oName, $aExpecting = null)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, 0, $oName->GetPos(), $oName->GetValue(), $aExpecting);
|
||||
}
|
||||
}
|
||||
|
||||
class OqlInterpreterException extends OQLException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
class OqlInterpreter
|
||||
{
|
||||
public $m_sQuery;
|
||||
|
||||
public function __construct($sQuery)
|
||||
{
|
||||
$this->m_sQuery = $sQuery;
|
||||
}
|
||||
|
||||
// Note: this function is left public for unit test purposes
|
||||
public function Parse()
|
||||
{
|
||||
$oLexer = new OQLLexer($this->m_sQuery);
|
||||
$oParser = new OQLParser($this->m_sQuery);
|
||||
|
||||
while($oLexer->yylex())
|
||||
{
|
||||
$oParser->doParse($oLexer->token, $oLexer->value, $oLexer->getTokenPos());
|
||||
}
|
||||
$res = $oParser->doFinish();
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function ParseObjectQuery()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof OqlObjectQuery)
|
||||
{
|
||||
throw new OQLException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes));
|
||||
}
|
||||
return $oRes;
|
||||
}
|
||||
|
||||
public function ParseExpression()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
if (!$oRes instanceof Expression)
|
||||
{
|
||||
throw new OQLException('Expecting an OQL expression', $this->m_sQuery, 0, 0, get_class($oRes), array('Expression'));
|
||||
}
|
||||
return $oRes;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
211
core/oql/oqlquery.class.inc.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Classes defined for lexical analyze (see oql-parser.y)
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
// Position a string within an OQL query
|
||||
// This is a must if we want to be able to pinpoint an error at any stage of the query interpretation
|
||||
// In particular, the normalization phase requires this
|
||||
class OqlName
|
||||
{
|
||||
protected $m_sValue;
|
||||
protected $m_iPos;
|
||||
|
||||
public function __construct($sValue, $iPos)
|
||||
{
|
||||
$this->m_iPos = $iPos;
|
||||
$this->m_sValue = $sValue;
|
||||
}
|
||||
|
||||
public function GetValue()
|
||||
{
|
||||
return $this->m_sValue;
|
||||
}
|
||||
|
||||
public function GetPos()
|
||||
{
|
||||
return $this->m_iPos;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->m_sValue;
|
||||
}
|
||||
}
|
||||
|
||||
class OqlJoinSpec
|
||||
{
|
||||
protected $m_oClass;
|
||||
protected $m_oClassAlias;
|
||||
protected $m_oLeftField;
|
||||
protected $m_oRightField;
|
||||
|
||||
protected $m_oNextJoinspec;
|
||||
|
||||
public function __construct($oClass, $oClassAlias, BinaryExpression $oExpression)
|
||||
{
|
||||
$this->m_oClass = $oClass;
|
||||
$this->m_oClassAlias = $oClassAlias;
|
||||
$this->m_oLeftField = $oExpression->GetLeftExpr();
|
||||
$this->m_oRightField = $oExpression->GetRightExpr();
|
||||
}
|
||||
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_oClass->GetValue();
|
||||
}
|
||||
public function GetClassAlias()
|
||||
{
|
||||
return $this->m_oClassAlias->GetValue();
|
||||
}
|
||||
|
||||
public function GetClassDetails()
|
||||
{
|
||||
return $this->m_oClass;
|
||||
}
|
||||
public function GetClassAliasDetails()
|
||||
{
|
||||
return $this->m_oClassAlias;
|
||||
}
|
||||
|
||||
public function GetLeftField()
|
||||
{
|
||||
return $this->m_oLeftField;
|
||||
}
|
||||
public function GetRightField()
|
||||
{
|
||||
return $this->m_oRightField;
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryOqlExpression extends BinaryExpression
|
||||
{
|
||||
}
|
||||
|
||||
class ScalarOqlExpression extends ScalarExpression
|
||||
{
|
||||
}
|
||||
|
||||
class FieldOqlExpression extends FieldExpression
|
||||
{
|
||||
protected $m_oParent;
|
||||
protected $m_oName;
|
||||
|
||||
public function __construct($oName, $oParent = null)
|
||||
{
|
||||
if (is_null($oParent))
|
||||
{
|
||||
$oParent = new OqlName('', 0);
|
||||
}
|
||||
$this->m_oParent = $oParent;
|
||||
$this->m_oName = $oName;
|
||||
|
||||
parent::__construct($oName->GetValue(), $oParent->GetValue());
|
||||
}
|
||||
|
||||
public function GetParentDetails()
|
||||
{
|
||||
return $this->m_oParent;
|
||||
}
|
||||
|
||||
public function GetNameDetails()
|
||||
{
|
||||
return $this->m_oName;
|
||||
}
|
||||
}
|
||||
|
||||
class VariableOqlExpression extends VariableExpression
|
||||
{
|
||||
}
|
||||
|
||||
class ListOqlExpression extends ListExpression
|
||||
{
|
||||
}
|
||||
|
||||
class FunctionOqlExpression extends FunctionExpression
|
||||
{
|
||||
}
|
||||
|
||||
class IntervalOqlExpression extends IntervalExpression
|
||||
{
|
||||
}
|
||||
|
||||
abstract class OqlQuery
|
||||
{
|
||||
protected $m_aJoins; // array of OqlJoinSpec
|
||||
protected $m_oCondition; // condition tree (expressions)
|
||||
|
||||
public function __construct($oCondition = null, $aJoins = null)
|
||||
{
|
||||
$this->m_aJoins = $aJoins;
|
||||
$this->m_oCondition = $oCondition;
|
||||
}
|
||||
|
||||
public function GetJoins()
|
||||
{
|
||||
return $this->m_aJoins;
|
||||
}
|
||||
public function GetCondition()
|
||||
{
|
||||
return $this->m_oCondition;
|
||||
}
|
||||
}
|
||||
|
||||
class OqlObjectQuery extends OqlQuery
|
||||
{
|
||||
protected $m_aSelect; // array of selected classes
|
||||
protected $m_oClass;
|
||||
protected $m_oClassAlias;
|
||||
|
||||
public function __construct($oClass, $oClassAlias, $oCondition = null, $aJoins = null, $aSelect = null)
|
||||
{
|
||||
$this->m_aSelect = $aSelect;
|
||||
$this->m_oClass = $oClass;
|
||||
$this->m_oClassAlias = $oClassAlias;
|
||||
parent::__construct($oCondition, $aJoins);
|
||||
}
|
||||
|
||||
public function GetSelectedClasses()
|
||||
{
|
||||
return $this->m_aSelect;
|
||||
}
|
||||
public function GetClass()
|
||||
{
|
||||
return $this->m_oClass->GetValue();
|
||||
}
|
||||
public function GetClassAlias()
|
||||
{
|
||||
return $this->m_oClassAlias->GetValue();
|
||||
}
|
||||
|
||||
public function GetClassDetails()
|
||||
{
|
||||
return $this->m_oClass;
|
||||
}
|
||||
public function GetClassAliasDetails()
|
||||
{
|
||||
return $this->m_oClassAlias;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
120
core/ormdocument.class.inc.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* ormDocument
|
||||
* encapsulate the behavior of a binary data set that will be stored an attribute of class AttributeBlob
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* ormDocument
|
||||
* encapsulate the behavior of a binary data set that will be stored an attribute of class AttributeBlob
|
||||
*
|
||||
* @package itopORM
|
||||
*/
|
||||
|
||||
class ormDocument
|
||||
{
|
||||
protected $m_data;
|
||||
protected $m_sMimeType;
|
||||
protected $m_sFileName;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct($data = null, $sMimeType = 'text/plain', $sFileName = '')
|
||||
{
|
||||
$this->m_data = $data;
|
||||
$this->m_sMimeType = $sMimeType;
|
||||
$this->m_sFileName = $sFileName;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return MyHelpers::beautifulstr($this->m_data, 100, true);
|
||||
}
|
||||
|
||||
public function IsEmpty()
|
||||
{
|
||||
return ($this->m_data == null);
|
||||
}
|
||||
|
||||
public function GetMimeType()
|
||||
{
|
||||
return $this->m_sMimeType;
|
||||
}
|
||||
public function GetMainMimeType()
|
||||
{
|
||||
$iSeparatorPos = strpos($this->m_sMimeType, '/');
|
||||
if ($iSeparatorPos > 0)
|
||||
{
|
||||
return substr($this->m_sMimeType, 0, $iSeparatorPos);
|
||||
}
|
||||
return $this->m_sMimeType;
|
||||
}
|
||||
|
||||
public function GetData()
|
||||
{
|
||||
return $this->m_data;
|
||||
}
|
||||
|
||||
public function GetFileName()
|
||||
{
|
||||
return $this->m_sFileName;
|
||||
}
|
||||
|
||||
public function GetAsHTML()
|
||||
{
|
||||
$sResult = '';
|
||||
if ($this->IsEmpty())
|
||||
{
|
||||
// If the filename is not empty, display it, this is used
|
||||
// by the creation wizard while the file has not yet been uploaded
|
||||
$sResult = $this->GetFileName();
|
||||
}
|
||||
else
|
||||
{
|
||||
$data = $this->GetData();
|
||||
$sResult = $this->GetFileName().' [ '.$this->GetMimeType().', size: '.strlen($data).' byte(s) ]<br/>';
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an hyperlink to display the document *inline*
|
||||
* @return string
|
||||
*/
|
||||
public function GetDisplayLink($sClass, $Id, $sAttCode)
|
||||
{
|
||||
return "<a href=\"../pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode\" target=\"_blank\" >".$this->GetFileName()."</a>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an hyperlink to download the document (content-disposition: attachment)
|
||||
* @return string
|
||||
*/
|
||||
public function GetDownloadLink($sClass, $Id, $sAttCode)
|
||||
{
|
||||
return "<a href=\"../pages/ajax.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".$this->GetFileName()."</a>\n";
|
||||
}
|
||||
}
|
||||
?>
|
||||
119
core/ormpassword.class.inc.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?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
|
||||
|
||||
require_once(APPROOT.'/core/simplecrypt.class.inc.php');
|
||||
|
||||
/**
|
||||
* ormPassword
|
||||
* encapsulate the behavior of a one way encrypted password stored hashed
|
||||
* with a per password (as random as possible) salt, in order to prevent a "Rainbow table" hack.
|
||||
* If a cryptographic random number generator is available (on Linux or Windows)
|
||||
* it will be used for generating the salt.
|
||||
*
|
||||
* @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
|
||||
* @package itopORM
|
||||
*/
|
||||
|
||||
class ormPassword
|
||||
{
|
||||
protected $m_sHashed;
|
||||
protected $m_sSalt;
|
||||
|
||||
/**
|
||||
* Constructor, initializes the password from the encrypted values
|
||||
*/
|
||||
public function __construct($sHash = '', $sSalt = '')
|
||||
{
|
||||
$this->m_sHashed = $sHash;
|
||||
$this->m_sSalt = $sSalt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the clear text password, with a unique salt
|
||||
*/
|
||||
public function SetPassword($sClearTextPassword)
|
||||
{
|
||||
$this->m_sSalt = SimpleCrypt::GetNewSalt();
|
||||
$this->m_sHashed = $this->ComputeHash($sClearTextPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print the password: displays some stars
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return '*****'; // Password can not be read
|
||||
}
|
||||
|
||||
public function IsEmpty()
|
||||
{
|
||||
return ($this->m_hashed == null);
|
||||
}
|
||||
|
||||
public function GetHash()
|
||||
{
|
||||
return $this->m_sHashed;
|
||||
}
|
||||
|
||||
public function GetSalt()
|
||||
{
|
||||
return $this->m_sSalt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the password: displays some stars
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsHTML()
|
||||
{
|
||||
return '*****'; // Password can not be read
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the supplied clear text password matches the encrypted one
|
||||
* @param string $sClearTextPassword
|
||||
* @return boolean True if it matches, false otherwise
|
||||
*/
|
||||
public function CheckPassword($sClearTextPassword)
|
||||
{
|
||||
$bResult = false;
|
||||
$sHashedPwd = $this->ComputeHash($sClearTextPassword);
|
||||
if ($this->m_sHashed == $sHashedPwd)
|
||||
{
|
||||
$bResult = true;
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the hashed version of a password using a unique salt
|
||||
* for this password. A unique salt is generated if needed
|
||||
* @return string
|
||||
*/
|
||||
protected function ComputeHash($sClearTextPwd)
|
||||
{
|
||||
if ($this->m_sSalt == null)
|
||||
{
|
||||
$this->m_sSalt = SimpleCrypt::GetNewSalt();
|
||||
}
|
||||
return hash('sha256', $this->m_sSalt.$sClearTextPwd);
|
||||
}
|
||||
}
|
||||
?>
|
||||
235
core/simplecrypt.class.inc.php
Normal file
@@ -0,0 +1,235 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* SimpleCrypt Class - crypto helpers
|
||||
* Simple encryption of strings, uses mcrypt or degrades to a pure PHP
|
||||
* implementation when mcrypt is not present.
|
||||
* Based on Miguel Ros' work found at:
|
||||
* http://rossoft.wordpress.com/2006/05/22/simple-encryption-class/
|
||||
*
|
||||
* Usage:
|
||||
* $oSimpleCrypt = new SimpleCrypt();
|
||||
* $encrypted = $oSimpleCrypt->encrypt('a_key','the_text');
|
||||
* $sClearText = $oSimpleCrypt->decrypt('a_key',$encrypted);
|
||||
*
|
||||
* The result is $plain equals to 'the_text'
|
||||
*
|
||||
* You can use a different engine if you don't have Mcrypt:
|
||||
* $oSimpleCrypt = new SimpleCrypt('Simple');
|
||||
*
|
||||
* A string encrypted with one engine can't be decrypted with
|
||||
* a different one even if the key is the same.
|
||||
*
|
||||
* @author Miguel Ros <rossoft@gmail.com>
|
||||
* @author Erwan Taloc <erwan.taloc@combodo.com>
|
||||
* @author Romain Quetiez <romain.quetiez@combodo.com>
|
||||
* @author Denis Flaven <denis.flaven@combodo.com>
|
||||
* @version 0.3
|
||||
* @license GPL
|
||||
*/
|
||||
|
||||
class SimpleCrypt
|
||||
{
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt
|
||||
*/
|
||||
function __construct($sEngineName = 'Mcrypt')
|
||||
{
|
||||
if (($sEngineName == 'Mcrypt') && (!function_exists('mcrypt_module_open')))
|
||||
{
|
||||
// Defaults to Simple encryption if the mcrypt module is not present
|
||||
$sEngineName = 'Simple';
|
||||
}
|
||||
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
|
||||
$this->oEngine = new $sEngineName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts the string with the given key
|
||||
* @param string $key
|
||||
* @param string $sString Plaintext string
|
||||
* @return string Ciphered string
|
||||
*/
|
||||
function Encrypt($key, $sString)
|
||||
{
|
||||
return $this->oEngine->Encrypt($key,$sString);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decrypts the string by the given key
|
||||
* @param string $key
|
||||
* @param string $string Ciphered string
|
||||
* @return string Plaintext string
|
||||
*/
|
||||
function Decrypt($key, $string)
|
||||
{
|
||||
return $this->oEngine->Decrypt($key,$string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random "salt" value, to be used when "hashing" a password
|
||||
* using a one-way encryption algorithm, to prevent an attack using a "rainbow table"
|
||||
* Tryes to use the best available random number generator
|
||||
* @return string The generated random "salt"
|
||||
*/
|
||||
static function GetNewSalt()
|
||||
{
|
||||
// Copied from http://www.php.net/manual/en/function.mt-rand.php#83655
|
||||
// get 128 pseudorandom bits in a string of 16 bytes
|
||||
|
||||
$sRandomBits = null;
|
||||
|
||||
// Unix/Linux platform?
|
||||
$fp = @fopen('/dev/urandom','rb');
|
||||
if ($fp !== FALSE)
|
||||
{
|
||||
//echo "Random bits pulled from /dev/urandom<br/>\n";
|
||||
$sRandomBits .= @fread($fp,16);
|
||||
@fclose($fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
// MS-Windows platform?
|
||||
if (@class_exists('COM'))
|
||||
{
|
||||
// http://msdn.microsoft.com/en-us/library/aa388176(VS.85).aspx
|
||||
try
|
||||
{
|
||||
$CAPI_Util = new COM('CAPICOM.Utilities.1');
|
||||
$sBase64RandomBits = ''.$CAPI_Util->GetRandom(16,0);
|
||||
|
||||
// if we ask for binary data PHP munges it, so we
|
||||
// request base64 return value. We squeeze out the
|
||||
// redundancy and useless ==CRLF by hashing...
|
||||
if ($sBase64RandomBits)
|
||||
{
|
||||
//echo "Random bits got from CAPICOM.Utilities.1<br/>\n";
|
||||
$sRandomBits = md5($sBase64RandomBits, TRUE);
|
||||
}
|
||||
}
|
||||
catch (Exception $ex)
|
||||
{
|
||||
// echo 'Exception: ' . $ex->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($sRandomBits == null)
|
||||
{
|
||||
// No "strong" random generator available, use PHP's built-in mechanism
|
||||
//echo "Random bits generated from mt_rand<br/>\n";
|
||||
mt_srand(crc32(microtime()));
|
||||
$sRandomBits = '';
|
||||
for($i = 0; $i < 4; $i++)
|
||||
{
|
||||
$sRandomBits .= sprintf('%04x', mt_rand(0, 65535));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return $sRandomBits;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for encryption engines
|
||||
*/
|
||||
interface CryptEngine
|
||||
{
|
||||
function Encrypt($key, $sString);
|
||||
function Decrypt($key, $encrypted_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple Engine doesn't need any PHP extension.
|
||||
* Every encryption of the same string with the same key
|
||||
* will return the same encrypted string
|
||||
*/
|
||||
class SimpleCryptSimpleEngine implements CryptEngine
|
||||
{
|
||||
public function Encrypt($key, $sString)
|
||||
{
|
||||
$result = '';
|
||||
for($i=1; $i<=strlen($sString); $i++)
|
||||
{
|
||||
$char = substr($sString, $i-1, 1);
|
||||
$keychar = substr($key, ($i % strlen($key))-1, 1);
|
||||
$char = chr(ord($char)+ord($keychar));
|
||||
$result.=$char;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function Decrypt($key, $encrypted_data)
|
||||
{
|
||||
$result = '';
|
||||
for($i=1; $i<=strlen($encrypted_data); $i++)
|
||||
{
|
||||
$char = substr($encrypted_data, $i-1, 1);
|
||||
$keychar = substr($key, ($i % strlen($key))-1, 1);
|
||||
$char = chr(ord($char)-ord($keychar));
|
||||
$result.=$char;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* McryptEngine requires Mcrypt extension
|
||||
* Every encryption of the same string with the same key
|
||||
* will return a different encrypted string.
|
||||
*/
|
||||
class SimpleCryptMcryptEngine implements CryptEngine
|
||||
{
|
||||
var $alg = MCRYPT_BLOWFISH;
|
||||
var $td = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->td = mcrypt_module_open($this->alg,'','cbc','');
|
||||
}
|
||||
|
||||
public function Encrypt($key, $sString)
|
||||
{
|
||||
$iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($this->td), MCRYPT_RAND); // MCRYPT_RAND is the only choice on Windows prior to PHP 5.3
|
||||
mcrypt_generic_init($this->td, $key, $iv);
|
||||
if (empty($sString))
|
||||
{
|
||||
$sString = str_repeat("\0", 8);
|
||||
}
|
||||
$encrypted_data = mcrypt_generic($this->td, $sString);
|
||||
mcrypt_generic_deinit($this->td);
|
||||
return $iv.$encrypted_data;
|
||||
}
|
||||
|
||||
public function Decrypt($key, $encrypted_data)
|
||||
{
|
||||
$iv = substr($encrypted_data, 0, mcrypt_enc_get_iv_size($this->td));
|
||||
$string = substr($encrypted_data, mcrypt_enc_get_iv_size($this->td));
|
||||
mcrypt_generic_init($this->td, $key, $iv);
|
||||
$decrypted_data = rtrim(mdecrypt_generic($this->td, $string), "\0");
|
||||
mcrypt_generic_deinit($this->td);
|
||||
return $decrypted_data;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
mcrypt_module_close($this->td);
|
||||
}
|
||||
}
|
||||
?>
|
||||
455
core/sqlquery.class.inc.php
Normal file
@@ -0,0 +1,455 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* SQLQuery
|
||||
* build an mySQL compatible SQL query
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* SQLQuery
|
||||
* build an mySQL compatible SQL query
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
|
||||
require_once('cmdbsource.class.inc.php');
|
||||
|
||||
|
||||
class SQLExpression extends BinaryExpression
|
||||
{
|
||||
}
|
||||
class ScalarSQLExpression extends ScalarExpression
|
||||
{
|
||||
}
|
||||
class TrueSQLExpression extends TrueExpression
|
||||
{
|
||||
}
|
||||
class FieldSQLExpression extends FieldExpression
|
||||
{
|
||||
}
|
||||
class VariableSQLExpression extends VariableExpression
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SQLQuery
|
||||
{
|
||||
private $m_sTable = '';
|
||||
private $m_sTableAlias = '';
|
||||
private $m_aFields = array();
|
||||
private $m_oConditionExpr = null;
|
||||
private $m_aFullTextNeedles = array();
|
||||
private $m_bToDelete = true; // The current table must be listed for deletion ?
|
||||
private $m_aValues = array(); // Values to set in case of an update query
|
||||
private $m_aJoinSelects = array();
|
||||
|
||||
public function __construct($sTable, $sTableAlias, $aFields, $oConditionExpr, $aFullTextNeedles, $bToDelete = true, $aValues = array())
|
||||
{
|
||||
// This check is not needed but for developping purposes
|
||||
//if (!CMDBSource::IsTable($sTable))
|
||||
//{
|
||||
// throw new CoreException("Unknown table '$sTable'");
|
||||
//}
|
||||
|
||||
// $aFields must be an array of "alias"=>"expr"
|
||||
// $oConditionExpr must be a condition tree
|
||||
// $aValues is an array of "alias"=>value
|
||||
|
||||
$this->m_sTable = $sTable;
|
||||
$this->m_sTableAlias = $sTableAlias;
|
||||
$this->m_aFields = $aFields;
|
||||
$this->m_oConditionExpr = $oConditionExpr;
|
||||
if (is_null($oConditionExpr))
|
||||
{
|
||||
$this->m_oConditionExpr = new TrueExpression;
|
||||
}
|
||||
else if (!$oConditionExpr instanceof Expression)
|
||||
{
|
||||
throw new CoreException('Invalid type for condition, expecting an Expression', array('class' => get_class($oConditionExpr)));
|
||||
}
|
||||
$this->m_aFullTextNeedles = $aFullTextNeedles;
|
||||
$this->m_bToDelete = $bToDelete;
|
||||
$this->m_aValues = $aValues;
|
||||
}
|
||||
|
||||
public function DisplayHtml()
|
||||
{
|
||||
if (count($this->m_aFields) == 0) $sFields = "";
|
||||
else
|
||||
{
|
||||
$aFieldDesc = array();
|
||||
foreach ($this->m_aFields as $sAlias => $oExpression)
|
||||
{
|
||||
$aFieldDesc[] = $oExpression->Render()." as <em>$sAlias</em>";
|
||||
}
|
||||
$sFields = " => ".implode(', ', $aFieldDesc);
|
||||
}
|
||||
echo "<b>$this->m_sTable</b>$sFields<br/>\n";
|
||||
// #@# todo - display html of an expression tree
|
||||
//$this->m_oConditionExpr->DisplayHtml()
|
||||
if (count($this->m_aFullTextNeedles) > 0)
|
||||
{
|
||||
echo "Full text criteria...<br/>\n";
|
||||
echo "<ul class=\"treeview\">\n";
|
||||
foreach ($this->m_aFullTextNeedles as $sFTNeedle)
|
||||
{
|
||||
echo "<li>$sFTNeedle</li>\n";
|
||||
}
|
||||
echo "</ul>";
|
||||
}
|
||||
if (count($this->m_aJoinSelects) > 0)
|
||||
{
|
||||
echo "Joined to...<br/>\n";
|
||||
echo "<ul class=\"treeview\">\n";
|
||||
foreach ($this->m_aJoinSelects as $aJoinInfo)
|
||||
{
|
||||
$sJoinType = $aJoinInfo["jointype"];
|
||||
$oSQLQuery = $aJoinInfo["select"];
|
||||
$sLeftField = $aJoinInfo["leftfield"];
|
||||
$sRightField = $aJoinInfo["rightfield"];
|
||||
$sRightTableAlias = $aJoinInfo["righttablealias"];
|
||||
|
||||
echo "<li>Join '$sJoinType', $sLeftField, $sRightTableAlias.$sRightField".$oSQLQuery->DisplayHtml()."</li>\n";
|
||||
}
|
||||
echo "</ul>";
|
||||
}
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
$oCondition = null;
|
||||
$aDelTables = array();
|
||||
$aSetValues = array();
|
||||
$this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues);
|
||||
echo "From ...<br/>\n";
|
||||
echo "<pre style=\"font-size: smaller;\">\n";
|
||||
print_r($aFrom);
|
||||
echo "</pre>";
|
||||
}
|
||||
|
||||
public function SetCondition($oConditionExpr)
|
||||
{
|
||||
$this->m_oConditionExpr = $oConditionExpr;
|
||||
}
|
||||
|
||||
public function AddCondition($oConditionExpr)
|
||||
{
|
||||
$this->m_oConditionExpr->LogAnd($oConditionExpr);
|
||||
}
|
||||
|
||||
private function AddJoin($sJoinType, $oSQLQuery, $sLeftField, $sRightField, $sRightTableAlias = '')
|
||||
{
|
||||
assert((get_class($oSQLQuery) == __CLASS__) || is_subclass_of($oSQLQuery, __CLASS__));
|
||||
// No need to check this here but for development purposes
|
||||
//if (!CMDBSource::IsField($this->m_sTable, $sLeftField))
|
||||
//{
|
||||
// throw new CoreException("Unknown field '$sLeftField' in table '".$this->m_sTable);
|
||||
//}
|
||||
|
||||
if (empty($sRightTableAlias))
|
||||
{
|
||||
$sRightTableAlias = $oSQLQuery->m_sTableAlias;
|
||||
}
|
||||
// #@# Could not be verified here because the namespace is unknown - do we need to check it there?
|
||||
//
|
||||
// if (!CMDBSource::IsField($sRightTable, $sRightField))
|
||||
// {
|
||||
// throw new CoreException("Unknown field '$sRightField' in table '".$sRightTable."'");
|
||||
// }
|
||||
|
||||
$this->m_aJoinSelects[] = array(
|
||||
"jointype" => $sJoinType,
|
||||
"select" => $oSQLQuery,
|
||||
"leftfield" => $sLeftField,
|
||||
"rightfield" => $sRightField,
|
||||
"righttablealias" => $sRightTableAlias
|
||||
);
|
||||
}
|
||||
public function AddInnerJoin($oSQLQuery, $sLeftField, $sRightField, $sRigthtTable = '')
|
||||
{
|
||||
$this->AddJoin("inner", $oSQLQuery, $sLeftField, $sRightField, $sRigthtTable);
|
||||
}
|
||||
public function AddLeftJoin($oSQLQuery, $sLeftField, $sRightField)
|
||||
{
|
||||
return $this->AddJoin("left", $oSQLQuery, $sLeftField, $sRightField);
|
||||
}
|
||||
|
||||
// Interface, build the SQL query
|
||||
public function RenderDelete($aArgs = array())
|
||||
{
|
||||
// The goal will be to complete the list as we build the Joins
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
$oCondition = null;
|
||||
$aDelTables = array();
|
||||
$aSetValues = array();
|
||||
$this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues);
|
||||
|
||||
// Target: DELETE myAlias1, myAlias2 FROM t1 as myAlias1, t2 as myAlias2, t3 as topreserve WHERE ...
|
||||
|
||||
$sDelete = self::ClauseDelete($aDelTables);
|
||||
$sFrom = self::ClauseFrom($aFrom);
|
||||
// #@# safety net to redo ?
|
||||
/*
|
||||
if ($this->m_oConditionExpr->IsAny())
|
||||
-- if (count($aConditions) == 0) --
|
||||
{
|
||||
throw new CoreException("Building a request wich will delete every object of a given table -looks suspicious- please use truncate instead...");
|
||||
}
|
||||
*/
|
||||
$sWhere = self::ClauseWhere($oCondition, $aArgs);
|
||||
return "DELETE $sDelete FROM $sFrom WHERE $sWhere";
|
||||
}
|
||||
|
||||
// Interface, build the SQL query
|
||||
public function RenderUpdate($aArgs = array())
|
||||
{
|
||||
// The goal will be to complete the list as we build the Joins
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
$oCondition = null;
|
||||
$aDelTables = array();
|
||||
$aSetValues = array();
|
||||
$this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues);
|
||||
|
||||
$sFrom = self::ClauseFrom($aFrom);
|
||||
$sValues = self::ClauseValues($aSetValues);
|
||||
$sWhere = self::ClauseWhere($oCondition, $aArgs);
|
||||
return "UPDATE $sFrom SET $sValues WHERE $sWhere";
|
||||
}
|
||||
|
||||
// Interface, build the SQL query
|
||||
public function RenderSelect($aOrderBy = array(), $aArgs = array(), $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
|
||||
{
|
||||
// The goal will be to complete the lists as we build the Joins
|
||||
$aFrom = array();
|
||||
$aFields = array();
|
||||
$oCondition = null;
|
||||
$aDelTables = array();
|
||||
$aSetValues = array();
|
||||
$this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues);
|
||||
|
||||
$sFrom = self::ClauseFrom($aFrom);
|
||||
$sWhere = self::ClauseWhere($oCondition, $aArgs);
|
||||
if ($bGetCount)
|
||||
{
|
||||
$sSQL = "SELECT COUNT(*) AS COUNT FROM $sFrom WHERE $sWhere";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelect = self::ClauseSelect($aFields);
|
||||
$sOrderBy = self::ClauseOrderBy($aOrderBy);
|
||||
if (!empty($sOrderBy))
|
||||
{
|
||||
$sOrderBy = "ORDER BY $sOrderBy";
|
||||
}
|
||||
if ($iLimitCount > 0)
|
||||
{
|
||||
$sLimit = 'LIMIT '.$iLimitStart.', '.$iLimitCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLimit = '';
|
||||
}
|
||||
$sSQL = "SELECT DISTINCT $sSelect FROM $sFrom WHERE $sWhere $sOrderBy $sLimit";
|
||||
}
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
private static function ClauseSelect($aFields)
|
||||
{
|
||||
$aSelect = array();
|
||||
foreach ($aFields as $sFieldAlias => $sSQLExpr)
|
||||
{
|
||||
$aSelect[] = "$sSQLExpr AS $sFieldAlias";
|
||||
}
|
||||
$sSelect = implode(', ', $aSelect);
|
||||
return $sSelect;
|
||||
}
|
||||
|
||||
private static function ClauseDelete($aDelTableAliases)
|
||||
{
|
||||
$aDelTables = array();
|
||||
foreach ($aDelTableAliases as $sTableAlias)
|
||||
{
|
||||
$aDelTables[] = "$sTableAlias";
|
||||
}
|
||||
$sDelTables = implode(', ', $aDelTables);
|
||||
return $sDelTables;
|
||||
}
|
||||
|
||||
private static function ClauseFrom($aFrom)
|
||||
{
|
||||
$sFrom = "";
|
||||
foreach ($aFrom as $sTableAlias => $aJoinInfo)
|
||||
{
|
||||
switch ($aJoinInfo["jointype"])
|
||||
{
|
||||
case "first":
|
||||
$sFrom .= "`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
break;
|
||||
case "inner":
|
||||
$sFrom .= " INNER JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
$sFrom .= ") ON ".$aJoinInfo["joincondition"];
|
||||
break;
|
||||
case "left":
|
||||
$sFrom .= " LEFT JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`";
|
||||
$sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]);
|
||||
$sFrom .= ") ON ".$aJoinInfo["joincondition"];
|
||||
break;
|
||||
default:
|
||||
throw new CoreException("Unknown jointype: '".$aJoinInfo["jointype"]."'");
|
||||
}
|
||||
}
|
||||
return $sFrom;
|
||||
}
|
||||
|
||||
private static function ClauseValues($aValues)
|
||||
{
|
||||
$aSetValues = array();
|
||||
foreach ($aValues as $sFieldSpec => $value)
|
||||
{
|
||||
$aSetValues[] = "$sFieldSpec = ".CMDBSource::Quote($value);
|
||||
}
|
||||
$sSetValues = implode(', ', $aSetValues);
|
||||
return $sSetValues;
|
||||
}
|
||||
|
||||
private static function ClauseWhere($oConditionExpr, $aArgs = array())
|
||||
{
|
||||
return $oConditionExpr->Render($aArgs);
|
||||
}
|
||||
|
||||
private static function ClauseOrderBy($aOrderBy)
|
||||
{
|
||||
$aOrderBySpec = array();
|
||||
foreach($aOrderBy as $sFieldAlias => $bAscending)
|
||||
{
|
||||
$aOrderBySpec[] = '`'.$sFieldAlias.'`'.($bAscending ? " ASC" : " DESC");
|
||||
}
|
||||
$sOrderBy = implode(", ", $aOrderBySpec);
|
||||
return $sOrderBy;
|
||||
}
|
||||
|
||||
// Purpose: prepare the query data, once for all
|
||||
private function privRender(&$aFrom, &$aFields, &$oCondition, &$aDelTables, &$aSetValues)
|
||||
{
|
||||
$sTableAlias = $this->privRenderSingleTable($aFrom, $aFields, $aDelTables, $aSetValues);
|
||||
|
||||
// Add the full text search condition, based on each and every requested field
|
||||
//
|
||||
// To be updated with a real full text search based on the mySQL settings
|
||||
// (then it might move somewhere else !)
|
||||
//
|
||||
$oCondition = $this->m_oConditionExpr;
|
||||
if ((count($aFields) > 0) && (count($this->m_aFullTextNeedles) > 0))
|
||||
{
|
||||
$aFieldExp = array();
|
||||
foreach ($aFields as $sField)
|
||||
{
|
||||
// This is TEMPORARY (that's why it is weird, actually)
|
||||
// Full text match will be done as an expression in the filter condition
|
||||
|
||||
// $sField is already a string `table`.`column`
|
||||
// Let's make an expression out of it (again !)
|
||||
$aFieldExp[] = Expression::FromOQL($sField);
|
||||
}
|
||||
$oFullTextExpr = new CharConcatExpression($aFieldExp);
|
||||
// The cast is necessary because the CONCAT result in a binary string:
|
||||
// if any of the field is a binary string => case sensitive comparison
|
||||
//
|
||||
foreach($this->m_aFullTextNeedles as $sFTNeedle)
|
||||
{
|
||||
$oNewCond = new BinaryExpression($oFullTextExpr, 'LIKE', new ScalarExpression("%$sFTNeedle%"));
|
||||
$oCondition = $oCondition->LogAnd($oNewCond);
|
||||
}
|
||||
}
|
||||
|
||||
return $sTableAlias;
|
||||
}
|
||||
|
||||
private function privRenderSingleTable(&$aFrom, &$aFields, &$aDelTables, &$aSetValues, $sJoinType = 'first', $sCallerAlias = '', $sLeftField = '', $sRightField = '', $sRightTableAlias = '')
|
||||
{
|
||||
$aActualTableFields = CMDBSource::GetTableFieldsList($this->m_sTable);
|
||||
|
||||
$aTranslationTable[$this->m_sTable]['*'] = $this->m_sTableAlias;
|
||||
|
||||
// Handle the various kinds of join (or first table in the list)
|
||||
//
|
||||
if (empty($sRightTableAlias))
|
||||
{
|
||||
$sRightTableAlias = $this->m_sTableAlias;
|
||||
}
|
||||
$sJoinCond = "`$sCallerAlias`.`$sLeftField` = `$sRightTableAlias`.`$sRightField`";
|
||||
switch ($sJoinType)
|
||||
{
|
||||
case "first":
|
||||
$aFrom[$this->m_sTableAlias] = array("jointype"=>"first", "tablename"=>$this->m_sTable, "joincondition"=>"");
|
||||
break;
|
||||
case "inner":
|
||||
case "left":
|
||||
// table or tablealias ???
|
||||
$aFrom[$this->m_sTableAlias] = array("jointype"=>$sJoinType, "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond");
|
||||
break;
|
||||
}
|
||||
|
||||
// Given the alias, modify the fields and conditions
|
||||
// before adding them into the current lists
|
||||
//
|
||||
foreach($this->m_aFields as $sAlias => $oExpression)
|
||||
{
|
||||
$sTable = $oExpression->GetParent();
|
||||
$sColumn = $oExpression->GetName();
|
||||
$aFields["`$sAlias`"] = $oExpression->Render();
|
||||
}
|
||||
if ($this->m_bToDelete)
|
||||
{
|
||||
$aDelTables[] = "`{$this->m_sTableAlias}`";
|
||||
}
|
||||
foreach($this->m_aValues as $sFieldName=>$value)
|
||||
{
|
||||
$aSetValues["`{$this->m_sTableAlias}`.`$sFieldName`"] = $value; // quoted further!
|
||||
}
|
||||
|
||||
// loop on joins, to complete the list of tables/fields/conditions
|
||||
//
|
||||
$aTempFrom = array(); // temporary subset of 'from' specs, to be grouped in the final query
|
||||
foreach ($this->m_aJoinSelects as $aJoinData)
|
||||
{
|
||||
$sJoinType = $aJoinData["jointype"];
|
||||
$oRightSelect = $aJoinData["select"];
|
||||
$sLeftField = $aJoinData["leftfield"];
|
||||
$sRightField = $aJoinData["rightfield"];
|
||||
$sRightTableAlias = $aJoinData["righttablealias"];
|
||||
|
||||
$sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $sJoinType, $this->m_sTableAlias, $sLeftField, $sRightField, $sRightTableAlias);
|
||||
}
|
||||
$aFrom[$this->m_sTableAlias]['subfrom'] = $aTempFrom;
|
||||
|
||||
return $this->m_sTableAlias;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
137
core/stimulus.class.inc.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Object lifecycle management: stimulus
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A stimulus is the trigger that makes the lifecycle go ahead (state machine)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
|
||||
// #@# Really dirty !!!
|
||||
// #@# TO BE CLEANED -> ALIGN WITH OTHER METAMODEL DECLARATIONS
|
||||
|
||||
class ObjectStimulus
|
||||
{
|
||||
private $m_aParams = array();
|
||||
private $m_sHostClass = null;
|
||||
private $m_sCode = null;
|
||||
|
||||
public function __construct($sCode, $aParams)
|
||||
{
|
||||
$this->m_sCode = $sCode;
|
||||
$this->m_aParams = $aParams;
|
||||
$this->ConsistencyCheck();
|
||||
}
|
||||
|
||||
public function SetHostClass($sHostClass)
|
||||
{
|
||||
$this->m_sHostClass = $sHostClass;
|
||||
}
|
||||
public function GetHostClass()
|
||||
{
|
||||
return $this->m_sHostClass;
|
||||
}
|
||||
public function GetCode()
|
||||
{
|
||||
return $this->m_sCode;
|
||||
}
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return Dict::S('Class:'.$this->m_sHostClass.'/Stimulus:'.$this->m_sCode, $this->m_sCode);
|
||||
}
|
||||
public function GetDescription()
|
||||
{
|
||||
return Dict::S('Class:'.$this->m_sHostClass.'/Stimulus:'.$this->m_sCode.'+', '');
|
||||
}
|
||||
|
||||
public function GetLabel_Obsolete()
|
||||
{
|
||||
// Written for compatibility with a data model written prior to version 0.9.1
|
||||
if (array_key_exists('label', $this->m_aParams))
|
||||
{
|
||||
return $this->m_aParams['label'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->GetLabel();
|
||||
}
|
||||
}
|
||||
|
||||
public function GetDescription_Obsolete()
|
||||
{
|
||||
// Written for compatibility with a data model written prior to version 0.9.1
|
||||
if (array_key_exists('description', $this->m_aParams))
|
||||
{
|
||||
return $this->m_aParams['description'];
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->GetDescription();
|
||||
}
|
||||
}
|
||||
|
||||
// obsolete- public function Get($sParamName) {return $this->m_aParams[$sParamName];}
|
||||
|
||||
// Note: I could factorize this code with the parameter management made for the AttributeDef class
|
||||
// to be overloaded
|
||||
static protected function ListExpectedParams()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
private function ConsistencyCheck()
|
||||
{
|
||||
|
||||
// Check that any mandatory param has been specified
|
||||
//
|
||||
$aExpectedParams = $this->ListExpectedParams();
|
||||
foreach($aExpectedParams as $sParamName)
|
||||
{
|
||||
if (!array_key_exists($sParamName, $this->m_aParams))
|
||||
{
|
||||
$aBacktrace = debug_backtrace();
|
||||
$sTargetClass = $aBacktrace[2]["class"];
|
||||
$sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"];
|
||||
throw new CoreException("missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class StimulusUserAction extends ObjectStimulus
|
||||
{
|
||||
// Entry in the menus
|
||||
}
|
||||
|
||||
class StimulusInternal extends ObjectStimulus
|
||||
{
|
||||
// Applied from page xxxx
|
||||
}
|
||||
|
||||
?>
|
||||
253
core/trigger.class.inc.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Persistent class Trigger and derived
|
||||
* User defined triggers, that may be used in conjunction with user defined actions
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* A user defined trigger, to customize the application
|
||||
* A trigger will activate an action
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class Trigger extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "description",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_trigger",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "realclass",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list", array("linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"trigger_id", "ext_key_to_remote"=>"action_id", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('finalclass', 'description', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass')); // 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 DoActivate($aContextArgs)
|
||||
{
|
||||
// Find the related
|
||||
$oLinkedActions = $this->Get('action_list');
|
||||
while ($oLink = $oLinkedActions->Fetch())
|
||||
{
|
||||
$iActionId = $oLink->Get('action_id');
|
||||
$oAction = MetaModel::GetObject('Action', $iActionId);
|
||||
if ($oAction->IsActive())
|
||||
{
|
||||
$oAction->DoExecute($this, $aContextArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TriggerOnObject extends Trigger
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "description",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_trigger_onobject",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("class_category"=>"bizmodel", "more_values"=>null, "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // 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 class TriggerOnStateChange extends TriggerOnObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "description",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_trigger_onstatechange",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("state", array("allowed_values"=>null, "sql"=>"state", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'state')); // 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 TriggerOnStateEnter extends TriggerOnStateChange
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "description",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_trigger_onstateenter",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // 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 TriggerOnStateLeave extends TriggerOnStateChange
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "description",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_trigger_onstateleave",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
// MetaModel::Init_SetZListItems('standard_search', array('')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
class TriggerOnObjectCreate extends TriggerOnObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "description",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_trigger_onobjcreate",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // 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 lnkTriggerAction extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(""),
|
||||
"db_table" => "priv_link_action_trigger",
|
||||
"db_key_field" => "link_id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "jointype"=> '', "allowed_values"=>null, "sql"=>"action_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("action_name", array("allowed_values"=>null, "extkey_attcode"=> 'action_id', "target_attcode"=>"name")));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> '', "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("trigger_name", array("allowed_values"=>null, "extkey_attcode"=> 'trigger_id', "target_attcode"=>"description")));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("order", array("allowed_values"=>null, "sql"=>"order", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list
|
||||
MetaModel::Init_SetZListItems('list', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('action_id', 'trigger_id', 'order')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('action_id', 'trigger_id', 'order')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
?>
|
||||
823
core/userrights.class.inc.php
Normal file
@@ -0,0 +1,823 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* User rights management API
|
||||
*
|
||||
* @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 UserRightException extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
define('UR_ALLOWED_NO', 0);
|
||||
define('UR_ALLOWED_YES', 1);
|
||||
define('UR_ALLOWED_DEPENDS', 2);
|
||||
|
||||
define('UR_ACTION_READ', 1); // View an object
|
||||
define('UR_ACTION_MODIFY', 2); // Create/modify an object/attribute
|
||||
define('UR_ACTION_DELETE', 3); // Delete an object
|
||||
|
||||
define('UR_ACTION_BULK_READ', 4); // Export multiple objects
|
||||
define('UR_ACTION_BULK_MODIFY', 5); // Create/modify multiple objects
|
||||
define('UR_ACTION_BULK_DELETE', 6); // Delete multiple objects
|
||||
|
||||
define('UR_ACTION_APPLICATION_DEFINED', 10000); // Application specific actions (CSV import, View schema...)
|
||||
|
||||
/**
|
||||
* User management module API
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class UserRightsAddOnAPI
|
||||
{
|
||||
abstract public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US'); // could be used during initial installation
|
||||
|
||||
abstract public function Init(); // loads data (possible optimizations)
|
||||
|
||||
// Used to build select queries showing only objects visible for the given user
|
||||
abstract public function GetSelectFilter($sLogin, $sClass); // returns a filter object
|
||||
|
||||
abstract public function IsActionAllowed($oUser, $sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null);
|
||||
abstract public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, /*dbObjectSet*/ $oInstanceSet = null);
|
||||
abstract public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null);
|
||||
abstract public function IsAdministrator($oUser);
|
||||
abstract public function IsPortalUser($oUser);
|
||||
abstract public function FlushPrivileges();
|
||||
}
|
||||
|
||||
|
||||
abstract class User extends cmdbAbstractObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "login",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_user",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("contactid", array("targetclass"=>"Person", "allowed_values"=>null, "sql"=>"contactid", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("last_name", array("allowed_values"=>null, "extkey_attcode"=> 'contactid', "target_attcode"=>"name")));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("first_name", array("allowed_values"=>null, "extkey_attcode"=> 'contactid', "target_attcode"=>"first_name")));
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("email", array("allowed_values"=>null, "extkey_attcode"=> 'contactid', "target_attcode"=>"email")));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeString("login", array("allowed_values"=>null, "sql"=>"login", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>"EN US", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("profile_list", array("linked_class"=>"URP_UserProfile", "ext_key_to_me"=>"userid", "ext_key_to_remote"=>"profileid", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("allowed_org_list", array("linked_class"=>"URP_UserOrg", "ext_key_to_me"=>"userid", "ext_key_to_remote"=>"allowed_org_id", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'login')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
abstract public function CheckCredentials($sPassword);
|
||||
abstract public function TrustWebServerContext();
|
||||
abstract public function CanChangePassword();
|
||||
abstract public function ChangePassword($sOldPassword, $sNewPassword);
|
||||
|
||||
/*
|
||||
* Compute a name in best effort mode
|
||||
*/
|
||||
public function GetFriendlyName()
|
||||
{
|
||||
if (!MetaModel::IsValidAttCode(get_class($this), 'contactid'))
|
||||
{
|
||||
return $this->Get('login');
|
||||
}
|
||||
if ($this->Get('contactid') != 0)
|
||||
{
|
||||
$sFirstName = $this->Get('first_name');
|
||||
$sLastName = $this->Get('last_name');
|
||||
$sEmail = $this->Get('email');
|
||||
if (strlen($sFirstName) > 0)
|
||||
{
|
||||
return "$sFirstName $sLastName";
|
||||
}
|
||||
elseif (strlen($sEmail) > 0)
|
||||
{
|
||||
return "$sLastName <$sEmail>";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sLastName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Overload the standard behavior
|
||||
*/
|
||||
public function DoCheckToWrite()
|
||||
{
|
||||
parent::DoCheckToWrite();
|
||||
|
||||
// Note: This MUST be factorized later: declare unique keys (set of columns) in the data model
|
||||
$aChanges = $this->ListChanges();
|
||||
if (array_key_exists('login', $aChanges))
|
||||
{
|
||||
$sNewLogin = $aChanges['login'];
|
||||
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT User WHERE login = :newlogin");
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('newlogin' => $sNewLogin));
|
||||
if ($oSet->Count() > 0)
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:LoginMustBeUnique', $sNewLogin);
|
||||
}
|
||||
}
|
||||
// Check that this user has at least one profile assigned
|
||||
$oSet = $this->Get('profile_list');
|
||||
$aProfileLinks = $oSet->ToArray();
|
||||
if (count($aProfileLinks) == 0)
|
||||
{
|
||||
$this->m_aCheckIssues[] = Dict::Format('Class:User/Error:AtLeastOneProfileIsNeeded');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function GetGrantAsHtml($sClass, $iAction)
|
||||
{
|
||||
if (UserRights::IsActionAllowed($sClass, $iAction, null, $this))
|
||||
{
|
||||
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
function DoShowGrantSumary($oPage, $sClassCategory)
|
||||
{
|
||||
if (UserRights::IsAdministrator($this))
|
||||
{
|
||||
// Looks dirty, but ok that's THE ONE
|
||||
$oPage->p(Dict::S('UI:UserManagement:AdminProfile+'));
|
||||
return;
|
||||
}
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
|
||||
$aDisplayData = array();
|
||||
foreach (MetaModel::GetClasses($sClassCategory) as $sClass)
|
||||
{
|
||||
$aClassStimuli = MetaModel::EnumStimuli($sClass);
|
||||
if (count($aClassStimuli) > 0)
|
||||
{
|
||||
$aStimuli = array();
|
||||
foreach ($aClassStimuli as $sStimulusCode => $oStimulus)
|
||||
{
|
||||
if (UserRights::IsStimulusAllowed($sClass, $sStimulusCode, null, $this))
|
||||
{
|
||||
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription()).'">'.htmlentities($oStimulus->GetLabel()).'</span>';
|
||||
}
|
||||
}
|
||||
$sStimuli = implode(', ', $aStimuli);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sStimuli = '<em title="'.Dict::S('UI:UserManagement:NoLifeCycleApplicable+').'">'.Dict::S('UI:UserManagement:NoLifeCycleApplicable').'</em>';
|
||||
}
|
||||
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName($sClass),
|
||||
'read' => $this->GetGrantAsHtml($sClass, UR_ACTION_READ),
|
||||
'bulkread' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_READ),
|
||||
'write' => $this->GetGrantAsHtml($sClass, UR_ACTION_MODIFY),
|
||||
'bulkwrite' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_MODIFY),
|
||||
'stimuli' => $sStimuli,
|
||||
);
|
||||
}
|
||||
|
||||
$oKPI->ComputeAndReport('Computation of user rights');
|
||||
|
||||
$aDisplayConfig = array();
|
||||
$aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
|
||||
$aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
|
||||
$aDisplayConfig['bulkread'] = array('label' => Dict::S('UI:UserManagement:Action:BulkRead'), 'description' => Dict::S('UI:UserManagement:Action:BulkRead+'));
|
||||
$aDisplayConfig['write'] = array('label' => Dict::S('UI:UserManagement:Action:Modify'), 'description' => Dict::S('UI:UserManagement:Action:Modify+'));
|
||||
$aDisplayConfig['bulkwrite'] = array('label' => Dict::S('UI:UserManagement:Action:BulkModify'), 'description' => Dict::S('UI:UserManagement:Action:BulkModify+'));
|
||||
$aDisplayConfig['stimuli'] = array('label' => Dict::S('UI:UserManagement:Action:Stimuli'), 'description' => Dict::S('UI:UserManagement:Action:Stimuli+'));
|
||||
$oPage->table($aDisplayConfig, $aDisplayData);
|
||||
}
|
||||
|
||||
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
|
||||
{
|
||||
parent::DisplayBareRelations($oPage, $bEditMode);
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$oPage->SetCurrentTab(Dict::S('UI:UserManagement:GrantMatrix'));
|
||||
$this->DoShowGrantSumary($oPage, 'bizmodel');
|
||||
|
||||
// debug
|
||||
if (false)
|
||||
{
|
||||
$oPage->SetCurrentTab('More on user rigths (dev only)');
|
||||
$oPage->add("<h3>User rights</h3>\n");
|
||||
$this->DoShowGrantSumary($oPage, 'addon/userrights');
|
||||
$oPage->add("<h3>Change log</h3>\n");
|
||||
$this->DoShowGrantSumary($oPage, 'core/cmdb');
|
||||
$oPage->add("<h3>Application</h3>\n");
|
||||
$this->DoShowGrantSumary($oPage, 'application');
|
||||
$oPage->add("<h3>GUI</h3>\n");
|
||||
$this->DoShowGrantSumary($oPage, 'gui');
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract class for all types of "internal" authentication i.e. users
|
||||
* for which the application is supplied a login and a password opposed
|
||||
* to "external" users for whom the authentication is performed outside
|
||||
* of the application (by the web server for example).
|
||||
* Note that "internal" users do not necessary correspond to a local authentication
|
||||
* they may be authenticated by a remote system, like in authent-ldap.
|
||||
*/
|
||||
abstract class UserInternal extends User
|
||||
{
|
||||
// Nothing special, just a base class to categorize this type of authenticated users
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "login",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('login'),
|
||||
"db_table" => "priv_internalUser",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('contactid', 'first_name', 'email', 'login', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'login')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('login', 'contactid')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User management core API
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class UserRights
|
||||
{
|
||||
protected static $m_oAddOn;
|
||||
protected static $m_oUser;
|
||||
protected static $m_oRealUser;
|
||||
|
||||
public static function SelectModule($sModuleName)
|
||||
{
|
||||
if (!class_exists($sModuleName))
|
||||
{
|
||||
throw new CoreException("Could not select this module, '$sModuleName' in not a valid class name");
|
||||
return;
|
||||
}
|
||||
if (!is_subclass_of($sModuleName, 'UserRightsAddOnAPI'))
|
||||
{
|
||||
throw new CoreException("Could not select this module, the class '$sModuleName' is not derived from UserRightsAddOnAPI");
|
||||
return;
|
||||
}
|
||||
self::$m_oAddOn = new $sModuleName;
|
||||
self::$m_oAddOn->Init();
|
||||
self::$m_oUser = null;
|
||||
self::$m_oRealUser = null;
|
||||
}
|
||||
|
||||
public static function GetModuleInstance()
|
||||
{
|
||||
return self::$m_oAddOn;
|
||||
}
|
||||
|
||||
// Installation: create the very first user
|
||||
public static function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
$bRes = self::$m_oAddOn->CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage);
|
||||
self::FlushPrivileges(true /* reset admin cache */);
|
||||
return $bRes;
|
||||
}
|
||||
|
||||
protected static function IsLoggedIn()
|
||||
{
|
||||
if (self::$m_oUser == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static function Login($sName, $sAuthentication = 'any')
|
||||
{
|
||||
$oUser = self::FindUser($sName, $sAuthentication);
|
||||
if (is_null($oUser))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
self::$m_oUser = $oUser;
|
||||
Dict::SetUserLanguage(self::GetUserLanguage());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function CheckCredentials($sName, $sPassword, $sAuthentication = 'any')
|
||||
{
|
||||
$oUser = self::FindUser($sName, $sAuthentication);
|
||||
if (is_null($oUser))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$oUser->CheckCredentials($sPassword))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function TrustWebServerContext()
|
||||
{
|
||||
if (!is_null(self::$m_oUser))
|
||||
{
|
||||
return self::$m_oUser->TrustWebServerContext();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function CanChangePassword()
|
||||
{
|
||||
if (MetaModel::DBIsReadOnly())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_null(self::$m_oUser))
|
||||
{
|
||||
return self::$m_oUser->CanChangePassword();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function CanLogOff()
|
||||
{
|
||||
if (!is_null(self::$m_oUser))
|
||||
{
|
||||
return self::$m_oUser->CanLogOff();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function ChangePassword($sOldPassword, $sNewPassword, $sName = '')
|
||||
{
|
||||
if (empty($sName))
|
||||
{
|
||||
$oUser = self::$m_oUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
// find the id out of the login string
|
||||
$oUser = self::FindUser($sName);
|
||||
}
|
||||
if (is_null($oUser))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $oUser->ChangePassword($sOldPassword, $sNewPassword);
|
||||
}
|
||||
}
|
||||
|
||||
public static function Impersonate($sName, $sPassword)
|
||||
{
|
||||
if (!self::CheckLogin()) return false;
|
||||
|
||||
$oUser = self::FindUser($sName);
|
||||
if (is_null($oUser))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!$oUser->CheckCredentials($sPassword))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
self::$m_oRealUser = self::$m_oUser;
|
||||
self::$m_oUser = $oUser;
|
||||
Dict::SetUserLanguage(self::GetUserLanguage());
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function GetUser()
|
||||
{
|
||||
if (is_null(self::$m_oUser))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::$m_oUser->Get('login');
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetUserObject()
|
||||
{
|
||||
if (is_null(self::$m_oUser))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::$m_oUser;
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetUserLanguage()
|
||||
{
|
||||
if (is_null(self::$m_oUser))
|
||||
{
|
||||
return 'EN US';
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::$m_oUser->Get('language');
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetUserId($sName = '')
|
||||
{
|
||||
if (empty($sName))
|
||||
{
|
||||
// return current user id
|
||||
if (is_null(self::$m_oUser))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return self::$m_oUser->GetKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
// find the id out of the login string
|
||||
$oUser = self::$m_oAddOn->FindUser($sName);
|
||||
if (is_null($oUser))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return $oUser->GetKey();
|
||||
}
|
||||
}
|
||||
|
||||
public static function GetContactId($sName = '')
|
||||
{
|
||||
if (empty($sName))
|
||||
{
|
||||
$oUser = self::$m_oUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oUser = FindUser($sName);
|
||||
}
|
||||
if (is_null($oUser))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
if (!MetaModel::IsValidAttCode(get_class($oUser), 'contactid'))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
return $oUser->Get('contactid');
|
||||
}
|
||||
|
||||
// Render the user name in best effort mode
|
||||
public static function GetUserFriendlyName($sName = '')
|
||||
{
|
||||
if (empty($sName))
|
||||
{
|
||||
$oUser = self::$m_oUser;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oUser = FindUser($sName);
|
||||
}
|
||||
if (is_null($oUser))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
return $oUser->GetFriendlyName();
|
||||
}
|
||||
|
||||
public static function IsImpersonated()
|
||||
{
|
||||
if (is_null(self::$m_oRealUser))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function GetRealUser()
|
||||
{
|
||||
if (is_null(self::$m_oRealUser))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
return self::$m_oRealUser->Get('login');
|
||||
}
|
||||
|
||||
public static function GetRealUserId()
|
||||
{
|
||||
if (is_null(self::$m_oRealUser))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
return self::$m_oRealUser->GetKey();
|
||||
}
|
||||
|
||||
public static function GetRealUserFriendlyName()
|
||||
{
|
||||
if (is_null(self::$m_oRealUser))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
return self::$m_oRealUser->GetFriendlyName();
|
||||
}
|
||||
|
||||
protected static function CheckLogin()
|
||||
{
|
||||
if (!self::IsLoggedIn())
|
||||
{
|
||||
//throw new UserRightException('No user logged in', array());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function GetSelectFilter($sClass)
|
||||
{
|
||||
// When initializing, we need to let everything pass trough
|
||||
if (!self::CheckLogin()) return true;
|
||||
|
||||
if (self::IsAdministrator()) return true;
|
||||
// Portal users actions are limited by the portal page...
|
||||
if (self::IsPortalUser()) return true;
|
||||
|
||||
if (MetaModel::HasCategory($sClass, 'bizmodel'))
|
||||
{
|
||||
return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static function IsActionAllowed($sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null, $oUser = null)
|
||||
{
|
||||
// When initializing, we need to let everything pass trough
|
||||
if (!self::CheckLogin()) return true;
|
||||
|
||||
if (MetaModel::DBIsReadOnly())
|
||||
{
|
||||
if ($iActionCode == UR_ACTION_MODIFY) return false;
|
||||
if ($iActionCode == UR_ACTION_DELETE) return false;
|
||||
if ($iActionCode == UR_ACTION_BULK_MODIFY) return false;
|
||||
if ($iActionCode == UR_ACTION_BULK_DELETE) return false;
|
||||
}
|
||||
|
||||
if (self::IsAdministrator($oUser)) return true;
|
||||
|
||||
if (MetaModel::HasCategory($sClass, 'bizmodel'))
|
||||
{
|
||||
// #@# Temporary?????
|
||||
// The read access is controlled in MetaModel::MakeSelectQuery()
|
||||
if ($iActionCode == UR_ACTION_READ) return true;
|
||||
|
||||
if (is_null($oUser))
|
||||
{
|
||||
$oUser = self::$m_oUser;
|
||||
}
|
||||
return self::$m_oAddOn->IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet);
|
||||
}
|
||||
elseif(($iActionCode == UR_ACTION_READ) && MetaModel::HasCategory($sClass, 'view_in_gui'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other classes could be edited/listed by the administrators
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function IsStimulusAllowed($sClass, $sStimulusCode, /*dbObjectSet*/ $oInstanceSet = null, $oUser = null)
|
||||
{
|
||||
// When initializing, we need to let everything pass trough
|
||||
if (!self::CheckLogin()) return true;
|
||||
|
||||
if (MetaModel::DBIsReadOnly())
|
||||
{
|
||||
if ($iActionCode == UR_ACTION_MODIFY) return false;
|
||||
if ($iActionCode == UR_ACTION_DELETE) return false;
|
||||
if ($iActionCode == UR_ACTION_BULK_MODIFY) return false;
|
||||
if ($iActionCode == UR_ACTION_BULK_DELETE) return false;
|
||||
}
|
||||
|
||||
if (self::IsAdministrator($oUser)) return true;
|
||||
|
||||
if (MetaModel::HasCategory($sClass, 'bizmodel'))
|
||||
{
|
||||
if (is_null($oUser))
|
||||
{
|
||||
$oUser = self::$m_oUser;
|
||||
}
|
||||
return self::$m_oAddOn->IsStimulusAllowed($oUser, $sClass, $sStimulusCode, $oInstanceSet);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Other classes could be edited/listed by the administrators
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null, $oUser = null)
|
||||
{
|
||||
// When initializing, we need to let everything pass trough
|
||||
if (!self::CheckLogin()) return true;
|
||||
|
||||
if (MetaModel::DBIsReadOnly())
|
||||
{
|
||||
if ($iActionCode == UR_ACTION_MODIFY) return false;
|
||||
if ($iActionCode == UR_ACTION_DELETE) return false;
|
||||
if ($iActionCode == UR_ACTION_BULK_MODIFY) return false;
|
||||
if ($iActionCode == UR_ACTION_BULK_DELETE) return false;
|
||||
}
|
||||
|
||||
if (self::IsAdministrator($oUser)) return true;
|
||||
|
||||
// this module is forbidden for non admins
|
||||
if (MetaModel::HasCategory($sClass, 'addon/userrights')) return false;
|
||||
|
||||
// the rest is allowed (#@# to be improved)
|
||||
if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true;
|
||||
|
||||
if (is_null($oUser))
|
||||
{
|
||||
$oUser = self::$m_oUser;
|
||||
}
|
||||
return self::$m_oAddOn->IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet);
|
||||
}
|
||||
|
||||
static $m_aAdmins = array();
|
||||
public static function IsAdministrator($oUser = null)
|
||||
{
|
||||
if (!self::CheckLogin()) return false;
|
||||
|
||||
if (is_null($oUser))
|
||||
{
|
||||
$oUser = self::$m_oUser;
|
||||
}
|
||||
$iUser = $oUser->GetKey();
|
||||
if (!isset(self::$m_aAdmins[$iUser]))
|
||||
{
|
||||
self::$m_aAdmins[$iUser] = self::$m_oAddOn->IsAdministrator($oUser);
|
||||
}
|
||||
return self::$m_aAdmins[$iUser];
|
||||
}
|
||||
|
||||
static $m_aPortalUsers = array();
|
||||
public static function IsPortalUser($oUser = null)
|
||||
{
|
||||
if (!self::CheckLogin()) return false;
|
||||
|
||||
if (is_null($oUser))
|
||||
{
|
||||
$oUser = self::$m_oUser;
|
||||
}
|
||||
$iUser = $oUser->GetKey();
|
||||
if (!isset(self::$m_aPortalUsers[$iUser]))
|
||||
{
|
||||
self::$m_aPortalUsers[$iUser] = self::$m_oAddOn->IsPortalUser($oUser);
|
||||
}
|
||||
return self::$m_aPortalUsers[$iUser];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset cached data
|
||||
* @param Bool Reset admin cache as well
|
||||
* @return void
|
||||
*/
|
||||
// Reset cached data
|
||||
//
|
||||
public static function FlushPrivileges($bResetAdminCache = false)
|
||||
{
|
||||
if ($bResetAdminCache)
|
||||
{
|
||||
self::$m_aAdmins = array();
|
||||
}
|
||||
return self::$m_oAddOn->FlushPrivileges();
|
||||
}
|
||||
|
||||
static $m_aCacheUsers;
|
||||
/**
|
||||
* Find a user based on its login and its type of authentication
|
||||
* @param string $sLogin Login/identifier of the user
|
||||
* @param string $sAuthentication Type of authentication used: internal|external|any
|
||||
* @return User The found user or null
|
||||
*/
|
||||
protected static function FindUser($sLogin, $sAuthentication = 'any')
|
||||
{
|
||||
if ($sAuthentication == 'any')
|
||||
{
|
||||
$oUser = self::FindUser($sLogin, 'internal');
|
||||
if ($oUser == null)
|
||||
{
|
||||
$oUser = self::FindUser($sLogin, 'external');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isset(self::$m_aCacheUsers))
|
||||
{
|
||||
self::$m_aCacheUsers = array('internal' => array(), 'external' => array());
|
||||
}
|
||||
|
||||
if (!isset(self::$m_aCacheUsers[$sAuthentication][$sLogin]))
|
||||
{
|
||||
switch($sAuthentication)
|
||||
{
|
||||
case 'external':
|
||||
$sBaseClass = 'UserExternal';
|
||||
break;
|
||||
|
||||
case 'internal':
|
||||
$sBaseClass = 'UserInternal';
|
||||
break;
|
||||
|
||||
default:
|
||||
echo "<p>sAuthentication = $sAuthentication</p>\n";
|
||||
assert(false); // should never happen
|
||||
}
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT $sBaseClass WHERE login = :login");
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('login' => $sLogin));
|
||||
$oUser = $oSet->fetch();
|
||||
self::$m_aCacheUsers[$sAuthentication][$sLogin] = $oUser;
|
||||
}
|
||||
$oUser = self::$m_aCacheUsers[$sAuthentication][$sLogin];
|
||||
}
|
||||
return $oUser;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
284
core/valuesetdef.class.inc.php
Normal file
@@ -0,0 +1,284 @@
|
||||
<?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
|
||||
|
||||
/**
|
||||
* Value set definitions (from a fixed list or from a query, etc.)
|
||||
*
|
||||
* @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('MyHelpers.class.inc.php');
|
||||
|
||||
/**
|
||||
* ValueSetDefinition
|
||||
* value sets API and implementations
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
abstract class ValueSetDefinition
|
||||
{
|
||||
protected $m_bIsLoaded = false;
|
||||
protected $m_aValues = array();
|
||||
|
||||
|
||||
// Displayable description that could be computed out of the std usage context
|
||||
public function GetValuesDescription()
|
||||
{
|
||||
$aValues = $this->GetValues(array(), '');
|
||||
$aDisplayedValues = array();
|
||||
foreach($aValues as $key => $value)
|
||||
{
|
||||
$aDisplayedValues[] = "$key => $value";
|
||||
}
|
||||
$sAllowedValues = implode(', ', $aDisplayedValues);
|
||||
return $sAllowedValues;
|
||||
}
|
||||
|
||||
|
||||
public function GetValues($aArgs, $sContains = '')
|
||||
{
|
||||
if (!$this->m_bIsLoaded)
|
||||
{
|
||||
$this->LoadValues($aArgs);
|
||||
$this->m_bIsLoaded = true;
|
||||
}
|
||||
if (strlen($sContains) == 0)
|
||||
{
|
||||
$aRet = $this->m_aValues;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRet = array();
|
||||
foreach ($this->m_aValues as $sKey=>$sValue)
|
||||
{
|
||||
if (stripos($sValue, $sContains) !== false)
|
||||
{
|
||||
$aRet[$sKey] = $sValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
asort($aRet);
|
||||
return $aRet;
|
||||
}
|
||||
|
||||
abstract protected function LoadValues($aArgs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set of existing values for an attribute, given a search filter
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class ValueSetObjects extends ValueSetDefinition
|
||||
{
|
||||
protected $m_sFilterExpr; // in OQL
|
||||
protected $m_sValueAttCode;
|
||||
protected $m_aOrderBy;
|
||||
private $m_bAllowAllData;
|
||||
|
||||
public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false)
|
||||
{
|
||||
$this->m_sFilterExpr = $sFilterExp;
|
||||
$this->m_sValueAttCode = $sValueAttCode;
|
||||
$this->m_aOrderBy = $aOrderBy;
|
||||
$this->m_bAllowAllData = $bAllowAllData;
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs)
|
||||
{
|
||||
$this->m_aValues = array();
|
||||
|
||||
if ($this->m_bAllowAllData)
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL_AllData($this->m_sFilterExpr);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($this->m_sFilterExpr);
|
||||
}
|
||||
if (!$oFilter) return false;
|
||||
|
||||
$oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs);
|
||||
while ($oObject = $oObjects->Fetch())
|
||||
{
|
||||
if (empty($this->m_sValueAttCode))
|
||||
{
|
||||
$this->m_aValues[$oObject->GetKey()] = $oObject->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aValues[$oObject->GetKey()] = $oObject->Get($this->m_sValueAttCode);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetValuesDescription()
|
||||
{
|
||||
return 'Filter: '.$this->m_sFilterExpr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set of existing values for a link set attribute, given a relation code
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class ValueSetRelatedObjectsFromLinkSet extends ValueSetDefinition
|
||||
{
|
||||
protected $m_sLinkSetAttCode;
|
||||
protected $m_sExtKeyToRemote;
|
||||
protected $m_sRelationCode;
|
||||
protected $m_iMaxDepth;
|
||||
protected $m_sTargetClass;
|
||||
protected $m_sTargetExtKey;
|
||||
// protected $m_aOrderBy;
|
||||
|
||||
public function __construct($sLinkSetAttCode, $sExtKeyToRemote, $sRelationCode, $iMaxDepth, $sTargetClass, $sTargetLinkClass, $sTargetExtKey)
|
||||
{
|
||||
$this->m_sLinkSetAttCode = $sLinkSetAttCode;
|
||||
$this->m_sExtKeyToRemote = $sExtKeyToRemote;
|
||||
$this->m_sRelationCode = $sRelationCode;
|
||||
$this->m_iMaxDepth = $iMaxDepth;
|
||||
$this->m_sTargetClass = $sTargetClass;
|
||||
$this->m_sTargetLinkClass = $sTargetLinkClass;
|
||||
$this->m_sTargetExtKey = $sTargetExtKey;
|
||||
// $this->m_aOrderBy = $aOrderBy;
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs)
|
||||
{
|
||||
$this->m_aValues = array();
|
||||
|
||||
if (!array_key_exists('this', $aArgs))
|
||||
{
|
||||
throw new CoreException("Missing 'this' in arguments", array('args' => $aArgs));
|
||||
}
|
||||
|
||||
$oTarget = $aArgs['this->object()'];
|
||||
|
||||
// Nodes from which we will start the search for neighbourhood
|
||||
$oNodes = DBObjectSet::FromLinkSet($oTarget, $this->m_sLinkSetAttCode, $this->m_sExtKeyToRemote);
|
||||
|
||||
// Neighbours, whatever their class
|
||||
$aRelated = $oNodes->GetRelatedObjects($this->m_sRelationCode, $this->m_iMaxDepth);
|
||||
|
||||
$sRootClass = MetaModel::GetRootClass($this->m_sTargetClass);
|
||||
if (array_key_exists($sRootClass, $aRelated))
|
||||
{
|
||||
$aLinksToCreate = array();
|
||||
foreach($aRelated[$sRootClass] as $iKey => $oObject)
|
||||
{
|
||||
if (MetaModel::IsParentClass($this->m_sTargetClass, get_class($oObject)))
|
||||
{
|
||||
$oNewLink = MetaModel::NewObject($this->m_sTargetLinkClass);
|
||||
$oNewLink->Set($this->m_sTargetExtKey, $iKey);
|
||||
//$oNewLink->Set('role', 'concerned by an impacted CI');
|
||||
|
||||
$aLinksToCreate[] = $oNewLink;
|
||||
}
|
||||
}
|
||||
// #@# or AddObjectArray($aObjects) ?
|
||||
$oSetToCreate = DBObjectSet::FromArray($this->m_sTargetLinkClass, $aLinksToCreate);
|
||||
$this->m_aValues[$oObject->GetKey()] = $oObject->GetAsHTML($oObject->GetName());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function GetValuesDescription()
|
||||
{
|
||||
return 'Filter: '.$this->m_sFilterExpr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fixed set values (could be hardcoded in the business model)
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class ValueSetEnum extends ValueSetDefinition
|
||||
{
|
||||
protected $m_values;
|
||||
|
||||
public function __construct($Values)
|
||||
{
|
||||
$this->m_values = $Values;
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs)
|
||||
{
|
||||
if (is_array($this->m_values))
|
||||
{
|
||||
$aValues = $this->m_values;
|
||||
}
|
||||
elseif (is_string($this->m_values) && strlen($this->m_values) > 0)
|
||||
{
|
||||
$aValues = array();
|
||||
foreach (explode(",", $this->m_values) as $sVal)
|
||||
{
|
||||
$sVal = trim($sVal);
|
||||
$sKey = $sVal;
|
||||
$aValues[$sKey] = $sVal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aValues = array();
|
||||
}
|
||||
$this->m_aValues = $aValues;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Data model classes
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class ValueSetEnumClasses extends ValueSetEnum
|
||||
{
|
||||
protected $m_sCategories;
|
||||
|
||||
public function __construct($sCategories = '', $sAdditionalValues = '')
|
||||
{
|
||||
$this->m_sCategories = $sCategories;
|
||||
parent::__construct($sAdditionalValues);
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs)
|
||||
{
|
||||
// First, get the additional values
|
||||
parent::LoadValues($aArgs);
|
||||
|
||||
// Then, add the classes from the category definition
|
||||
foreach (MetaModel::GetClasses($this->m_sCategories) as $sClass)
|
||||
{
|
||||
$this->m_aValues[$sClass] = MetaModel::GetName($sClass);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
222
css/blue_green.css
Normal file
@@ -0,0 +1,222 @@
|
||||
/* CSS Document */
|
||||
body {
|
||||
font-family: Verdana, Arial, Helevtica;
|
||||
font-size: smaller;
|
||||
background-color: #68a;
|
||||
color:#000000;
|
||||
margin: 0; /* Remove body margin/padding */
|
||||
padding: 0;
|
||||
overflow: hidden; /* Remove scroll bars on browser window */
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid #000000;
|
||||
}
|
||||
|
||||
.raw_output {
|
||||
font-family: Courier-New, Courier, Arial, Helevtica;
|
||||
font-size: smaller;
|
||||
background-color: #eeeeee;
|
||||
color: #000000;
|
||||
border: 1px dashed #000000;
|
||||
padding: 0.25em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
th {
|
||||
font-family: Verdana, Arial, Helvetica;
|
||||
font-size: smaller;
|
||||
color: #000000;
|
||||
background-color:#ace27d;
|
||||
}
|
||||
|
||||
td {
|
||||
font-family: Verdana, Arial, Helvetica;
|
||||
font-size: smaller;
|
||||
background-color: #b7cfe8;
|
||||
}
|
||||
|
||||
tr.clicked td {
|
||||
font-family: Verdana, Arial, Helvetica;
|
||||
font-size: smaller;
|
||||
background-color: #ffcfe8;
|
||||
}
|
||||
|
||||
td.label {
|
||||
font-family: Verdana, Arial, Helvetica;
|
||||
font-size: smaller;
|
||||
color: #000000;
|
||||
background-color:#ace27d;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
td a, td a:visited {
|
||||
text-decoration:none;
|
||||
color:#000000;
|
||||
}
|
||||
td a:hover {
|
||||
text-decoration:underline;
|
||||
color:#FFFFFF;
|
||||
}
|
||||
|
||||
a.small_action {
|
||||
font-family: Verdana, Arial, Helvetica;
|
||||
font-size: smaller;
|
||||
color: #000000;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.display_block {
|
||||
noborder: 1px dashed #CCC;
|
||||
background: #79b;
|
||||
padding:0.25em;
|
||||
}
|
||||
div#TopPane .display_block {
|
||||
background: #f0eee0;
|
||||
padding:0.25em;
|
||||
text-align:center;
|
||||
}
|
||||
div#TopPane label {
|
||||
color:#000;
|
||||
background: #f0eee0;
|
||||
}
|
||||
|
||||
div#TopPane td {
|
||||
color:#000;
|
||||
background: #f0eee0;
|
||||
}
|
||||
|
||||
.loading {
|
||||
noborder: 1px dashed #CCC;
|
||||
background: #b9c1c8;
|
||||
padding:0.25em;
|
||||
}
|
||||
|
||||
label {
|
||||
font-family:Georgia, "Times New Roman", Times, serif;
|
||||
color:#FFFFFF;
|
||||
text-align:right;
|
||||
}
|
||||
|
||||
input.textSearch {
|
||||
border:1px solid #333;
|
||||
noheight:1.2em;
|
||||
font-size:0.8em;
|
||||
font-family:Verdana, Arial, Helvetica, sans-serif;
|
||||
color:#000000;
|
||||
}
|
||||
|
||||
/* By Rom */
|
||||
.csvimport_createobj {
|
||||
color: #AA0000;
|
||||
background-color:#EEEEEE;
|
||||
}
|
||||
.csvimport_error {
|
||||
font-weight: bold;
|
||||
color: #FF0000;
|
||||
background-color:#EEEEEE;
|
||||
}
|
||||
.csvimport_warning {
|
||||
color: #CC8888;
|
||||
background-color:#EEEEEE;
|
||||
}
|
||||
.csvimport_ok {
|
||||
color: #00000;
|
||||
background-color:#BBFFBB;
|
||||
}
|
||||
.csvimport_reconkey {
|
||||
font-style: italic;
|
||||
color: #888888;
|
||||
background-color:#FFFFF;
|
||||
}
|
||||
.csvimport_extreconkey {
|
||||
color: #888888;
|
||||
background-color:#FFFFFF;
|
||||
}
|
||||
|
||||
.treeview, .treeview ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.treeview li {
|
||||
margin: 0;
|
||||
padding: 3px 0pt 3px 16px;
|
||||
font-size:0.9em;
|
||||
}
|
||||
|
||||
ul.dir li {
|
||||
padding: 2px 0 0 16px;
|
||||
}
|
||||
|
||||
.treeview li { background: url(../images/tv-item.gif) 0 0 no-repeat; }
|
||||
.treeview .collapsable { background-image: url(../images/tv-collapsable.gif); }
|
||||
.treeview .expandable { background-image: url(../images/tv-expandable.gif); }
|
||||
.treeview .last { background-image: url(../images/tv-item-last.gif); }
|
||||
.treeview .lastCollapsable { background-image: url(../images/tv-collapsable-last.gif); }
|
||||
.treeview .lastExpandable { background-image: url(../images/tv-expandable-last.gif); }
|
||||
|
||||
#Header { padding: 0; background:#ccc url(../images/bandeau2.gif) repeat-x center;}
|
||||
div.iTopLogo {
|
||||
background:url(../images/iTop.gif) no-repeat center;
|
||||
width:100px;
|
||||
height:56px;
|
||||
}
|
||||
div.iTopLogo span {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#MySplitter {
|
||||
/* Height is set to match window size in $().ready() below */
|
||||
border:0px;
|
||||
margin:4px;
|
||||
padding:0px;
|
||||
min-width: 100px; /* Splitter can't be too thin ... */
|
||||
min-height: 100px; /* ... or too flat */
|
||||
}
|
||||
#LeftPane {
|
||||
background: #f0eee0;
|
||||
padding: 4px;
|
||||
overflow: auto; /* Scroll bars appear as needed */
|
||||
color:#666;
|
||||
}
|
||||
#TopPane { /* Top nested in right pane */
|
||||
background: #f0eee0;
|
||||
padding: 4px;
|
||||
height: 150px; /* Initial height */
|
||||
min-height: 75px; /* Minimum height */
|
||||
overflow: auto;
|
||||
color:#666;
|
||||
}
|
||||
#RightPane { /* Bottom nested in right pane */
|
||||
background: #79b;
|
||||
height:150px; /* Initial height */
|
||||
min-height:130px;
|
||||
no.padding:15px;
|
||||
no.margin:10px;
|
||||
overflow:auto;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
#BottomPane { /* Bottom nested in right pane */
|
||||
background: #79b;
|
||||
padding: 4px;
|
||||
overflow: auto;
|
||||
color:#fff;
|
||||
}
|
||||
#MySplitter .vsplitbar {
|
||||
width: 7px;
|
||||
height: 50px;
|
||||
background: #68a url(../images/vgrabber2.gif) no-repeat center;
|
||||
}
|
||||
#MySplitter .vsplitbar.active, #MySplitter .vsplitbar:hover {
|
||||
background: #68a url(../images/vgrabber2_active.gif) no-repeat center;
|
||||
}
|
||||
#MySplitter .hsplitbar {
|
||||
height: 8px;
|
||||
background: #68a url(../images/hgrabber2.gif) no-repeat center;
|
||||
}
|
||||
#MySplitter .hsplitbar.active, #MySplitter .hsplitbar:hover {
|
||||
background: #68a url(../images/hgrabber2_active.gif) no-repeat center;
|
||||
}
|
||||
117
css/date.picker.css
Normal file
@@ -0,0 +1,117 @@
|
||||
|
||||
|
||||
table.jCalendar {
|
||||
border: 1px solid #000;
|
||||
background: #aaa;
|
||||
border-collapse: separate;
|
||||
border-spacing: 2px;
|
||||
}
|
||||
table.jCalendar th {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
table.jCalendar td {
|
||||
background: #ccc;
|
||||
color: #000;
|
||||
padding: 3px 5px;
|
||||
text-align: center;
|
||||
}
|
||||
table.jCalendar td.other-month {
|
||||
background: #ddd;
|
||||
color: #aaa;
|
||||
}
|
||||
table.jCalendar td.today {
|
||||
background: #666;
|
||||
color: #fff;
|
||||
}
|
||||
table.jCalendar td.selected {
|
||||
background: #f66;
|
||||
color: #fff;
|
||||
}
|
||||
table.jCalendar td.selected:hover {
|
||||
background: #f33;
|
||||
color: #fff;
|
||||
}
|
||||
table.jCalendar td:hover, table.jCalendar td.dp-hover {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
}
|
||||
table.jCalendar td.disabled, table.jCalendar td.disabled:hover {
|
||||
background: #bbb;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
/* For the popup */
|
||||
|
||||
/* NOTE - you will probably want to style a.dp-choose-date - see how I did it in demo.css */
|
||||
|
||||
div.dp-popup {
|
||||
position: relative;
|
||||
background: #ccc;
|
||||
font-size: 10px;
|
||||
font-family: arial, sans-serif;
|
||||
padding: 2px;
|
||||
width: 171px;
|
||||
line-height: 1.2em;
|
||||
}
|
||||
div#dp-popup {
|
||||
position: absolute;
|
||||
z-index: 199;
|
||||
}
|
||||
div.dp-popup h2 {
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
margin: 2px 0;
|
||||
padding: 0;
|
||||
}
|
||||
a#dp-close {
|
||||
font-size: 11px;
|
||||
padding: 4px 0;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
a#dp-close:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
div.dp-popup a {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
padding: 3px 2px 0;
|
||||
}
|
||||
div.dp-popup div.dp-nav-prev {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 4px;
|
||||
width: 100px;
|
||||
}
|
||||
div.dp-popup div.dp-nav-prev a {
|
||||
float: left;
|
||||
}
|
||||
/* Opera needs the rules to be this specific otherwise it doesn't change the cursor back to pointer after you have disabled and re-enabled a link */
|
||||
div.dp-popup div.dp-nav-prev a, div.dp-popup div.dp-nav-next a {
|
||||
cursor: pointer;
|
||||
}
|
||||
div.dp-popup div.dp-nav-prev a.disabled, div.dp-popup div.dp-nav-next a.disabled {
|
||||
cursor: default;
|
||||
}
|
||||
div.dp-popup div.dp-nav-next {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 4px;
|
||||
width: 100px;
|
||||
}
|
||||
div.dp-popup div.dp-nav-next a {
|
||||
float: right;
|
||||
}
|
||||
div.dp-popup a.disabled {
|
||||
cursor: default;
|
||||
color: #aaa;
|
||||
}
|
||||
div.dp-popup td {
|
||||
cursor: pointer;
|
||||
}
|
||||
div.dp-popup td.disabled {
|
||||
cursor: default;
|
||||
}
|
||||
165
css/default.css
Normal file
@@ -0,0 +1,165 @@
|
||||
/* CSS Document */
|
||||
body {
|
||||
font-family: Verdana, Arial, Helevtica;
|
||||
font-size: smaller;
|
||||
background-color: #ffffff;
|
||||
color:#000000;
|
||||
margin: 0; /* Remove body margin/padding */
|
||||
padding: 0;
|
||||
overflow: hidden; /* Remove scroll bars on browser window */
|
||||
}
|
||||
|
||||
table {
|
||||
border: 1px solid #000000;
|
||||
}
|
||||
|
||||
.raw_output {
|
||||
font-family: Courier-New, Courier, Arial, Helevtica;
|
||||
font-size: smaller;
|
||||
background-color: #eeeeee;
|
||||
color: #000000;
|
||||
border: 1px dashed #000000;
|
||||
padding: 0.25em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
th {
|
||||
font-family: Verdana, Arial, Helevtica;
|
||||
font-size: smaller;
|
||||
color: #000000;
|
||||
background-color:#E1DEB5;
|
||||
}
|
||||
|
||||
td {
|
||||
font-family: Verdana, Arial, Helevtica;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
td.label {
|
||||
font-family: Verdana, Arial, Helevtica;
|
||||
font-size: smaller;
|
||||
color: #000000;
|
||||
background-color:#E1DEB5;
|
||||
padding: 0.2em;
|
||||
}
|
||||
|
||||
a.small_action {
|
||||
font-family: Verdana, Arial, Helvetica;
|
||||
font-size: smaller;
|
||||
color: #000000;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.display_block {
|
||||
border: 1px dashed #CCC;
|
||||
background: #CFC;
|
||||
padding:0.25em;
|
||||
}
|
||||
|
||||
.loading {
|
||||
border: 1px dashed #CCC;
|
||||
background: #FCC;
|
||||
padding:0.25em;
|
||||
}
|
||||
|
||||
/* By Rom */
|
||||
.csvimport_createobj {
|
||||
color: #AA0000;
|
||||
background-color:#EEEEEE;
|
||||
}
|
||||
.csvimport_error {
|
||||
font-weight: bold;
|
||||
color: #FF0000;
|
||||
background-color:#EEEEEE;
|
||||
}
|
||||
.csvimport_warning {
|
||||
color: #CC8888;
|
||||
background-color:#EEEEEE;
|
||||
}
|
||||
.csvimport_ok {
|
||||
color: #00000;
|
||||
background-color:#BBFFBB;
|
||||
}
|
||||
.csvimport_reconkey {
|
||||
font-style: italic;
|
||||
color: #888888;
|
||||
background-color:#FFFFF;
|
||||
}
|
||||
.csvimport_extreconkey {
|
||||
color: #888888;
|
||||
background-color:#FFFFFF;
|
||||
}
|
||||
|
||||
.treeview, .treeview ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.treeview li {
|
||||
margin: 0;
|
||||
padding: 3px 0pt 3px 16px;
|
||||
}
|
||||
|
||||
ul.dir li { padding: 2px 0 0 16px; }
|
||||
|
||||
.treeview li { background: url(../images/tv-item.gif) 0 0 no-repeat; }
|
||||
.treeview .collapsable { background-image: url(../images/tv-collapsable.gif); }
|
||||
.treeview .expandable { background-image: url(../images/tv-expandable.gif); }
|
||||
.treeview .last { background-image: url(../images/tv-item-last.gif); }
|
||||
.treeview .lastCollapsable { background-image: url(../images/tv-collapsable-last.gif); }
|
||||
.treeview .lastExpandable { background-image: url(../images/tv-expandable-last.gif); }
|
||||
|
||||
#MySplitter {
|
||||
/* Height is set to match window size in $().ready() below */
|
||||
border:0px;
|
||||
margin:4px;
|
||||
padding:0px;
|
||||
min-width: 100px; /* Splitter can't be too thin ... */
|
||||
min-height: 100px; /* ... or too flat */
|
||||
}
|
||||
#LeftPane {
|
||||
background: #f0eee0;
|
||||
padding: 4px;
|
||||
overflow: auto; /* Scroll bars appear as needed */
|
||||
color:#666;
|
||||
}
|
||||
#TopPane { /* Top nested in right pane */
|
||||
background: #f0eee0;
|
||||
padding: 4px;
|
||||
height: 150px; /* Initial height */
|
||||
min-height: 75px; /* Minimum height */
|
||||
overflow: auto;
|
||||
color:#666;
|
||||
}
|
||||
#RightPane { /* Bottom nested in right pane */
|
||||
background: #79b;
|
||||
height:150px; /* Initial height */
|
||||
min-height:130px;
|
||||
no.padding:15px;
|
||||
no.margin:10px;
|
||||
overflow:auto;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
#BottomPane { /* Bottom nested in right pane */
|
||||
background: #79b;
|
||||
padding: 4px;
|
||||
overflow: auto;
|
||||
color:#fff;
|
||||
}
|
||||
#MySplitter .vsplitbar {
|
||||
width: 7px;
|
||||
height: 50px;
|
||||
background: #68a url(../images/vgrabber2.gif) no-repeat center;
|
||||
}
|
||||
#MySplitter .vsplitbar.active, #MySplitter .vsplitbar:hover {
|
||||
background: #68a url(../images/vgrabber2_active.gif) no-repeat center;
|
||||
}
|
||||
#MySplitter .hsplitbar {
|
||||
height: 8px;
|
||||
background: #68a url(../images/hgrabber2.gif) no-repeat center;
|
||||
}
|
||||
#MySplitter .hsplitbar.active, #MySplitter .hsplitbar:hover {
|
||||
background: #68a url(../images/hgrabber2_active.gif) no-repeat center;
|
||||
}
|
||||
39
css/jqModal.css
Normal file
@@ -0,0 +1,39 @@
|
||||
/* jqModal base Styling courtesy of;
|
||||
Brice Burgess <bhb@iceburg.net> */
|
||||
|
||||
/* The Window's CSS z-index value is respected (takes priority). If none is supplied,
|
||||
the Window's z-index value will be set to 3000 by default (via jqModal.js). */
|
||||
|
||||
.jqmWindow {
|
||||
display: none;
|
||||
|
||||
position: fixed;
|
||||
no.top: 17%;
|
||||
no.left: 50%;
|
||||
|
||||
no.margin-left: -300px;
|
||||
no.width: 700px;
|
||||
|
||||
background-color: #EEE;
|
||||
color: #333;
|
||||
border: 1px solid black;
|
||||
padding: 12px;
|
||||
|
||||
z-index:9999;
|
||||
}
|
||||
|
||||
.jqmOverlay { background-color: #000; }
|
||||
|
||||
/* Background iframe styling for IE6. Prevents ActiveX bleed-through (<select> form elements, etc.) */
|
||||
* iframe.jqm {position:absolute;top:0;left:0;z-index:-1;
|
||||
width: expression(this.parentNode.offsetWidth+'px');
|
||||
height: expression(this.parentNode.offsetHeight+'px');
|
||||
}
|
||||
|
||||
/* Fixed posistioning emulation for IE6
|
||||
Star selector used to hide definition from browsers other than IE6
|
||||
For valid CSS, use a conditional include instead */
|
||||
* html .jqmWindow {
|
||||
position: absolute;
|
||||
top: expression((document.documentElement.scrollTop || document.body.scrollTop) + Math.round(17 * (document.documentElement.offsetHeight || document.body.clientHeight) / 100) + 'px');
|
||||
}
|
||||
48
css/jquery.autocomplete.css
Normal file
@@ -0,0 +1,48 @@
|
||||
.ac_results {
|
||||
padding: 0px;
|
||||
border: 1px solid black;
|
||||
background-color: white;
|
||||
overflow: hidden;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.ac_results ul {
|
||||
width: 100%;
|
||||
list-style-position: outside;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ac_results li {
|
||||
margin: 0px;
|
||||
padding: 2px 5px;
|
||||
cursor: default;
|
||||
display: block;
|
||||
/*
|
||||
if width will be 100% horizontal scrollbar will apear
|
||||
when scroll mode will be used
|
||||
*/
|
||||
/*width: 100%;*/
|
||||
font: menu;
|
||||
font-size: 12px;
|
||||
/*
|
||||
it is very important, if line-height not setted or setted
|
||||
in relative units scroll will be broken in firefox
|
||||
*/
|
||||
line-height: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ac_loading {
|
||||
background: white url('../images/indicator.gif') right center no-repeat;
|
||||
}
|
||||
|
||||
.ac_odd {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.ac_over {
|
||||
background-color: #0A246A;
|
||||
color: white;
|
||||
}
|
||||
15
css/jquery.tabs-ie.css
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
Tabs - additional IE specific bug fixes
|
||||
|
||||
Recommended usage (Conditional Comments):
|
||||
<!--[if lte IE 7]>
|
||||
<link rel="stylesheet" href="tabs_ie.css" type="text/css" media="projection, screen" />
|
||||
<![endif]-->
|
||||
|
||||
*/
|
||||
.tabs-nav { /* auto clear */
|
||||
display: inline-block;
|
||||
}
|
||||
.tabs-nav .tabs-disabled a {
|
||||
filter: alpha(opacity=40);
|
||||
}
|
||||
76
css/jquery.tabs.css
Normal file
@@ -0,0 +1,76 @@
|
||||
/* Caution! Ensure accessibility in print and other media types... */
|
||||
@media projection, screen { /* Use class for showing/hiding tab content, so that visibility can be better controlled in different media types... */
|
||||
.tabs-hide {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hide useless elements in print layouts... */
|
||||
@media print {
|
||||
.tabs-nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Skin */
|
||||
.tabs-nav {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0 0 0 4px;
|
||||
}
|
||||
.tabs-nav:after { /* clearing without presentational markup, IE gets extra treatment */
|
||||
display: block;
|
||||
clear: both;
|
||||
content: " ";
|
||||
}
|
||||
.tabs-nav li {
|
||||
float: left;
|
||||
margin: 0 0 0 1px;
|
||||
}
|
||||
.tabs-nav a {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
z-index: 2;
|
||||
padding: 6px 10px 0;
|
||||
width: 64px;
|
||||
height: 18px;
|
||||
color: #27537a;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
background: url(../images/tab.png) no-repeat;
|
||||
}
|
||||
.tabs-nav .tabs-selected a {
|
||||
padding-top: 7px;
|
||||
color: #000;
|
||||
}
|
||||
.tabs-nav .tabs-selected a, .tabs-nav a:hover, .tabs-nav a:focus, .tabs-nav a:active {
|
||||
background-position: 0 -50px;
|
||||
outline: 0; /* @ Firefox, switch off dotted border */
|
||||
}
|
||||
.tabs-nav .tabs-disabled a:hover, .tabs-nav .tabs-disabled a:focus, .tabs-nav .tabs-disabled a:active {
|
||||
background-position: 0 0;
|
||||
}
|
||||
.tabs-nav .tabs-selected a:link, .tabs-nav .tabs-selected a:visited,
|
||||
.tabs-nav .tabs-disabled a:link, .tabs-nav .tabs-disabled a:visited { /* @ Opera, use pseudo classes otherwise it confuses cursor... */
|
||||
cursor: text;
|
||||
}
|
||||
.tabs-nav a:hover, .tabs-nav a:focus, .tabs-nav a:active { /* @ Opera, we need to be explicit again here now... */
|
||||
cursor: pointer;
|
||||
}
|
||||
.tabs-nav .tabs-disabled {
|
||||
opacity: .4;
|
||||
}
|
||||
.tabs-container {
|
||||
border-top: 1px solid #97a5b0;
|
||||
padding: 1em 8px;
|
||||
background: #fff; /* declare background color for container to avoid distorted fonts in IE while fading */
|
||||
}
|
||||
/* Uncomment this if you want a little spinner to be shown next to the tab title while an Ajax tab gets loaded
|
||||
.tabs-loading span {
|
||||
padding: 0 0 0 20px;
|
||||
background: url(loading.gif) no-repeat 0 50%;
|
||||
}*/
|
||||
47
css/jquery.treeview.css
Normal file
@@ -0,0 +1,47 @@
|
||||
.treeview ul { background-color: white; }
|
||||
|
||||
.treeview, .treeview ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.treeview div.hitarea {
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
margin-left: -15px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
/* fix for IE6 */
|
||||
* html div.hitarea {
|
||||
background: #fff;
|
||||
filter: alpha(opacity=0);
|
||||
display: inline;
|
||||
float:none;
|
||||
}
|
||||
|
||||
.treeview li {
|
||||
margin: 0;
|
||||
padding: 3px 0pt 3px 16px;
|
||||
}
|
||||
|
||||
.treeview a.selected {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
#treecontrol { margin: 1em 0; }
|
||||
|
||||
.treeview .hover { color: red; cursor: pointer; }
|
||||
|
||||
.treeview li { background: url(../images/tv-item.gif) 0 0 no-repeat; }
|
||||
.treeview .collapsable { background-image: url(../images/tv-collapsable.gif); }
|
||||
.treeview .expandable { background-image: url(../images/tv-expandable.gif); }
|
||||
.treeview .last { background-image: url(../images/tv-item-last.gif); }
|
||||
.treeview .lastCollapsable { background-image: url(../images/tv-collapsable-last.gif); }
|
||||
.treeview .lastExpandable { background-image: url(../images/tv-expandable-last.gif); }
|
||||
|
||||
.filetree li { padding: 3px 0 1px 16px; }
|
||||
.filetree span.folder, .filetree span.file { padding-left: 16px; display: block; height: 15px; }
|
||||
.filetree span.folder { background: url(../images/folder.gif) 0 0 no-repeat; }
|
||||
.filetree span.file { background: url(../images/file.gif) 0 0 no-repeat; }
|
||||
919
css/light-grey.css
Normal file
@@ -0,0 +1,919 @@
|
||||
/* CSS Document */
|
||||
body {
|
||||
font-family: Tahoma, Verdana, Arial, Helevtica;
|
||||
font-size: 10pt;
|
||||
background-color: #fff;
|
||||
color:#000000;
|
||||
margin: 0; /* Remove body margin/padding */
|
||||
padding: 0;
|
||||
overflow: hidden; /* Remove scroll bars on browser window */
|
||||
}
|
||||
|
||||
.raw_output {
|
||||
font-family: Courier-New, Courier, Arial, Helevtica;
|
||||
font-size: 8pt;
|
||||
background-color: #eeeeee;
|
||||
color: #000000;
|
||||
border: 1px dashed #000000;
|
||||
padding: 0.25em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
font-size: 12pt;
|
||||
}
|
||||
h2 {
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
color: #000;
|
||||
font-weight: normal;
|
||||
font-size: 12pt;
|
||||
}
|
||||
h3 {
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
color: #000;
|
||||
font-weight: normal;
|
||||
font-size: 10pt;
|
||||
}
|
||||
.hilite, .hilite a, .hilite a:visited {
|
||||
color: #EB8F00;
|
||||
text-decoration: none;
|
||||
}
|
||||
table.listResults {
|
||||
padding: 0px;
|
||||
border-top: 3px solid #f6f6f1;
|
||||
border-left: 3px solid #f6f6f1;
|
||||
border-bottom: 3px solid #e6e6e1;
|
||||
border-right: 3px solid #e6e6e1;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
table.listResults td {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
table.listContainer {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin:0;
|
||||
width: 97%;
|
||||
}
|
||||
|
||||
tr.containerHeader, tr.containerHeader td {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
tr.even td, .wizContainer tr.even td {
|
||||
background-color: #f9f9f1;
|
||||
}
|
||||
tr.red_even td, .wizContainer tr.red_even td {
|
||||
background-color: #f97e75;
|
||||
color: #ffffff;
|
||||
}
|
||||
tr.red td, .wizContainer tr.red td {
|
||||
background-color: #f9a397;
|
||||
color: #ffffff;
|
||||
}
|
||||
tr.orange_even td, .wizContainer tr.orange_even td {
|
||||
background-color: #f4d07a;
|
||||
}
|
||||
tr.orange td, .wizContainer tr.orange td {
|
||||
background-color: #f4e96c;
|
||||
}
|
||||
tr.green_even td, .wizContainer tr.green_even td {
|
||||
background-color: #bee5a3;
|
||||
}
|
||||
tr.green td, .wizContainer tr.green td {
|
||||
background-color: #b3e5b4;
|
||||
}
|
||||
|
||||
tr td.hover, tr.even td.hover, .hover a, .hover a:visited, .hover a:hover, .wizContainer tr.even td.hover, .wizContainer tr td.hover {
|
||||
background-color: #fdf5d0;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
th {
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
font-size: 8pt;
|
||||
color: #1C94C4;
|
||||
height:20px;
|
||||
background: #f6f6f1 url(../images/grey-header.gif) bottom repeat-x;
|
||||
}
|
||||
|
||||
th.header {
|
||||
cursor: pointer;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center right;
|
||||
background-image: url(../images/bg.gif);
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 16px; // some space for the asc/desc icons
|
||||
}
|
||||
|
||||
th.headerSortUp {
|
||||
background-image: url(../images/asc.gif);
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
th.headerSortDown {
|
||||
background-image: url(../images/desc.gif);
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
td {
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
font-size: 8pt;
|
||||
color:#696969;
|
||||
background-color: #ffffff;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
tr.clicked td {
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
font-size: smaller;
|
||||
background-color: #ffcfe8;
|
||||
}
|
||||
|
||||
td.label {
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
font-size: 12px;
|
||||
color: #000000;
|
||||
background-color:#f6f6f6;
|
||||
padding: 0.25em;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.ui-widget-content td a, p a, p a:visited, td a, td a:visited {
|
||||
text-decoration:none;
|
||||
color: #1C94C4;
|
||||
padding-left:14px;
|
||||
background: url(../images/mini-arrow-orange.gif) no-repeat left;
|
||||
}
|
||||
p a:hover, td a:hover {
|
||||
text-decoration:underline;
|
||||
color:#EB8F00;
|
||||
padding-left:14px;
|
||||
background: url(../images/mini-arrow-orange.gif) no-repeat left;
|
||||
}
|
||||
|
||||
td a.no-arrow, td a.no-arrow:visited {
|
||||
text-decoration:none;
|
||||
color:#000000;
|
||||
padding-left:0px;
|
||||
background: inherit;
|
||||
}
|
||||
td a.no-arrow:hover {
|
||||
text-decoration:underline;
|
||||
color:#d81515;
|
||||
padding-left:0px;
|
||||
background: inherit;
|
||||
}
|
||||
td a.mailto, td a.mailto:visited {
|
||||
text-decoration:none;
|
||||
color:#000000;
|
||||
padding-left:20px;
|
||||
background: url(../images/mail.png) no-repeat left;
|
||||
}
|
||||
td a.mailto:hover {
|
||||
text-decoration:underline;
|
||||
color:#EB8F00;
|
||||
padding-left:20px;
|
||||
background: url(../images/mail.png) no-repeat left;
|
||||
}
|
||||
|
||||
a.small_action {
|
||||
font-family: Tahoma, Verdana, Arial, Helvetica;
|
||||
font-size: 8pt;
|
||||
color: #000000;
|
||||
text-decoration:none;
|
||||
}
|
||||
.display_block {
|
||||
padding:0.25em;
|
||||
}
|
||||
.actions_details {
|
||||
float:right;
|
||||
margin-top:10px;
|
||||
margin-right:10px;
|
||||
padding-left: 5px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
background: url(../images/actions_left.png) no-repeat left;
|
||||
}
|
||||
.actions_details span{
|
||||
background: url(../images/actions_right.png) no-repeat right;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.actions_details a {
|
||||
text-decoration:none;
|
||||
}
|
||||
.loading {
|
||||
noborder: 1px dashed #CCC;
|
||||
background: #b9c1c8;
|
||||
padding:0.25em;
|
||||
}
|
||||
|
||||
input.textSearch {
|
||||
border:1px solid #000;
|
||||
font-family:Tahoma,Verdana, Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
color:#000000;
|
||||
}
|
||||
|
||||
.ac_input {
|
||||
border: 1px solid #7f9db9;
|
||||
background: #fff url(../images/ac-background.gif) no-repeat right;
|
||||
}
|
||||
|
||||
/* By Rom */
|
||||
.csvimport_createobj {
|
||||
color: #AA0000;
|
||||
background-color:#EEEEEE;
|
||||
}
|
||||
.csvimport_error {
|
||||
font-weight: bold;
|
||||
color: #FF0000;
|
||||
background-color:#EEEEEE;
|
||||
}
|
||||
.csvimport_warning {
|
||||
color: #CC8888;
|
||||
background-color:#EEEEEE;
|
||||
}
|
||||
.csvimport_ok {
|
||||
color: #00000;
|
||||
background-color:#BBFFBB;
|
||||
}
|
||||
.csvimport_reconkey {
|
||||
font-style: italic;
|
||||
color: #888888;
|
||||
background-color:#FFFFF;
|
||||
}
|
||||
.csvimport_extreconkey {
|
||||
color: #888888;
|
||||
background-color:#FFFFFF;
|
||||
}
|
||||
|
||||
#accordion h3 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.ui-accordion-content ul {
|
||||
list-style:none;
|
||||
padding-left:16px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.ui-accordion-content li.submenu {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.ui-accordion-content ul ul {
|
||||
padding: 8px 0px 8px 8px;
|
||||
margin:0;
|
||||
list-style:none;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.nothing {
|
||||
noborder-top: 1px solid #8b8b8b;
|
||||
padding: 4px 0px 0px 16px;
|
||||
font-size:8pt;
|
||||
background: url(../images/green-square.gif) no-repeat bottom left;
|
||||
color:#83b217;
|
||||
font-weight:bold;
|
||||
text-decoration:none;
|
||||
}
|
||||
div.ui-accordion-content {
|
||||
padding-top: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.ui-accordion-content a, ui-accordion-content a:visited {
|
||||
color: #1C94C4;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.ui-accordion-content a:hover {
|
||||
color: #EB8F00;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-accordion-content p {
|
||||
color:#8b8b8b;
|
||||
text-decoration:none;
|
||||
margin: 0;
|
||||
padding-bottom: 1px;
|
||||
font-size: 9pt;
|
||||
font-weight:normal;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.ui-accordion-content ul {
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ui-accordion-content li {
|
||||
color:#8b8b8b;
|
||||
text-decoration:none;
|
||||
margin: 0;
|
||||
padding: 0px 0pt 0px 16px;
|
||||
font-size: 9pt;
|
||||
background: url(../images/mini-arrow-orange.gif) no-repeat top left;
|
||||
font-weight:normal;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
a.CollapsibleLabel, td a.CollapsibleLabel {
|
||||
margin: 0;
|
||||
padding: 0px 0pt 0px 16px;
|
||||
font-size:8pt;
|
||||
text-decoration:none;
|
||||
color:#8b8b8b;
|
||||
background: url(../images/mini-arrow-orange.gif) no-repeat left;
|
||||
}
|
||||
|
||||
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
|
||||
a.CollapsibleLabel.open, td a.CollapsibleLabel.open {
|
||||
margin: 0;
|
||||
padding: 0px 0pt 0px 16px;
|
||||
font-size:8pt;
|
||||
text-decoration:none;
|
||||
color: #EB8F00;
|
||||
background: url(../images/mini-arrow-orange-open.gif) no-repeat left;
|
||||
}
|
||||
|
||||
.page_header {
|
||||
background-color:#f6f6f1;
|
||||
padding:5px;
|
||||
}
|
||||
|
||||
.notreeview li { background: url(../images/tv-item.gif) 0 0 no-repeat; }
|
||||
.notreeview .collapsable { background-image: url(../images/tv-collapsable.gif); }
|
||||
.notreeview .expandable { background-image: url(../images/tv-expandable.gif); }
|
||||
.notreeview .last { background-image: url(../images/tv-item-last.gif); }
|
||||
.notreeview .lastCollapsable { background-image: url(../images/tv-collapsable-last.gif); }
|
||||
.notreeview .lastExpandable { background-image: url(../images/tv-expandable-last.gif); }
|
||||
|
||||
#OrganizationSelection {
|
||||
padding:5px 0px 16px 20px;
|
||||
}
|
||||
|
||||
/* popup menus */
|
||||
div.itop_popup {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
float:right;
|
||||
}
|
||||
|
||||
div.itop_popup > ul {
|
||||
height:19px;
|
||||
display:block;
|
||||
width:70px; /* Nasty work-around for IE... en attendant mieux */
|
||||
padding-left: 5px;
|
||||
background: url(../images/actions_left.png) no-repeat top left;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.itop_popup > ul > li {
|
||||
float: left;
|
||||
list-style: none;
|
||||
font-size: 11px;
|
||||
font-family: Tahoma,sans-serif;
|
||||
height: 19px;
|
||||
padding-right: 16px;
|
||||
padding-left: 4px;
|
||||
background: url(../images/actions_right.png) no-repeat top right transparent;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#logOffBtn > ul > li {
|
||||
list-style: none;
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#logOffBtn > ul {
|
||||
list-style: none;
|
||||
vertical-align: middle;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 25px;
|
||||
noline-height: 25px;
|
||||
}
|
||||
|
||||
.itop_popup li a, #logOffBtn li a {
|
||||
display: block;
|
||||
padding: 5px 12px;
|
||||
text-decoration: none;
|
||||
nowidth: 70px;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
#logOffBtn li span {
|
||||
display: block;
|
||||
padding: 5px 12px;
|
||||
text-decoration: none;
|
||||
nowidth: 70px;
|
||||
color: #000;
|
||||
white-space: nowrap;
|
||||
background: #fff;
|
||||
}
|
||||
.itop_popup li a:hover, #logOffBtn li a:hover {
|
||||
background: #1A4473;
|
||||
}
|
||||
|
||||
.itop_popup ul > li > ul, #logOffBtn ul > li > ul
|
||||
{
|
||||
border: 1px solid black;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.itop_popup li > ul, #logOffBtn li > ul
|
||||
{ margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
display: none;
|
||||
border-top: 1px solid white;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.itop_popup li ul li, #logOffBtn li ul li {
|
||||
float: none;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.itop_popup li ul li a, #logOffBtn li ul li a {
|
||||
width: auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.itop_popup li ul li a:hover, #logOffBtn li ul li a:hover{
|
||||
background: #D81515;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
hr.menu-separator {
|
||||
border: none 0;
|
||||
border-top: 1px solid #ccc;;
|
||||
color: #ccc;
|
||||
background-color: transparent;
|
||||
height: 1px;
|
||||
margin: 3px;
|
||||
cursor: default;
|
||||
}
|
||||
/************************************/
|
||||
.wizHeader {
|
||||
background: #1c94c4;
|
||||
padding: 15px;
|
||||
}
|
||||
.wizSeparator {
|
||||
float:left;
|
||||
background: #83b217;
|
||||
height: 29px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
div.wizActiveStep {
|
||||
height: 29px;
|
||||
background: url(../images/wizActiveStepLeft.gif) no-repeat left;
|
||||
color: #d81515;
|
||||
padding-left: 8px;
|
||||
margin-top: 5px;
|
||||
vertical-align: middle;
|
||||
float: left;
|
||||
}
|
||||
div.wizActiveStep span {
|
||||
height: 29px;
|
||||
background: url(../images/wizActiveStepRight.gif) no-repeat top right;
|
||||
padding-right: 8px;
|
||||
padding-top: 8px;
|
||||
float:left;
|
||||
}
|
||||
|
||||
div.wizStep {
|
||||
height: 29px;
|
||||
background: url(../images/wizStepLeft.gif) no-repeat left;
|
||||
padding-left: 8px;
|
||||
vertical-align: middle;
|
||||
margin-top: 5px;
|
||||
float:left;
|
||||
}
|
||||
div.wizStep span {
|
||||
height: 29px;
|
||||
background: url(../images/wizStepRight.gif) no-repeat top right;
|
||||
padding-right: 8px;
|
||||
padding-top: 8px;
|
||||
float:left;
|
||||
}
|
||||
.wizContainer {
|
||||
border: 5px solid #1c94c4;
|
||||
background: #d6e8ef;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.wizContainer table tr td {
|
||||
background: transparent;
|
||||
}
|
||||
.alignRight {
|
||||
text-align: right;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.alignLeft {
|
||||
text-align: left;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.red {
|
||||
background-color: #ff6000;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
th.red {
|
||||
background: url(../images/red-header.gif) bottom left repeat-x;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.green {
|
||||
background-color: #00cc00;
|
||||
color: #000;
|
||||
}
|
||||
th.green {
|
||||
background: url(../images/green-header.gif) bottom left repeat-x;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.orange {
|
||||
background-color: #ffde00;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
th.orange {
|
||||
background: url(../images/orange-header.gif) bottom left repeat-x;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* For Date Picker: Creates a little calendar icon
|
||||
* instead of a text link for "Choose date"
|
||||
*/
|
||||
td a.dp-choose-date, a.dp-choose-date, td a.dp-choose-date:hover, a.dp-choose-date:hover, td a.dp-choose-date:visited, a.dp-choose-date:visited {
|
||||
float: left;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
margin: 5px 3px 0;
|
||||
display: block;
|
||||
text-indent: -2000px;
|
||||
overflow: hidden;
|
||||
background: url(../images/calendar.png) no-repeat;
|
||||
}
|
||||
td a.dp-choose-date.dp-disabled, a.dp-choose-date.dp-disabled {
|
||||
background-position: 0 -20px;
|
||||
cursor: default;
|
||||
}
|
||||
/* For Date Picker: makes the input field shorter once the date picker code
|
||||
* has run (to allow space for the calendar icon)
|
||||
*/
|
||||
input.dp-applied {
|
||||
width: 140px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* For search forms */
|
||||
.SearchDrawer {
|
||||
//background: #1c94c4 url(../images/search-top-left-corner.png) top left no-repeat;
|
||||
border-top: 5px solid #1c94c4;
|
||||
border-left: 5px solid #1c94c4;
|
||||
border-right: 5px solid #1c94c4;
|
||||
border-bottom: 0;
|
||||
background: #d6e8ef;
|
||||
color: #000;
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
.SearchDrawer label {
|
||||
background: #d6e8ef;
|
||||
color: #000;
|
||||
}
|
||||
.SearchDrawer h1 {
|
||||
color: #000;
|
||||
}
|
||||
.DrawerClosed {
|
||||
display: none;
|
||||
}
|
||||
.DrawerHandle {
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
background: url(../images/drawer-handle.gif) bottom no-repeat transparent;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
/* center the block */
|
||||
width: 100px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
div.HRDrawer {
|
||||
height: 5px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
background-color: #1c94c4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
display: block;
|
||||
}
|
||||
.mini_tabs a {
|
||||
text-decoration: none;
|
||||
font-weight:bold;
|
||||
color: #ccc;
|
||||
background-color:#333;
|
||||
padding-left: 1em;
|
||||
padding-right: 1em;
|
||||
padding-bottom: 0.25em;
|
||||
}
|
||||
.mini_tabs a.selected {
|
||||
color: #fff;
|
||||
background-color: #1c94c4;
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
.mini_tabs ul {
|
||||
margin: -10px;
|
||||
}
|
||||
.mini_tabs ul li {
|
||||
float: right;
|
||||
list-style: none;
|
||||
nopadding-left: 1em;
|
||||
nopadding-right: 1em;
|
||||
margin-top: 0;
|
||||
}
|
||||
.mandatory {
|
||||
border: 1px solid #f00;
|
||||
}
|
||||
|
||||
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
|
||||
table.listResults tr.odd td.truncated, table.listResults tr td.truncated, .wizContainer table.listResults tr.odd td.truncated, .wizContainer table.listResults tr td.truncated {
|
||||
background: url(../images/truncated.png) bottom repeat-x;
|
||||
}
|
||||
|
||||
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
|
||||
table.listResults tr.even td.truncated, .wizContainer table.listResults tr.even td.truncated {
|
||||
background: #f9f9f1 url(../images/truncated.png) bottom repeat-x;
|
||||
}
|
||||
|
||||
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
|
||||
table.listResults tr.even td.hover.truncated, , .wizContainer table.listResults tr.even td.hover.truncated {
|
||||
background: #fdf5d0 url(../images/truncated.png) bottom repeat-x;
|
||||
}
|
||||
|
||||
/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */
|
||||
table.listResults tr.odd td.hover.truncated, table.listResults tr td.hover.truncated, .wizContainer table.listResults tr.odd td.hover.truncated, .wizContainer table.listResults tr td.hover.truncated {
|
||||
background: #fdf5d0 url(../images/truncated.png) bottom repeat-x;
|
||||
}
|
||||
|
||||
table.listResults.truncated {
|
||||
border-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
tr.csv_row0 td {
|
||||
padding-top:5px;
|
||||
padding-bottom:5px;
|
||||
padding-left:10px;
|
||||
padding-right:10px;
|
||||
background: #fff;
|
||||
border-left: #000 1px solid;
|
||||
|
||||
}
|
||||
tr.csv_row1 td {
|
||||
padding-top:5px;
|
||||
padding-bottom:5px;
|
||||
padding-left:10px;
|
||||
padding-right:10px;
|
||||
background: #f9f9f1;
|
||||
border-left: #000 1px solid;
|
||||
|
||||
}
|
||||
tr.csv_row1 th, tr.csv_row0 th {
|
||||
padding-top:5px;
|
||||
padding-bottom:5px;
|
||||
padding-left:10px;
|
||||
padding-right:10px;
|
||||
border-left: #000 1px solid;
|
||||
|
||||
}
|
||||
|
||||
td.cell_modified {
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
td.cell_error {
|
||||
font-weight: bold;
|
||||
color: #D81515;
|
||||
}
|
||||
table.transparent, table.transparent td {
|
||||
background: transparent;
|
||||
}
|
||||
p.page-header {
|
||||
color:#1c94c4;
|
||||
font-weight: bold;
|
||||
font-size: 12pt;
|
||||
font-family: Verdana, Arial, Helvetica, Sans-Serif
|
||||
}
|
||||
td.dashboard {
|
||||
vertical-align:top;
|
||||
text-align: center;
|
||||
border: 1px solid #ccc;
|
||||
-moz-border-radius: 10px;
|
||||
padding: 10px;
|
||||
width: 50%;
|
||||
}
|
||||
.white {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/*** New Lacanau layout ***/
|
||||
|
||||
.ui-layout-pane { /* all 'panes' */
|
||||
background: #FFF;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ui-layout-resizer { /* all 'resizer-bars' */
|
||||
background: #DDD;
|
||||
}
|
||||
.ui-layout-toggler { /* all 'toggler-buttons' */
|
||||
background: #AAA;
|
||||
}
|
||||
div#top-left {
|
||||
width: 28px;
|
||||
height: 70px;
|
||||
background: #f6f6f1 url(../images/top-left-bkg.png) top left no-repeat;
|
||||
float:left;
|
||||
}
|
||||
div#header-logo {
|
||||
width: 100%;
|
||||
height: 70px;
|
||||
background: #f6f6f1 url(../images/splitter-top.png) top repeat-x;
|
||||
}
|
||||
.ui-layout-pane-west {
|
||||
background: #f6f6f1 url(../images/left-bkg.png) left repeat-y;
|
||||
}
|
||||
div#right {
|
||||
min-width: 260px;
|
||||
}
|
||||
div#menu {
|
||||
width: 100%;
|
||||
}
|
||||
.header-menu {
|
||||
padding-left: 24px;
|
||||
padding-right: 8px;
|
||||
padding-bottom: 24px;
|
||||
background: transparent;
|
||||
}
|
||||
div#inner_menu {
|
||||
padding-left: 24px;
|
||||
padding-right: 8px;
|
||||
background: transparent;
|
||||
}
|
||||
div#logo {
|
||||
height: 70px;
|
||||
nowidth: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
div#logo div {
|
||||
height: 88px;
|
||||
width: 244px;
|
||||
background: url(../images/itop-logo-2.png) left no-repeat;
|
||||
}
|
||||
#top-bar {
|
||||
height: 55px;
|
||||
background: url(../images/banner-bkg.png) repeat-x scroll 0 0 transparent;
|
||||
text-align: right;
|
||||
}
|
||||
#admin-banner {
|
||||
float: left;
|
||||
margin-top: 2px;
|
||||
padding: 8px;
|
||||
border: 1px solid #c33;
|
||||
background-color: #fee;
|
||||
-moz-border-radius: 0.5em;
|
||||
}
|
||||
#global-search {
|
||||
height: 55px;
|
||||
float: right;
|
||||
background: url(../images/banner-search.png) no-repeat;
|
||||
nopadding-top: 15px;
|
||||
text-align: right;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
#global-search > form div {
|
||||
padding-left: 4px;
|
||||
}
|
||||
#global-search table, #global-search tr td, #global-search tr {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
height: 55px;
|
||||
margin: 0;
|
||||
background: transparent;
|
||||
overflow-y: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#global-search > form input[type="text"] {
|
||||
border: 0;
|
||||
height: 18px;
|
||||
width: 180px;
|
||||
padding-top: 4;
|
||||
background: transparent;
|
||||
}
|
||||
#global-search > form tr td#g-search-input {
|
||||
padding-top: 3px;
|
||||
padding-left: 3px;
|
||||
background: transparent;
|
||||
}
|
||||
div.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 2px;
|
||||
float: right;
|
||||
}
|
||||
span.ui-icon {
|
||||
float:left;
|
||||
margin:0 2px;
|
||||
}
|
||||
.ui-layout-button-pin-down {
|
||||
background: url(../images/splitter-bkg.png) transparent;
|
||||
width: 16px;
|
||||
background-position: -144px -144px;
|
||||
}
|
||||
.ui-layout-resizer-west-open {
|
||||
background: url(../images/splitter-bkg.png) repeat-y scroll 0 0 transparent;
|
||||
width: 16px;
|
||||
}
|
||||
div.footer img {
|
||||
border: 0;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
div.footer{
|
||||
text-align:center;
|
||||
}
|
||||
#SearchResultsToAdd table.listResults tbody {
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
tr.row_unchanged td {
|
||||
border-bottom: 1px #ccc solid;
|
||||
padding: 2px;
|
||||
}
|
||||
.wizContainer table tr.row_error td {
|
||||
border-bottom: 1px #ccc solid;
|
||||
background-color: #fdd;
|
||||
padding: 2px;
|
||||
}
|
||||
tr.row_modified td {
|
||||
border-bottom: 1px #ccc solid;
|
||||
padding: 2px;
|
||||
}
|
||||
tr.row_added td {
|
||||
border-bottom: 1px #ccc solid;
|
||||
padding: 2px;
|
||||
}
|
||||
a.truncated {
|
||||
cursor: pointer;
|
||||
}
|
||||
.org_combo
|
||||
{
|
||||
font-size:x-small;
|
||||
width: auto;
|
||||
max-width: 200px;
|
||||
}
|
||||
span.form_validation {
|
||||
width:24px;
|
||||
text-align:center;
|
||||
}
|
||||
.notification {
|
||||
border: 1px solid #c33;
|
||||
background-color: #fee;
|
||||
padding: 0.5em;
|
||||
margin: 0.5em;
|
||||
text-align:center;
|
||||
width: 95%;
|
||||
-moz-border-radius: 0.5em;
|
||||
}
|
||||
BIN
css/ui-lightness/images/ui-anim_basic_16x16.gif
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 260 B |
|
After Width: | Height: | Size: 251 B |
BIN
css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png
Normal file
|
After Width: | Height: | Size: 178 B |
BIN
css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png
Normal file
|
After Width: | Height: | Size: 104 B |
BIN
css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png
Normal file
|
After Width: | Height: | Size: 125 B |
BIN
css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png
Normal file
|
After Width: | Height: | Size: 105 B |
BIN
css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 90 B |
BIN
css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png
Normal file
|
After Width: | Height: | Size: 129 B |