diff --git a/application/application.inc.php b/application/application.inc.php index 6a5ddaabd..890d71794 100644 --- a/application/application.inc.php +++ b/application/application.inc.php @@ -29,6 +29,7 @@ require_once(APPROOT.'/application/displayblock.class.inc.php'); require_once(APPROOT.'/application/sqlblock.class.inc.php'); require_once(APPROOT.'/application/audit.category.class.inc.php'); require_once(APPROOT.'/application/audit.rule.class.inc.php'); +require_once(APPROOT.'/application/query.class.inc.php'); //require_once(APPROOT.'/application/menunode.class.inc.php'); require_once(APPROOT.'/application/utils.inc.php'); diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index e64e6f1b3..5eee4f917 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -1653,6 +1653,7 @@ EOF $sHTMLValue = " {$sValidationField}"; break; + case 'OQLExpression': case 'Text': $aEventsList[] ='validate'; $aEventsList[] ='keyup'; @@ -1674,7 +1675,21 @@ EOF { $sStyle = 'style="'.implode('; ', $aStyles).'"'; } - $sHTMLValue = "
{$sValidationField}
"; + if ($oAttDef->GetEditClass() == 'OQLExpression') + { + $sTestResId = 'query_res_'.$sFieldPrefix.$sAttCode.$sNameSuffix; //$oPage->GetUniqueId(); + $sBaseUrl = utils::GetAbsoluteUrlAppRoot().'pages/run_query.php?expression='; + $sInitialUrl = $sBaseUrl.urlencode($sEditValue); + $sAdditionalStuff = "".Dict::S('UI:Edit:TestQuery').""; + $oPage->add_ready_script("$('#$iId').bind('change keyup', function(evt, sFormId) { $('#$sTestResId').attr('href', '$sBaseUrl'+encodeURIComponent($(this).val())); } );"); + } + else + { + $sAdditionalStuff = ""; + } + // Ok, the text area is drawn here + $sHTMLValue = "
$sAdditionalStuff{$sValidationField}
"; + break; case 'CaseLog': diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 8ab182eee..88ff5913f 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -1908,6 +1908,7 @@ class AttributeIPAddress extends AttributeString */ class AttributeOQL extends AttributeText { + public function GetEditClass() {return "OQLExpression";} } /** diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 5981de40d..35a19c00d 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -737,11 +737,24 @@ class DBObjectSearch { return $this->m_aRelatedTo; } + + public function SetInternalParams($aParams) + { + return $this->m_aParams = $aParams; + } + public function GetInternalParams() { return $this->m_aParams; } + public function GetQueryParams() + { + $aParams = array(); + $this->m_oSearchCondition->Render($aParams, true); + return $aParams; + } + public function ListConstantFields() { return $this->m_oSearchCondition->ListConstantFields(); diff --git a/core/event.class.inc.php b/core/event.class.inc.php index 3f58f38f9..edc678ed3 100644 --- a/core/event.class.inc.php +++ b/core/event.class.inc.php @@ -103,7 +103,7 @@ class Event extends DBObject implements iDisplay $this->DisplayBareProperties($oPage, $bEditMode); } - function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $aExtraParams = array()) + function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array()) { if ($bEditMode) return; // Not editable diff --git a/core/expression.class.inc.php b/core/expression.class.inc.php index e7df0c033..ee07e2565 100644 --- a/core/expression.class.inc.php +++ b/core/expression.class.inc.php @@ -282,16 +282,7 @@ class UnaryExpression extends Expression // 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); - } + return CMDBSource::Quote($this->m_value); } public function GetUnresolvedFields($sAlias, &$aUnresolved) @@ -483,7 +474,7 @@ class VariableExpression extends UnaryExpression } elseif ($bRetrofitParams) { - //$aArgs[$this->m_sName] = null; + $aArgs[$this->m_sName] = null; return ':'.$this->m_sName; } else diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index d22890747..2411d7049 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -76,6 +76,24 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:AuditRule/Attribute:category_name+' => 'Name of the category for this rule', )); +// +// Class: QueryOQL +// + +Dict::Add('EN US', 'English', 'English', array( + 'Class:Query' => 'Query', + 'Class:Query+' => 'A query is a data set defined in a dynamic way', + 'Class:Query/Attribute:name' => 'Name', + 'Class:Query/Attribute:name+' => 'Identifies the query', + 'Class:Query/Attribute:description' => 'Description', + 'Class:Query/Attribute:description+' => 'Long description for the query (purpose, usage, etc.)', + + 'Class:QueryOQL' => 'OQL Query', + 'Class:QueryOQL+' => 'A query based on the Object Query Language', + 'Class:QueryOQL/Attribute:oql' => 'Expression', + 'Class:QueryOQL/Attribute:oql+' => 'OQL Expression', +)); + ////////////////////////////////////////////////////////////////////// // Classes in 'addon/userrights' ////////////////////////////////////////////////////////////////////// @@ -548,7 +566,7 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:RunQuery:DevelopedQuery' => 'Redevelopped query expression: ', 'UI:RunQuery:SerializedFilter' => 'Serialized filter: ', 'UI:RunQuery:Error' => 'An error occured while running the query: %1$s', - + 'UI:Query:UrlForExcel' => 'URL to use for MS-Excel web queries', 'UI:Schema:Title' => 'iTop objects schema', 'UI:Schema:CategoryMenuItem' => 'Category %1$s', 'UI:Schema:Relationships' => 'Relationships', @@ -608,8 +626,8 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:Schema:LifeCycleAttributeMustChange' => 'Must change', 'UI:Schema:LifeCycleAttributeMustPrompt' => 'User will be prompted to change the value', 'UI:Schema:LifeCycleEmptyList' => 'empty list', - 'UI:LinksWidget:Autocomplete+' => 'Type the first 3 characters...', + 'UI:Edit:TestQuery' => 'Test query', 'UI:Combo:SelectValue' => '--- select a value ---', 'UI:Label:SelectedObjects' => 'Selected objects: ', 'UI:Label:AvailableObjects' => 'Available objects: ', @@ -622,7 +640,6 @@ Dict::Add('EN US', 'English', 'English', array( 'UI:RemoveLinkedObjectsOf_Class' => 'Remove selected objects', 'UI:Message:EmptyList:UseAdd' => 'The list is empty, use the "Add..." button to add elements.', 'UI:Message:EmptyList:UseSearchForm' => 'Use the search form above to search for objects to be added.', - 'UI:Wizard:FinalStepTitle' => 'Final step: confirmation', 'UI:Title:DeletionOf_Object' => 'Deletion of %1$s', 'UI:Title:BulkDeletionOf_Count_ObjectsOf_Class' => 'Bulk deletion of %1$d objects of class %2$s', @@ -825,6 +842,9 @@ When associated with a trigger, each action is given an "order" number, specifyi 'Menu:RunQueriesMenu' => 'Run Queries', 'Menu:RunQueriesMenu+' => 'Run any query', + 'Menu:QueryMenu' => 'Query phrasebook', + 'Menu:QueryMenu+' => 'Query phrasebook', + 'Menu:DataAdministration' => 'Data administration', 'Menu:DataAdministration+' => 'Data administration', diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index 1a9edff89..33ed6caff 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -50,6 +50,16 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:AuditCategory/Attribute:definition_set+' => 'Expression OQL qui défini le périmètre d\'application de l\'audit', 'Class:AuditCategory/Attribute:rules_list' => 'Règles d\'audit', 'Class:AuditCategory/Attribute:rules_list+' => 'Règles d\'audit pour cette catégorie', + 'Class:Query' => 'Requête', + 'Class:Query+' => 'Une requête définit un ensemble d\'information de manière dynamique', + 'Class:Query/Attribute:name' => 'Nom', + 'Class:Query/Attribute:name+' => 'Identification de la requête', + 'Class:Query/Attribute:description' => 'Description', + 'Class:Query/Attribute:description+' => 'Description complète (finalité, utilisations, public)', + 'Class:QueryOQL' => 'Requête OQL', + 'Class:QueryOQL+' => 'Une requête écrite dans le langage "Object Query Language"', + 'Class:QueryOQL/Attribute:oql' => 'Expression', + 'Class:QueryOQL/Attribute:oql+' => 'Expression OQL', 'Class:URP_Profiles' => 'Profil', 'Class:URP_Profiles+' => 'Profil utilisateur', 'Class:URP_Profiles/Attribute:name' => 'Nom', @@ -431,6 +441,7 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:RunQuery:DevelopedQuery' => 'Requête OQL décompilée : ', 'UI:RunQuery:SerializedFilter' => 'Version sérialisée : ', 'UI:RunQuery:Error' => 'Une erreur s\'est produite durant l\'exécution de la requête : %1$s', + 'UI:Query:UrlForExcel' => 'Lien à copier-coller dans Excel, pour déclarer une source de données à partir du web', 'UI:Schema:Title' => 'Modèle de données iTop', 'UI:Schema:CategoryMenuItem' => 'Catégorie %1$s', 'UI:Schema:Relationships' => 'Relations', @@ -490,6 +501,7 @@ Dict::Add('FR FR', 'French', 'Français', array( 'UI:Schema:LifeCycleAttributeMustPrompt' => 'L\'utilisateur se verra proposer de changer la valeur', 'UI:Schema:LifeCycleEmptyList' => 'liste vide', 'UI:LinksWidget:Autocomplete+' => 'Tapez les 3 premiers caractères...', + 'UI:Edit:TestQuery' => 'Tester le requête', 'UI:Combo:SelectValue' => '--- choisissez une valeur ---', 'UI:Label:SelectedObjects' => 'Objets sélectionnés: ', 'UI:Label:AvailableObjects' => 'Objets disponibles: ', @@ -684,6 +696,8 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé 'Menu:Notifications:Title' => 'Catégories d\'audit', 'Menu:RunQueriesMenu' => 'Requêtes OQL', 'Menu:RunQueriesMenu+' => 'Executer une requête OQL', + 'Menu:QueryMenu' => 'Livre des requêtes', + 'Menu:QueryMenu+' => 'Livre des requêtes', 'Menu:DataAdministration' => 'Administration des données', 'Menu:DataAdministration+' => 'Administration des données', 'Menu:UniversalSearchMenu' => 'Recherche Universelle', diff --git a/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php b/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php index 9931d94c3..376bf6e92 100644 --- a/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php +++ b/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php @@ -365,7 +365,7 @@ class FileDoc extends Document $oPage->add($this->DisplayDocumentInline($oPage, 'contents')); $oPage->SetCurrentTab(Dict::S('UI:PropertiesTab')); } - parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix); + parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams); } } diff --git a/modules/itop-welcome-itil/model.itop-welcome-itil.php b/modules/itop-welcome-itil/model.itop-welcome-itil.php index a48cfb9b4..d47c94467 100644 --- a/modules/itop-welcome-itil/model.itop-welcome-itil.php +++ b/modules/itop-welcome-itil/model.itop-welcome-itil.php @@ -56,6 +56,7 @@ class ItopWelcome extends ModuleHandlerAPI new TemplateMenuNode('NotificationsMenu', APPROOT.'application/templates/notifications_menu.html', $oAdminMenu->GetIndex(), 3 /* fRank */); new OQLMenuNode('AuditCategories', 'SELECT AuditCategory', $oAdminMenu->GetIndex(), 4 /* fRank */); new WebPageMenuNode('RunQueriesMenu', utils::GetAbsoluteUrlAppRoot().'pages/run_query.php', $oAdminMenu->GetIndex(), 8 /* fRank */); + new OQLMenuNode('QueryMenu', 'SELECT Query', $oAdminMenu->GetIndex(), 8.5 /* fRank */); new WebPageMenuNode('ExportMenu', utils::GetAbsoluteUrlAppRoot().'webservices/export.php', $oAdminMenu->GetIndex(), 9 /* fRank */); new WebPageMenuNode('DataModelMenu', utils::GetAbsoluteUrlAppRoot().'pages/schema.php', $oAdminMenu->GetIndex(), 10 /* fRank */); new WebPageMenuNode('UniversalSearchMenu', utils::GetAbsoluteUrlAppRoot().'pages/UniversalSearch.php', $oAdminMenu->GetIndex(), 11 /* fRank */); diff --git a/pages/run_query.php b/pages/run_query.php index 6909eaed2..a256cd39a 100644 --- a/pages/run_query.php +++ b/pages/run_query.php @@ -120,30 +120,63 @@ try // leave $sExpression as is } - $oP->add("
\n"); - $oP->add(Dict::S('UI:RunQuery:ExpressionToEvaluate')."
\n"); - $oP->add("\n"); - $oP->add("\n"); - $oP->add($oAppContext->GetForForm()); - $oP->add("
\n"); + $oFilter = null; + $aArgs = array(); if (!empty($sExpression)) { $oFilter = DBObjectSearch::FromOQL($sExpression); if ($oFilter) { - $oP->add("

