diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php index 3f4f9369b..7ef21ff1b 100644 --- a/addons/userrights/userrightsmatrix.class.inc.php +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -274,7 +274,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI return true; } - public function GetSelectFilter($oUser, $sClass) + public function GetSelectFilter($oUser, $sClass, $aSettings = array()) { $oNullFilter = new DBObjectSearch($sClass); return $oNullFilter; diff --git a/addons/userrights/userrightsnull.class.inc.php b/addons/userrights/userrightsnull.class.inc.php index 760b93ab7..2c0bda359 100644 --- a/addons/userrights/userrightsnull.class.inc.php +++ b/addons/userrights/userrightsnull.class.inc.php @@ -47,7 +47,7 @@ class UserRightsNull extends UserRightsAddOnAPI return true; } - public function GetSelectFilter($oUser, $sClass) + public function GetSelectFilter($oUser, $sClass, $aSettings = array()) { $oNullFilter = new DBObjectSearch($sClass); return $oNullFilter; diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 39987001f..a00c840c2 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -659,6 +659,11 @@ class UserRightsProfile extends UserRightsAddOnAPI $oKPI = new ExecutionKPI(); + if (self::HasSharing()) + { + SharedObject::InitSharedClassProperties(); + } + $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_Profiles")); $this->m_aProfiles = array(); while ($oProfile = $oProfileSet->Fetch()) @@ -683,11 +688,27 @@ class UserRightsProfile extends UserRightsAddOnAPI } } - $oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_UserOrg")); $this->m_aUserOrgs = array(); - while ($oUserOrg = $oUserOrgSet->Fetch()) + + $sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization'); + if ($sHierarchicalKeyCode !== false) { - $this->m_aUserOrgs[$oUserOrg->Get('userid')][] = $oUserOrg->Get('allowed_org_id'); + $sUserOrgQuery = 'SELECT UserOrg, Org FROM Organization AS Org JOIN Organization AS Root ON Org.parent_id BELOW Root.id JOIN URP_UserOrg AS UserOrg ON UserOrg.allowed_org_id = Root.id'; + $oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sUserOrgQuery)); + while ($aRow = $oUserOrgSet->FetchAssoc()) + { + $oUserOrg = $aRow['UserOrg']; + $oOrg = $aRow['Org']; + $this->m_aUserOrgs[$oUserOrg->Get('userid')][] = $oOrg->GetKey(); + } + } + else + { + $oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData("SELECT URP_UserOrg")); + while ($oUserOrg = $oUserOrgSet->Fetch()) + { + $this->m_aUserOrgs[$oUserOrg->Get('userid')][] = $oUserOrg->Get('allowed_org_id'); + } } $this->m_aClassStimulusGrants = array(); @@ -742,7 +763,7 @@ exit; } } - public function GetSelectFilter($oUser, $sClass) + public function GetSelectFilter($oUser, $sClass, $aSettings = array()) { $this->LoadCache(); @@ -754,33 +775,10 @@ exit; // Determine how to position the objects of this class // - $aCallSpec = array($sClass, 'MapContextParam'); - if (($sClass == 'Organization') || is_subclass_of($sClass, 'Organization')) + $sAttCode = self::GetOwnerOrganizationAttCode($sClass); + if (is_null($sAttCode)) { - $sAttCode = 'id'; - } - elseif (is_callable($aCallSpec)) - { - $sAttCode = call_user_func($aCallSpec, 'org_id'); // Returns null when there is no mapping for this parameter - - if ($sAttCode == null) - { - return true; - } - if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) - { - // Skip silently. The data model checker will tell you something about this... - return true; - } - } - 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 + // No filtering for this object return true; } // Position the user @@ -796,48 +794,65 @@ exit; $oFilter = new DBObjectSearch($sClass); $oListExpr = ListExpression::FromScalars($aUserOrgs); - // Check if the condition points to a hierarchical key - $bConditionAdded = false; + $oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); + $oFilter->AddConditionExpression($oCondition); - if ($sAttCode == 'id') + if (self::HasSharing()) { - // Filtering on the objects themselves - $sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sClass); - - if ($sHierarchicalKeyCode !== false) + if (($sAttCode == 'id') && isset($aSettings['bSearchMode']) && $aSettings['bSearchMode']) { - $oRootFilter = new DBObjectSearch($sClass); - $oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); - $oRootFilter->AddConditionExpression($oCondition); - $oFilter->AddCondition_PointingTo($oRootFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW); // Use the 'below' operator by default - $bConditionAdded = true; - } - } - else - { - $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); - if ($oAttDef->IsExternalKey()) - { - $sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($oAttDef->GetTargetClass()); - - if ($sHierarchicalKeyCode !== false) + // Querying organizations (or derived) + // and the expected list of organizations will be used as a search criteria + // Therefore the query can also return organization having objects shared with the allowed organizations + // + // 1) build the list of organizations sharing something with the allowed organizations + // Organization <== sharing_org_id == SharedObject having org_id IN {user orgs} + $oShareSearch = new DBObjectSearch('SharedObject'); + $oOrgField = new FieldExpression('org_id', 'SharedObject'); + $oShareSearch->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr)); + + $oSearchSharers = new DBObjectSearch('Organization'); + $oSearchSharers->AllowAllData(); + $oSearchSharers->AddCondition_ReferencedBy($oShareSearch, 'sharing_org_id'); + $aSharers = array(); + foreach($oSearchSharers->ToDataArray(array('id')) as $aRow) { - $oRootFilter = new DBObjectSearch($oAttDef->GetTargetClass()); - $oExpression = new FieldExpression('id', $oAttDef->GetTargetClass()); - $oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); - $oRootFilter->AddConditionExpression($oCondition); - $oHKFilter = new DBObjectSearch($oAttDef->GetTargetClass()); - $oHKFilter->AddCondition_PointingTo($oRootFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW); // Use the 'below' operator by default - $oFilter->AddCondition_PointingTo($oHKFilter, $sAttCode); - $bConditionAdded = true; + $aSharers[] = $aRow['id']; + } + // 2) Enlarge the overall results: ... OR id IN(id1, id2, id3) + if (count($aSharers) > 0) + { + $oSharersList = ListExpression::FromScalars($aSharers); + $oFilter->MergeConditionExpression(new BinaryExpression($oExpression, 'IN', $oSharersList)); } } - } - if (!$bConditionAdded) - { - $oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); - $oFilter->AddConditionExpression($oCondition); - } + + $aShareProperties = SharedObject::GetSharedClassProperties($sClass); + if ($aShareProperties) + { + $sShareClass = $aShareProperties['share_class']; + $sShareAttCode = $aShareProperties['attcode']; + + $oSearchShares = new DBObjectSearch($sShareClass); + $oSearchShares->AllowAllData(); + + $sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization'); + $oOrgField = new FieldExpression('org_id', $sShareClass); + $oSearchShares->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr)); + $aShared = array(); + foreach($oSearchShares->ToDataArray(array($sShareAttCode)) as $aRow) + { + $aShared[] = $aRow[$sShareAttCode]; + } + if (count($aShared) > 0) + { + $oObjId = new FieldExpression('id', $sClass); + $oSharedIdList = ListExpression::FromScalars($aShared); + $oFilter->MergeConditionExpression(new BinaryExpression($oObjId, 'IN', $oSharedIdList)); + } + } + } // if HasSharing + return $oFilter; } @@ -926,10 +941,76 @@ exit; { $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']; + $iPermission = $aObjectPermissions['permission']; + + // Note: In most cases 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 + + if ($iPermission != UR_ALLOWED_YES) + { + // It is already NO for everyone... that's the final word! + } + elseif ($iActionCode == UR_ACTION_READ) + { + // We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading + } + elseif ($iActionCode == UR_ACTION_BULK_READ) + { + // We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading + } + elseif ($oInstanceSet) + { + // We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading + // We have to answer NO for objects shared for reading purposes + if (self::HasSharing()) + { + $aClassProps = SharedObject::GetSharedClassProperties($sClass); + if ($aClassProps) + { + // This class is shared, GetSelectFilter may allow some objects for read only + // But currently we are checking wether the objects might be written... + // Let's exclude the objects based on the relevant criteria + + $sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass); + if (!is_null($sOrgAttCode)) + { + $aUserOrgs = $this->GetUserOrgs($oUser, $sClass); + if (!is_null($aUserOrgs) && count($aUserOrgs) > 0) + { + $iCountNO = 0; + $iCountYES = 0; + $oInstanceSet->Rewind(); + while($oObject = $oInstanceSet->Fetch()) + { + $iOrg = $oObject->Get($sOrgAttCode); + if (in_array($iOrg, $aUserOrgs)) + { + $iCountYES++; + } + else + { + $iCountNO++; + } + } + if ($iCountNO == 0) + { + $iPermission = UR_ALLOWED_YES; + } + elseif ($iCountYES == 0) + { + $iPermission = UR_ALLOWED_NO; + } + else + { + $iPermission = UR_ALLOWED_DEPENDS; + } + } + } + } + } + } + return $iPermission; } public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null) @@ -993,6 +1074,49 @@ exit; { $this->ResetCache(); } + + /** + * Find out which attribute is corresponding the the dimension 'owner org' + * returns null if no such attribute has been found (no filtering should occur) + */ + public static function GetOwnerOrganizationAttCode($sClass) + { + $sAttCode = null; + + $aCallSpec = array($sClass, 'MapContextParam'); + if (($sClass == 'Organization') || is_subclass_of($sClass, 'Organization')) + { + $sAttCode = 'id'; + } + elseif (is_callable($aCallSpec)) + { + $sAttCode = call_user_func($aCallSpec, 'org_id'); // Returns null when there is no mapping for this parameter + if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) + { + // Skip silently. The data model checker will tell you something about this... + $sAttCode = null; + } + } + elseif(MetaModel::IsValidAttCode($sClass, 'org_id')) + { + $sAttCode = 'org_id'; + } + + return $sAttCode; + } + + /** + * Determine wether the objects can be shared by the mean of a class SharedObject + **/ + protected static function HasSharing() + { + static $bHasSharing; + if (!isset($bHasSharing)) + { + $bHasSharing = class_exists('SharedObject'); + } + return $bHasSharing; + } } diff --git a/addons/userrights/userrightsprojection.class.inc.php b/addons/userrights/userrightsprojection.class.inc.php index 08810e389..4c7f3d3f8 100644 --- a/addons/userrights/userrightsprojection.class.inc.php +++ b/addons/userrights/userrightsprojection.class.inc.php @@ -734,7 +734,7 @@ exit; return true; } - public function GetSelectFilter($oUser, $sClass) + public function GetSelectFilter($oUser, $sClass, $aSettings = array()) { $aConditions = array(); foreach ($this->m_aDimensions as $iDimension => $oDimension) diff --git a/application/applicationcontext.class.inc.php b/application/applicationcontext.class.inc.php index 0d5f27f4d..85c8bcbeb 100644 --- a/application/applicationcontext.class.inc.php +++ b/application/applicationcontext.class.inc.php @@ -115,6 +115,7 @@ class ApplicationContext if (MetaModel::IsValidClass('Organization')) { $oSearchFilter = new DBObjectSearch('Organization'); + $oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true); $oSet = new CMDBObjectSet($oSearchFilter); $iCount = $oSet->Count(); if ($iCount == 1) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index adb77cdf8..b6623b5e5 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -1548,7 +1548,9 @@ EOF if ($oAttDef->IsExternalKey()) { $sTargetClass = $oAttDef->GetTargetClass(); - $oAllowedValues = new DBObjectSet(new DBObjectSearch($sTargetClass)); + $oSearch = new DBObjectSearch($sTargetClass); + $oSearch->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true); + $oAllowedValues = new DBObjectSet($oSearch); $iFieldSize = $oAttDef->GetMaxSize(); $iMaxComboLength = $oAttDef->GetMaximumComboLength(); @@ -2382,7 +2384,7 @@ EOF $current = HILIGHT_CLASS_NONE; // Not hilighted by default // Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information - foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $new = $oExtensionInstance->GetHilightClass($this); @$current = self::$m_highlightComparison[$current][$new]; @@ -2667,7 +2669,7 @@ EOF $this->UpdateObjectFromArray($aFinalValues); // Invoke extensions after the update of the object from the form - foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oExtensionInstance->OnFormSubmit($this, $sFormPrefix); } diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index b7a4a1fc8..21e53109f 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -462,11 +462,13 @@ EOF // Display the list of *favorite* organizations... but keeping in mind what is the real number of organizations $aFavoriteOrgs = appUserPreferences::GetPref('favorite_orgs', null); $oSearchFilter = new DBObjectSearch('Organization'); + $oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true); $oSet = new CMDBObjectSet($oSearchFilter); $iCount = $oSet->Count(); // total number of existing Orgs // Now get the list of Orgs to be displayed in the menu $oSearchFilter = DBObjectSearch::FromOQL(ApplicationMenu::GetFavoriteSiloQuery()); + $oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true); if (!empty($aFavoriteOrgs)) { $oSearchFilter->AddCondition('id', $aFavoriteOrgs, 'IN'); @@ -513,8 +515,8 @@ EOF $sHtml .= ''; */ $sFavoriteOrgs = ''; - $oWidget = new UIExtKeyWidget('Organization', 'org_id'); - $sHtml .= $oWidget->Display($this, 50, false, '', $oSet, $iCurrentOrganization, 'org_id', false, 'c[org_id]', '', array('iFieldSize' => 20, 'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'), 'sDefaultValue' => Dict::S('UI:AllOrganizations')), $bSearchMode = true); + $oWidget = new UIExtKeyWidget('Organization', 'org_id', '', true /* search mode */); + $sHtml .= $oWidget->Display($this, 50, false, '', $oSet, $iCurrentOrganization, 'org_id', false, 'c[org_id]', '', array('iFieldSize' => 20, 'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'), 'sDefaultValue' => Dict::S('UI:AllOrganizations'))); $this->add_ready_script('$("#org_id").bind("extkeychange", function() { $("#SiloSelection form").submit(); } )'); $this->add_ready_script("$('#label_org_id').click( function() { $(this).val(''); $('#org_id').val(''); return true; } );\n"); // Add other dimensions/context information to this form diff --git a/application/ui.extkeywidget.class.inc.php b/application/ui.extkeywidget.class.inc.php index 0a4682876..d5be22940 100644 --- a/application/ui.extkeywidget.class.inc.php +++ b/application/ui.extkeywidget.class.inc.php @@ -66,6 +66,7 @@ class UIExtKeyWidget protected $iId; protected $sTargetClass; protected $sAttCode; + protected $bSearchMode; //public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '') static public function DisplayFromAttCode($oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '', $aArgs, $bSearchMode = false) @@ -82,15 +83,16 @@ class UIExtKeyWidget { $sDisplayStyle = 'select'; // In search mode, always use a drop-down list } - $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode); - return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, $bSearchMode, $sDisplayStyle); + $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode); + return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle); } - public function __construct($sTargetClass, $iInputId, $sAttCode = '') + public function __construct($sTargetClass, $iInputId, $sAttCode = '', $bSearchMode = false) { $this->sTargetClass = $sTargetClass; $this->iId = $iInputId; $this->sAttCode = $sAttCode; + $this->bSearchMode = $bSearchMode; } /** @@ -99,28 +101,34 @@ class UIExtKeyWidget * @param Hash $aArgs Extra context arguments * @return string The HTML fragment to be inserted into the page */ - public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = false, $sDisplayStyle = 'select') + public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select') { + if (!is_null($bSearchMode)) + { + $this->bSearchMode = $bSearchMode; + } $sTitle = addslashes($sTitle); $oPage->add_linked_script('../js/extkeywidget.js'); $oPage->add_linked_script('../js/forms-json-utils.js'); - $bCreate = (!$bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation); + $bCreate = (!$this->bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation); $bExtensions = true; $sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm'); - $sAttrFieldPrefix = ($bSearchMode) ? '' : 'attr_'; + $sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_'; $sHTMLValue = ""; // no wrap $sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL()); - if($bSearchMode) + if($this->bSearchMode) { $sWizHelper = 'null'; $sWizHelperJSON = "''"; + $sJSSearchMode = 'true'; } else { $sWizHelper = 'oWizardHelper'.$sFormPrefix; $sWizHelperJSON = $sWizHelper.'.ToJSON()'; + $sJSSearchMode = 'false'; } if (is_null($oAllowedValues)) { @@ -155,7 +163,7 @@ class UIExtKeyWidget $sHelpText = ''; //$this->oAttDef->GetHelpOnEdition(); $sHTMLValue = "\n"; $oPage->add_ready_script( <<iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}'); + oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode); oACWidget_{$this->iId}.emptyHtml = "