Query results

\n"); - - $oResultBlock = new DisplayBlock($oFilter, 'list', false); - $oResultBlock->Display($oP, 'runquery'); - - $oP->p(''); - $oP->StartCollapsibleSection(Dict::S('UI:RunQuery:MoreInfo'), false); - $oP->p(Dict::S('UI:RunQuery:DevelopedQuery').$oFilter->ToOQL()); - $oP->p(Dict::S('UI:RunQuery:SerializedFilter').$oFilter->serialize()); - $oP->EndCollapsibleSection(); + $aArgs = array(); + foreach($oFilter->GetQueryParams() as $sParam => $foo) + { + $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data'); + if (!is_null($value)) + { + $aArgs[$sParam] = $value; + } + else + { + $aArgs[$sParam] = ''; + } + } + $oFilter->SetInternalParams($aArgs); } } + + $oP->add("
\n"); + $oP->add(Dict::S('UI:RunQuery:ExpressionToEvaluate')."
\n"); + $oP->add("\n"); + + if (count($aArgs) > 0) + { + $oP->add("
\n"); + $oP->add("

Query arguments

\n"); + foreach($aArgs as $sParam => $sValue) + { + $oP->p("$sParam: \n"); + } + $oP->add("
\n"); + } + + $oP->add("\n"); + $oP->add($oAppContext->GetForForm()); + $oP->add("
\n"); + + if ($oFilter) + { + $oP->add("

Query results

\n"); + + $oResultBlock = new DisplayBlock($oFilter, 'list', false); + $oResultBlock->Display($oP, 'runquery'); + + $oP->p(''); + $oP->StartCollapsibleSection(Dict::S('UI:RunQuery:MoreInfo'), false); + $oP->p(Dict::S('UI:RunQuery:DevelopedQuery').$oFilter->ToOQL()); + $oP->p(Dict::S('UI:RunQuery:SerializedFilter').$oFilter->serialize()); + $oP->EndCollapsibleSection(); + } } catch(CoreException $e) { diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index 81487a848..5ce3828ba 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -2275,7 +2275,7 @@ class SynchroReplica extends DBObject implements iDisplay $this->DisplayBareProperties($oPage, $bEditMode); } - function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $aExtraParams = array()) + function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array()) { if ($bEditMode) return; // Not editable @@ -2297,7 +2297,7 @@ class SynchroReplica extends DBObject implements iDisplay { $oPage->add('
'); $oPage->add(''.Dict::Format('Core:SynchroReplica:TargetObject', $oDestObj->GetHyperlink()).''); - $oDestObj->DisplayBareProperties($oPage, false, $aExtraParams); + $oDestObj->DisplayBareProperties($oPage, false, $sPrefix, $aExtraParams); $oPage->add('
'); } $oPage->add(''); diff --git a/webservices/export.php b/webservices/export.php index b9c59e6b6..bc09cf760 100644 --- a/webservices/export.php +++ b/webservices/export.php @@ -55,6 +55,22 @@ $currentOrganization = utils::ReadParam('org_id', ''); // Main program $sExpression = utils::ReadParam('expression', '', true /* Allow CLI */, 'raw_data'); +if (strlen($sExpression) == 0) +{ + $sQueryId = trim(utils::ReadParam('query', '', true /* Allow CLI */, 'raw_data')); + if (strlen($sQueryId) > 0) + { + $oSearch = DBObjectSearch::FromOQL('SELECT QueryOQL WHERE id = :query_id', array('query_id' => $sQueryId)); + $oQueries = new DBObjectSet($oSearch); + if ($oQueries->Count() > 0) + { + $oQuery = $oQueries->Fetch(); + $sExpression = $oQuery->Get('oql'); + } + } +} + + $sFormat = strtolower(utils::ReadParam('format', 'html')); $sFields = utils::ReadParam('fields', '', true, 'raw_data'); // CSV field list (allows to specify link set attributes, still not taken into account for XML export) @@ -76,9 +92,21 @@ if (!empty($sExpression)) try { $oFilter = DBObjectSearch::FromOQL($sExpression); + + $aArgs = array(); + foreach($oFilter->GetQueryParams() as $sParam => $foo) + { + $value = utils::ReadParam('arg_'.$sParam, null, true, 'raw_data'); + if (!is_null($value)) + { + $aArgs[$sParam] = $value; + } + } + $oFilter->SetInternalParams($aArgs); + if ($oFilter) { - $oSet = new CMDBObjectSet($oFilter); + $oSet = new CMDBObjectSet($oFilter, array(), $aArgs); switch($sFormat) { case 'html': @@ -164,7 +192,9 @@ if (!$oP) $oP->p("General purpose export page."); $oP->p("Parameters:"); $oP->p(""); }