$sMessage

"; $('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } ); $('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } ); @@ -215,13 +223,14 @@ EOF // another hidden input to store & pass the object's Id $sHTMLValue .= "iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"$value\" />\n"; - + + $JSSearchMode = $this->bSearchMode ? 'true' : 'false'; // Scripts to start the autocomplete and bind some events to it $oPage->add_ready_script( <<iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}'); + oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode); oACWidget_{$this->iId}.emptyHtml = "

$sMessage

"; - $('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter', json: function() { return $sWizHelperJSON; } }}); + $('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }}); $('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly ! $('#label_$this->iId').result( function(event, data, formatted) { OnAutoComplete('{$this->iId}', event, data, formatted); } ); $('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } ); @@ -277,10 +286,10 @@ EOF } else { - $aParam = array(); + $aParams = array(); $oFilter = new DBObjectSearch($this->sTargetClass); - $oSet = new CMDBObjectSet($oFilter); } + $oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode); $oBlock = new DisplayBlock($oFilter, 'search', false, $aParams); $sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => true, 'currentId' => $this->iId)); $sHTML .= "
iId}\" OnSubmit=\"return oACWidget_{$this->iId}.DoOk();\">\n"; @@ -319,6 +328,7 @@ EOF try { $oFilter = DBObjectSearch::FromOQL($sFilter); + $oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode); $oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj))); $oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results } @@ -328,6 +338,7 @@ EOF // TODO check if we can improve this behavior... $sOQL = 'SELECT '.$sRemoteClass; $oFilter = DBObjectSearch::FromOQL($sOQL); + $oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode); //$oBlock = new DisplayBlock($oFilter, 'list', false); //$oBlock->Display($oP, $this->iId.'_results', array('cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single')); // Don't display the 'Actions' menu on the results } @@ -347,6 +358,7 @@ EOF throw new Exception('Implementation: null value for allowed values definition'); } $oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities + $oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode); $aValues = $oValuesSet->GetValues(array('this' => $oObj), $sContains); foreach($aValues as $sKey => $sFriendlyName) { @@ -359,8 +371,18 @@ EOF */ public function GetObjectName($iObjId) { - $oObj = MetaModel::GetObject($this->sTargetClass, $iObjId); - return $oObj->GetName(); + $aModifierProps = array(); + $aModifierProps['UserRightsGetSelectFilter']['bSearchMode'] = $this->bSearchMode; + + $oObj = MetaModel::GetObject($this->sTargetClass, $iObjId, false, false, $aModifierProps); + if ($oObj) + { + return $oObj->GetName(); + } + else + { + return ''; + } } /** @@ -421,6 +443,7 @@ EOF try { $oFilter = DBObjectSearch::FromOQL($sFilter); + $oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode); $oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj)); } catch(MissingQueryArgument $e) @@ -429,6 +452,7 @@ EOF // TODO check if we can improve this behavior... $sOQL = 'SELECT '.$this->m_sTargetClass; $oFilter = DBObjectSearch::FromOQL($sOQL); + $oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode); $oSet = new DBObjectSet($oFilter); } diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index c71b48d35..65c3b449f 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -62,8 +62,9 @@ class UILinksWidget $this->m_aTableConfig = array(); $this->m_aTableConfig['form::checkbox'] = array( 'label' => "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) + foreach(MetaModel::FlattenZList(MetaModel::GetZListItems($this->m_sLinkedClass, 'list')) as $sAttCode) { + $oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sAttCode); if ($sStateAttCode == $sAttCode) { // State attribute is always hidden from the UI diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index a8add04e2..06c3487bb 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -807,7 +807,7 @@ class DBObjectSearch public function serialize($bDevelopParams = false, $aContextParams = null) { $sOql = $this->ToOql($bDevelopParams, $aContextParams); - return base64_encode(serialize(array($sOql, $this->m_aParams))); + return base64_encode(serialize(array($sOql, $this->m_aParams, $this->m_aModifierProperties))); } static public function unserialize($sValue) @@ -818,7 +818,9 @@ class DBObjectSearch // 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); + $oRetFilter = self::FromOQL($sOql, $aParams); + $oRetFilter->m_aModifierProperties = $aData[2]; + return $oRetFilter; } // SImple BUt Structured Query Languag - SubuSQL diff --git a/core/dict.class.inc.php b/core/dict.class.inc.php index dd39b754c..e618081fa 100644 --- a/core/dict.class.inc.php +++ b/core/dict.class.inc.php @@ -220,6 +220,20 @@ class Dict } } + /** + * Clone a string in every language (if it exists in that language) + */ + public static function CloneString($sSourceCode, $sDestCode) + { + foreach(self::$m_aLanguages as $sLanguageCode => $foo) + { + if (isset(self::$m_aData[$sLanguageCode][$sSourceCode])) + { + self::$m_aData[$sLanguageCode][$sDestCode] = self::$m_aData[$sLanguageCode][$sSourceCode]; + } + } + } + public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US') { $aMissing = array(); // Strings missing for the target language diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 08acfa7ca..f59293313 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -17,6 +17,7 @@ require_once(APPROOT.'core/modulehandler.class.inc.php'); require_once(APPROOT.'core/querybuildercontext.class.inc.php'); require_once(APPROOT.'core/querymodifier.class.inc.php'); +require_once(APPROOT.'core/metamodelmodifier.inc.php'); /** * Metamodel @@ -1172,6 +1173,33 @@ abstract class MetaModel self::$m_sTablePrefix = $sTablePrefix; + // Build the list of available extensions + // + $aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier', 'iOnClassInitialization'); + foreach($aInterfaces as $sInterface) + { + self::$m_aExtensionClasses[$sInterface] = array(); + } + + foreach(get_declared_classes() as $sPHPClass) + { + $oRefClass = new ReflectionClass($sPHPClass); + $oExtensionInstance = null; + foreach($aInterfaces as $sInterface) + { + if ($oRefClass->implementsInterface($sInterface)) + { + if (is_null($oExtensionInstance)) + { + $oExtensionInstance = new $sPHPClass; + } + self::$m_aExtensionClasses[$sInterface][$sPHPClass] = $oExtensionInstance; + } + } + } + + // Initialize the classes (declared attributes, etc.) + // foreach(get_declared_classes() as $sPHPClass) { if (is_subclass_of($sPHPClass, 'DBObject')) { @@ -1184,6 +1212,10 @@ abstract class MetaModel if (method_exists($sPHPClass, 'Init')) { call_user_func(array($sPHPClass, 'Init')); + foreach (MetaModel::EnumPlugins('iOnClassInitialization') as $sPluginClass => $oClassInit) + { + $oClassInit->OnAfterClassInitialization($sPHPClass); + } } } } @@ -1401,31 +1433,6 @@ abstract class MetaModel // } //} } - - // Build the list of available extensions - // - $aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension', 'iQueryModifier'); - foreach($aInterfaces as $sInterface) - { - self::$m_aExtensionClasses[$sInterface] = array(); - } - - foreach(get_declared_classes() as $sPHPClass) - { - $oRefClass = new ReflectionClass($sPHPClass); - $oExtensionInstance = null; - foreach($aInterfaces as $sInterface) - { - if ($oRefClass->implementsInterface($sInterface)) - { - if (is_null($oExtensionInstance)) - { - $oExtensionInstance = new $sPHPClass; - } - self::$m_aExtensionClasses[$sInterface][$sPHPClass] = $oExtensionInstance; - } - } - } } // To be overriden, must be called for any object class (optimization) @@ -1552,9 +1559,12 @@ abstract class MetaModel return true; } - public static function Init_AddAttribute(AttributeDefinition $oAtt) + public static function Init_AddAttribute(AttributeDefinition $oAtt, $sTargetClass = null) { - $sTargetClass = self::GetCallersPHPClass("Init"); + if (!$sTargetClass) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + } $sAttCode = $oAtt->GetCode(); if ($sAttCode == 'finalclass') @@ -1615,11 +1625,14 @@ abstract class MetaModel // Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used } - public static function Init_SetZListItems($sListCode, $aItems) + public static function Init_SetZListItems($sListCode, $aItems, $sTargetClass = null) { MyHelpers::CheckKeyInArray('list code', $sListCode, self::$m_aListInfos); - $sTargetClass = self::GetCallersPHPClass("Init"); + if (!$sTargetClass) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + } // Discard attributes that do not make sense // (missing classes in the current module combination, resulting in irrelevant ext key or link set) @@ -1906,7 +1919,7 @@ abstract class MetaModel // if (!$oFilter->IsAllDataAllowed() && !$oFilter->IsDataFiltered()) { - $oVisibleObjects = UserRights::GetSelectFilter($oFilter->GetClass()); + $oVisibleObjects = UserRights::GetSelectFilter($oFilter->GetClass(), $oFilter->GetModifierProperties('UserRightsGetSelectFilter')); if ($oVisibleObjects === false) { // Make sure this is a valid search object, saying NO for all @@ -4375,17 +4388,31 @@ abstract class MetaModel { $aRes = array(); $iTotalHits = 0; - foreach(self::$aQueryCacheGetObjectHits as $sClass => $iHits) + foreach(self::$aQueryCacheGetObjectHits as $sClassSign => $iHits) { - $aRes[] = "$sClass: $iHits"; + $aRes[] = "$sClassSign: $iHits"; $iTotalHits += $iHits; } return $iTotalHits.' ('.implode(', ', $aRes).')'; } - public static function MakeSingleRow($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false) + public static function MakeSingleRow($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null) { - if (!array_key_exists($sClass, self::$aQueryCacheGetObject)) + // Build the query cache signature + // + $sQuerySign = $sClass; + if($bAllowAllData) + { + $sQuerySign .= '_all_'; + } + if (count($aModifierProperties)) + { + array_multisort($aModifierProperties); + $sModifierProperties = json_encode($aModifierProperties); + $sQuerySign .= '_all_'.md5($sModifierProperties); + } + + if (!array_key_exists($sQuerySign, self::$aQueryCacheGetObject)) { // NOTE: Quick and VERY dirty caching mechanism which relies on // the fact that the string '987654321' will never appear in the @@ -4394,20 +4421,30 @@ abstract class MetaModel // but this would slow down -by how much time?- the application $oFilter = new DBObjectSearch($sClass); $oFilter->AddCondition('id', 987654321, '='); + if ($aModifierProperties) + { + foreach ($aModifierProperties as $sPluginClass => $aProperties) + { + foreach ($aProperties as $sProperty => $value) + { + $oFilter->SetModifierProperty($sPluginClass, $sProperty, $value); + } + } + } if ($bAllowAllData) { $oFilter->AllowAllData(); } $sSQL = self::MakeSelectQuery($oFilter); - self::$aQueryCacheGetObject[$sClass] = $sSQL; - self::$aQueryCacheGetObjectHits[$sClass] = 0; + self::$aQueryCacheGetObject[$sQuerySign] = $sSQL; + self::$aQueryCacheGetObjectHits[$sQuerySign] = 0; } else { - $sSQL = self::$aQueryCacheGetObject[$sClass]; - self::$aQueryCacheGetObjectHits[$sClass] += 1; -// echo " -load $sClass/$iKey- ".self::$aQueryCacheGetObjectHits[$sClass]."
\n"; + $sSQL = self::$aQueryCacheGetObject[$sQuerySign]; + self::$aQueryCacheGetObjectHits[$sQuerySign] += 1; +// echo " -load $sClass/$iKey- ".self::$aQueryCacheGetObjectHits[$sQuerySign]."
\n"; } $sSQL = str_replace(CMDBSource::Quote(987654321), CMDBSource::Quote($iKey), $sSQL); $res = CMDBSource::Query($sSQL); @@ -4453,10 +4490,10 @@ abstract class MetaModel return new $sClass($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec); } - public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false) + public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null) { self::_check_subclass($sClass); - $aRow = self::MakeSingleRow($sClass, $iKey, $bMustBeFound, $bAllowAllData); + $aRow = self::MakeSingleRow($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties); if (empty($aRow)) { return null; diff --git a/core/metamodelmodifier.inc.php b/core/metamodelmodifier.inc.php new file mode 100644 index 000000000..c91d0b617 --- /dev/null +++ b/core/metamodelmodifier.inc.php @@ -0,0 +1,31 @@ + + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL + */ + +interface iOnClassInitialization +{ + public function OnAfterClassInitialization($sClass); +} + +?> diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index 58793dd91..f25baa098 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -55,7 +55,7 @@ abstract class UserRightsAddOnAPI 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 GetSelectFilter($sLogin, $sClass, $aSettings = array()); // returns a filter object abstract public function IsActionAllowed($oUser, $sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null); abstract public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, /*dbObjectSet*/ $oInstanceSet = null); @@ -647,7 +647,7 @@ class UserRights return true; } - public static function GetSelectFilter($sClass) + public static function GetSelectFilter($sClass, $aSettings = array()) { // When initializing, we need to let everything pass trough if (!self::CheckLogin()) return true; @@ -656,7 +656,7 @@ class UserRights if (MetaModel::HasCategory($sClass, 'bizmodel')) { - return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass); + return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass, $aSettings); } else { diff --git a/core/valuesetdef.class.inc.php b/core/valuesetdef.class.inc.php index d8295a5a8..ce7e9ee12 100644 --- a/core/valuesetdef.class.inc.php +++ b/core/valuesetdef.class.inc.php @@ -97,17 +97,24 @@ class ValueSetObjects extends ValueSetDefinition protected $m_aOrderBy; protected $m_aExtraConditions; private $m_bAllowAllData; + private $m_aModifierProperties; - public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false) + public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array(), $bAllowAllData = false, $aModifierProperties = array()) { $this->m_sContains = ''; $this->m_sFilterExpr = $sFilterExp; $this->m_sValueAttCode = $sValueAttCode; $this->m_aOrderBy = $aOrderBy; $this->m_bAllowAllData = $bAllowAllData; + $this->m_aModifierProperties = $aModifierProperties; $this->m_aExtraConditions = array(); } + public function SetModifierProperty($sPluginClass, $sProperty, $value) + { + $this->m_aModifierProperties[$sPluginClass][$sProperty] = $value; + } + public function AddCondition(DBObjectSearch $oFilter) { $this->m_aExtraConditions[] = $oFilter; @@ -127,6 +134,13 @@ class ValueSetObjects extends ValueSetDefinition { $oFilter->MergeWith($oExtraFilter); } + foreach($this->m_aModifierProperties as $sPluginClass => $aProperties) + { + foreach ($aProperties as $sProperty => $value) + { + $oFilter->SetModifierProperty($sPluginClass, $sProperty, $value); + } + } return new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs); } @@ -162,6 +176,13 @@ class ValueSetObjects extends ValueSetDefinition { $oFilter->MergeWith($oExtraFilter); } + foreach($this->m_aModifierProperties as $sPluginClass => $aProperties) + { + foreach ($aProperties as $sProperty => $value) + { + $oFilter->SetModifierProperty($sPluginClass, $sProperty, $value); + } + } $oValueExpr = new ScalarExpression('%'.$sContains.'%'); $oNameExpr = new FieldExpression('friendlyname', $oFilter->GetClassAlias()); diff --git a/js/extkeywidget.js b/js/extkeywidget.js index 9f5b0ca2e..53ca9ff36 100644 --- a/js/extkeywidget.js +++ b/js/extkeywidget.js @@ -13,7 +13,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper, sAttCode) +function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper, sAttCode, bSearchMode) { this.id = id; this.sTargetClass = sTargetClass; @@ -25,6 +25,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper this.oWizardHelper = oWizHelper; this.ajax_request = null; this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete + this.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form this.v_html = ''; var me = this; @@ -64,6 +65,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper sTitle: me.sTitle, sAttCode: me.sAttCode, sTargetClass: me.sTargetClass, + bSearchMode: me.bSearchMode, operation: 'objectSearchForm' } @@ -138,7 +140,8 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper { var theMap = { sTargetClass: me.sTargetClass, iInputId: me.id, - sFilter: me.sFilter + sFilter: me.sFilter, + bSearchMode: me.bSearchMode } // Gather the parameters from the search form @@ -215,6 +218,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper iInputId: me.id, iObjectId: iObjectId, sAttCode: me.sAttCode, + bSearchMode: me.bSearchMode, operation: 'getObjectName' } @@ -420,6 +424,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper var theMap = { sTargetClass: me.sTargetClass, sInputId: me.id, sFilter: me.sFilter, + bSearchMode: me.bSearchMode, sAttCode: me.sAttCode, value: $('#'+me.id).val() }; @@ -503,6 +508,7 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper iInputId: me.id, iObjectId: iObjectId, sAttCode: me.sAttCode, + bSearchMode: me.bSearchMode, operation: 'getObjectName' } diff --git a/pages/ajax.render.php b/pages/ajax.render.php index ac8957e3b..7abae7cbb 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -214,6 +214,7 @@ try $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); $sJson = utils::ReadParam('json', '', false, 'raw_data'); $sAttCode = utils::ReadParam('sAttCode', ''); + $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); if (!empty($sJson)) { $oWizardHelper = WizardHelper::FromJSON($sJson); @@ -224,7 +225,7 @@ try // Search form: no current object $oObj = null; } - $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode); + $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode); $oWidget->SearchObjectsToSelect($oPage, $sFilter, $sRemoteClass, $oObj); break; @@ -235,18 +236,22 @@ try $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); $sJson = utils::ReadParam('json', '', false, 'raw_data'); $sContains = utils::ReadParam('q', '', false, 'raw_data'); - if (!empty($sJson)) + $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); + if ($sContains !='') { - $oWizardHelper = WizardHelper::FromJSON($sJson); - $oObj = $oWizardHelper->GetTargetObject(); + if (!empty($sJson)) + { + $oWizardHelper = WizardHelper::FromJSON($sJson); + $oObj = $oWizardHelper->GetTargetObject(); + } + else + { + // Search form: no current object + $oObj = null; + } + $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, '', $bSearchMode); + $oWidget->AutoComplete($oPage, $sFilter, $oObj, $sContains); } - else - { - // Search form: no current object - $oObj = null; - } - $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId); - $oWidget->AutoComplete($oPage, $sFilter, $oObj, $sContains); break; // ui.extkeywidget @@ -256,7 +261,8 @@ try $iInputId = utils::ReadParam('iInputId', ''); $sTitle = utils::ReadParam('sTitle', '', false, 'raw_data'); $sAttCode = utils::ReadParam('sAttCode', ''); - $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode); + $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); + $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode); $sJson = utils::ReadParam('json', '', false, 'raw_data'); if (!empty($sJson)) { @@ -276,7 +282,7 @@ try $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $iInputId = utils::ReadParam('iInputId', ''); $sAttCode = utils::ReadParam('sAttCode', ''); - $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode); + $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, false); $sJson = utils::ReadParam('json', '', false, 'raw_data'); if (!empty($sJson)) { @@ -297,7 +303,7 @@ try $iInputId = utils::ReadParam('iInputId', ''); $sFormPrefix = utils::ReadParam('sFormPrefix', ''); $sAttCode = utils::ReadParam('sAttCode', ''); - $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode); + $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, false); $aResult = $oWidget->DoCreateObject($oPage); echo json_encode($aResult); break; @@ -307,7 +313,8 @@ try $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $iInputId = utils::ReadParam('iInputId', ''); $iObjectId = utils::ReadParam('iObjectId', ''); - $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId); + $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); + $oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, '', $bSearchMode); $sName = $oWidget->GetObjectName($iObjectId); echo json_encode(array('name' => $sName)); break; @@ -320,6 +327,7 @@ try $sFilter = utils::ReadParam('sFilter', '', false, 'raw_data'); $sJson = utils::ReadParam('json', '', false, 'raw_data'); $currValue = utils::ReadParam('value', ''); + $bSearchMode = (utils::ReadParam('bSearchMode', 'false') == 'true'); if (!empty($sJson)) { $oWizardHelper = WizardHelper::FromJSON($sJson); @@ -330,7 +338,7 @@ try // Search form: no current object $oObj = null; } - $oWidget = new UIExtKeyWidget($sTargetClass, $sInputId); + $oWidget = new UIExtKeyWidget($sTargetClass, $sInputId, '', $bSearchMode); $oWidget->DisplayHierarchy($oPage, $sFilter, $currValue, $oObj); break; @@ -619,7 +627,7 @@ try // Let's take this opportunity to inform the plug-ins so that they can perform some cleanup $iTransactionId = utils::ReadParam('transaction_id', 0); $sTempId = session_id().'_'.$iTransactionId; - foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) + foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oExtensionInstance->OnFormCancel($sTempId); }