Index: main/plugin-datainclusion/i18n/messages_fr.xml
===================================================================
--- main/plugin-datainclusion/i18n/messages_fr.xml	(revision 39323)
+++ main/plugin-datainclusion/i18n/messages_fr.xml	(working copy)
@@ -64,7 +64,7 @@
     <message key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_ATTRIBUTES">Attributs retournés</message>
     <message key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_ATTRIBUTES_DESC">Attributs LDAP retournés et leurs labels, séparés par des virgules (par exemple sn[nom], givenName[prenom], mail)</message>
     <message key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_CONSTRAINT">Contrainte</message>
-    <message key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_CONSTRAINT_DESC">Contrainte de recherche, éventuellement paramétrée sous la forme '(uid = $&amp;#123;uid&amp;#125;)', par exemple. Les paramètres seront à saisir à l'utilisation de la requête.</message>
+    <message key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_CONSTRAINT_DESC">Contrainte de recherche, éventuellement paramétrée sous la forme '(uid = $&amp;#123;uid[Label]&amp;#125;)' ou '(uid = $&amp;#123;uid&amp;#125;)', par exemple. Les paramètres seront à saisir à l'utilisation de la requête.</message>
     <message key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_DIALOG_BUTTON_OK">OK</message>
     <message key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_DIALOG_BUTTON_CANCEL">Annuler</message>
     <message key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_DELETE_QUERY_LABEL">Suppression d'une requête</message>
@@ -150,4 +150,13 @@
     <message key="RIBBON_TABS_TAB_QUERY_CONF_LABEL">Requête</message>
     <message key="RIBBON_TABS_TAB_QUERY_PARAMETERS_LABEL">Paramètres</message>
     
+    <!-- Data sources widget -->
+    <message key="PLUGINS_DATAINCLUSION_WIDGET_NO_DATASOURCE">Aucune source sélectionnée</message>
+    <message key="PLUGINS_DATAINCLUSION_WIDGET_DELETE_BUTTON">Supprimer la source sélectionnée</message>
+    <message key="PLUGINS_DATAINCLUSION_WIDGET_ADD_BUTTON">Cliquez pour sélectionner une source</message>
+    <message key="PLUGINS_DATAINCLUSION_WIDGET_GET_DATASOURCE_ERROR">Une erreur est survenue lors de la récupération des informations de la source.</message>
+    
+    <message key="PLUGINS_DATAINCLUSION_WIDGET_TITLE">Sélectionner une requête</message>
+    <message key="PLUGINS_DATAINCLUSION_WIDGET_DESC">Choisissez la requête associée à votre recherche</message>
+    
 </catalogue>
Index: main/plugin-datainclusion/plugin.xml
===================================================================
--- main/plugin-datainclusion/plugin.xml	(revision 39325)
+++ main/plugin-datainclusion/plugin.xml	(working copy)
@@ -308,6 +308,22 @@
         </extensions>
     </feature>
 
+    <!-- +
+         | WIDGET
+         + -->
+    <feature name="plugin.datainclusion.widgets">
+        <extensions>
+            <extension id="org.ametys.plugins.datainclusion.Widgets"
+                       point="org.ametys.cms.workspace.uitool.StaticFileImportsManager"
+                       class="org.ametys.cms.workspace.uitool.StaticFileImportsClientSideElement">
+                       
+                <scripts>
+                    <file>js/org/ametys/datainclusion/widgets/ChooseQueryWidget.i18n.js</file>
+                </scripts>
+            </extension>
+        </extensions>
+    </feature>
+
 	<feature name="module.datainclusion.tags">
         <extensions>
             <extension id="org.ametys.plugins.datainclusion.transformation.DataInclusionEnhancementHandler"
Index: main/plugin-datainclusion/resources/js/org/ametys/datainclusion/DataInclusionActions.i18n.js
===================================================================
--- main/plugin-datainclusion/resources/js/org/ametys/datainclusion/DataInclusionActions.i18n.js	(revision 39323)
+++ main/plugin-datainclusion/resources/js/org/ametys/datainclusion/DataInclusionActions.i18n.js	(working copy)
@@ -278,6 +278,23 @@
                 name: 'constraint',
                 fieldLabel: "<i18n:text i18n:key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_CONSTRAINT"/>",
                 desc: "<i18n:text i18n:key="PLUGINS_DATAINCLUSION_HANDLE_QUERIES_CONSTRAINT_DESC"/>",
+                validator: function (value) {
+                	var isValid = true;
+                	var regexpContraint = /\$\{([^\[}]+)(\[[^\]]+\])?\}/g;
+                	
+                	var match = regexpContraint.exec(value);
+                	while (match != null) {
+                		var param = match[1];
+                		var regexpParam = /^[a-z]+$/;
+                		if (!regexpParam.test(param))
+            			{
+                			isValid = false;
+            			}
+                	    match = regexpContraint.exec(value);
+                	}
+                	
+                	return isValid;
+                },
                 width: 220
             })
         );
@@ -511,8 +528,8 @@
     }
     else if (type == 'LDAP')
     {
-        var attributes = queryNode != null ? queryNode.getAttribute('attributes') : 'sn[nom], givenName[prenom], mail';
-        var constraint = queryNode != null ? queryNode.getAttribute('constraint') : '(supannAffectation=${affectation})';
+        var attributes = queryNode != null ? queryNode.getAttribute('attributes') : 'sn[Nom de famille], givenName[Prénom], mail';
+        var constraint = queryNode != null ? queryNode.getAttribute('constraint') : '(sn=${nom[Nom}*)(supannAffectation=${affectation[Affectation]})';
         form.findField('attributes').setValue(attributes);
         form.findField('attributes').clearInvalid();
         form.findField('constraint').setValue(constraint);
Index: main/plugin-datainclusion/resources/js/org/ametys/datainclusion/helper/ChooseQuery.i18n.js
===================================================================
--- main/plugin-datainclusion/resources/js/org/ametys/datainclusion/helper/ChooseQuery.i18n.js	(revision 39323)
+++ main/plugin-datainclusion/resources/js/org/ametys/datainclusion/helper/ChooseQuery.i18n.js	(working copy)
@@ -25,18 +25,18 @@
  * @param {String} siteName The site name. Can be null to see all sites.
  * @param {String} lang The language. Can be null for all languages
  */
-org.ametys.datainclusion.helper.ChooseQuery = function(icon, title, helpmessage, callback, siteName)
+org.ametys.datainclusion.helper.ChooseQuery = function(icon, title, helpmessage, callback, siteName, dataSourceType, resultType)
 {
 	org.ametys.datainclusion.helper.ChooseQuery._cb = callback;
 	org.ametys.datainclusion.helper.ChooseQuery._siteName = siteName;
 	
-	org.ametys.datainclusion.helper.ChooseQuery._initialize(icon, title, helpmessage);
+	org.ametys.datainclusion.helper.ChooseQuery._initialize(icon, title, helpmessage, dataSourceType, resultType);
 
 	org.ametys.datainclusion.helper.ChooseQuery._box.show();
 }
 //--------------------------------
 //--------------------------------
-org.ametys.datainclusion.helper.ChooseQuery._initialize = function (icon, title, helpmessage)
+org.ametys.datainclusion.helper.ChooseQuery._initialize = function (icon, title, helpmessage, dataSourceType, resultType)
 {
 	if (!org.ametys.datainclusion.helper.ChooseQuery._initialized)
 	{
@@ -46,6 +46,11 @@
 			width: 350,
 	    	height: 300,
 	    	
+	    	loader: new org.ametys.cms.tree.DataSourceXmlLoader ({
+	            dataUrl: getPluginDirectUrl('datainclusion') + '/datasource/fulltree.xml',
+	            baseParams: {siteName: context.parameters.siteName, dataSourceType: dataSourceType, resultType : resultType}
+	        }),
+	        
 		    autoScroll: true,
 		    enableDD: false,
 		    containerScroll: true
@@ -91,11 +96,18 @@
 					disabled: true,
 					handler: function() 
 					{ 
-						var selection = org.ametys.datainclusion.helper.ChooseQuery._tree.getSelectionModel().getSelectedNode();
-						
-						var id = selection.attributes.nodeName;
-						var title = selection.text;
-						if (org.ametys.datainclusion.helper.ChooseQuery._cb(id, title) !== false)
+						if (org.ametys.datainclusion.helper.ChooseQuery._cb)
+						{
+							var selection = org.ametys.datainclusion.helper.ChooseQuery._tree.getSelectionModel().getSelectedNode();
+							
+							var id = selection.attributes.nodeName;
+							var title = selection.text;
+							if (org.ametys.datainclusion.helper.ChooseQuery._cb(id, title) !== false)
+							{
+								org.ametys.datainclusion.helper.ChooseQuery._box.hide();
+							}
+						}
+						else
 						{
 							org.ametys.datainclusion.helper.ChooseQuery._box.hide();
 						}
@@ -105,7 +117,10 @@
 					text : "<i18n:text i18n:key="PLUGINS_DATAINCLUSION_HELPER_CHOOSEQUERY_CANCELBUTTON"/>",
 					handler: function() 
 					{ 
-						org.ametys.datainclusion.helper.ChooseQuery._cb(null); 
+						if (org.ametys.datainclusion.helper.ChooseQuery._cb)
+						{
+							org.ametys.datainclusion.helper.ChooseQuery._cb(null); 
+						}
 						org.ametys.datainclusion.helper.ChooseQuery._box.hide(); 
 					} 
 			    } 
Index: main/plugin-datainclusion/resources/js/org/ametys/datainclusion/ribbon/control/button/QueryEditorButton.i18n.js
===================================================================
--- main/plugin-datainclusion/resources/js/org/ametys/datainclusion/ribbon/control/button/QueryEditorButton.i18n.js	(revision 39323)
+++ main/plugin-datainclusion/resources/js/org/ametys/datainclusion/ribbon/control/button/QueryEditorButton.i18n.js	(working copy)
@@ -162,7 +162,7 @@
     var parameterNodes = response.selectNodes("query/parameter");
     for (var i= 0; i &lt; parameterNodes.length; i++)
     {
-        var param = parameterNodes[i].getAttribute('name');
+        var param = [parameterNodes[i].getAttribute('name'), parameterNodes[i].getAttribute('label')];
         parameters.push(param);
     }
     queryData['parameters'] = parameters;
@@ -222,9 +222,10 @@
         // Add an input field per parameter.
         for (var i= 0; i &lt; parameters.length; i++)
         {
-            var paramName = parameters[i];
+            var paramName = parameters[i][0];
+            var paramLabel = parameters[i][1];
             // Initialize the field value.
-            var field = this.addInputField(paramName);
+            var field = this.addInputField(paramName, paramLabel);
             if (paramValues != null &amp;&amp; paramValues[paramName] != null)
             {
                 var value = paramValues[paramName];
@@ -260,7 +261,7 @@
 /**
  * Add an input field to the parameter panel.
  */
-org.ametys.datainclusion.ribbon.control.button.QueryEditorButton.prototype.addInputField = function(name)
+org.ametys.datainclusion.ribbon.control.button.QueryEditorButton.prototype.addInputField = function(name, label)
 {
     var elementConf = 
     {
@@ -268,7 +269,7 @@
         xtype: 'textfield',
         
         hideLabel: false,
-        fieldLabel: name,
+        fieldLabel: label,
         name: 'query-param-' + name,
         
         enableKeyEvents : true,
Index: main/plugin-datainclusion/resources/js/org/ametys/datainclusion/widgets/ChooseQueryWidget.i18n.js
===================================================================
--- main/plugin-datainclusion/resources/js/org/ametys/datainclusion/widgets/ChooseQueryWidget.i18n.js	(revision 0)
+++ main/plugin-datainclusion/resources/js/org/ametys/datainclusion/widgets/ChooseQueryWidget.i18n.js	(revision 0)
@@ -0,0 +1,302 @@
+/*
+ *  Copyright 2016 Anyware Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+Ext.namespace('org.ametys.plugins.datainclusion.widgets');
+
+// Register widget
+org.ametys.utils.Widgets.registerWidget('string', 'choose-query', 'org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget');
+
+/**
+ * @class This class provides a widget to select a query in the data sources queries
+ * @extends Ext.form.TextField
+ * @xtype choosequeryfield
+ */
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget = function(config) 
+{
+    config.itemCls = 'ametys-input';
+    config.labelSeparator = '';
+    
+    if (!config.width)
+    {
+        config.width = 470; // default width
+    }
+    
+    this._dataSourceType = config.dataSourceType;
+	this._resultType = config.resultType;
+    
+    org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.superclass.constructor.call(this, config);
+}; 
+
+Ext.extend(org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget, Ext.form.TextField, {
+    /**
+     * @cfg {String} buttonText The button text to display on the action button.
+     * Note that if you supply a value for {@link #buttonCfg}, the buttonCfg.text
+     * value will be used instead if available.
+     */
+    buttonText: "",
+    /**
+     * @cfg {Boolean} buttonOnly True to display the field as a button with no visible
+     * text field (defaults to true).  If true, all inherited TextField members will still be available.
+     */
+    buttonOnly: false,
+    /**
+     * @cfg {Number} buttonOffset The number of pixels of space reserved between the button and the text field
+     * (defaults to 3).  Note that this only applies if {@link #buttonOnly} = false.
+     */
+    buttonOffset: 5,
+    /**
+     * @cfg {String} buttonIcon The button icon path for the action button.
+     */
+    buttonIcon: getPluginResourcesUrl('datainclusion') + '/img/widget/datasources_16.png',
+    
+    // private
+    readOnly: true,
+    xtype: 'choosequeryfield'
+});
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.getMargin = function()
+{
+    return 20;
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.getBtnConfig = function()
+{
+    var btnCfg = Ext.applyIf(this.buttonCfg || {}, {
+        text: this.buttonText, 
+        icon: this.buttonIcon,
+        
+        handler: this.chooseQuery,
+        scope: this
+    });
+    
+    return btnCfg;
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.onRender = function(ct, position)
+{
+	org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.superclass.onRender.call(this, ct, position);
+    
+    this.wrap = this.el.wrap({cls:'x-form-field-wrap x-form-file-widget-wrap'});
+    
+    this.displayField = new org.ametys.HtmlContainer({
+        id: this.id + '-display',
+        html: '&lt;span class="empty"&gt;' + "<i18n:text i18n:key='PLUGINS_DATAINCLUSION_WIDGET_NO_DATASOURCE'/>" + '&lt;/span&gt;',
+        cls: 'x-form-widget-display-field',
+        hidden: false,
+        renderTo: this.wrap,
+        width: (this.getWidth() - 17)
+    });
+    
+    // Button to delete the selected query
+    this.deleteBtn = new Ext.Button({
+        tooltip: "<i18n:text i18n:key='PLUGINS_DATAINCLUSION_WIDGET_DELETE_BUTTON'/>",
+        handler: this.deleteQuery,
+        scope: this,
+        hidden: true,
+        cls: 'x-form-file-widget-btn x-btn-icon',
+        icon: getPluginResourcesUrl('web') + '/img/widget/delete_16.png',
+        renderTo: this.wrap
+    });
+    
+    var btnCfg = this.getBtnConfig();
+    this.button = new Ext.Button(Ext.apply(btnCfg, {
+        renderTo: this.wrap,
+        widget: this,
+        cls: 'x-form-file-widget-btn' + (btnCfg.iconCls ? ' x-btn-icon' : ''),
+        handler: this.chooseQuery,
+        tooltip: "<i18n:text i18n:key='PLUGINS_DATAINCLUSION_WIDGET_ADD_BUTTON'/>"
+    }));
+    
+    if (this.buttonOnly){
+        this.el.hide();
+        this.wrap.setWidth(this.button.getEl().getWidth());
+    }
+    
+    if (this.desc)
+    {
+        this.itemCt.child('div.x-form-element div.x-form-field-wrap').insertSibling({
+            id: this.id + '-img',
+            tag:'img',
+            style: 'padding-left: 20px; padding-top : 7px;',
+            src: getPluginResourcesUrl('core') + '/img/administrator/config/help.gif'}, 'after');
+    
+        var tooltip = new Ext.ToolTip({
+            target: this.id + '-img',
+            html: this.desc
+        });
+    }
+    
+    if (this.width != null &amp;&amp; this.width &gt; 0)
+    {
+        this.setWidth(this.width);
+    }
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.markInvalid = function (msg)
+{
+	org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.superclass.markInvalid.call(this, msg);
+    
+    if (!this.rendered || this.preventMark)
+    {
+        // not rendered
+        return;
+    }
+    
+    this.wrap.addClass('x-widget-invalid');
+    this.displayField.el.dom.qtip = msg;
+    this.displayField.el.dom.qclass = 'x-form-invalid-tip'; 
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.clearInvalid = function ()
+{
+	org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.superclass.clearInvalid.call(this);
+    
+    if(!this.rendered || this.preventMark)
+    {
+        // not rendered
+        return;
+    }
+    
+    this.wrap.removeClass('x-widget-invalid');
+    this.displayField.el.dom.qtip = '';
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.chooseQuery = function()
+{
+	org.ametys.datainclusion.helper.ChooseQuery(
+            getPluginResourcesUrl("datainclusion") + "/img/datainclusion-small.png",
+            "<i18n:text i18n:key="PLUGINS_DATAINCLUSION_WIDGET_TITLE"/>",
+            "<i18n:text i18n:key="PLUGINS_DATAINCLUSION_WIDGET_DESC"/>",
+            this.setQuery.createDelegate(this),
+            context.parameters.siteName,
+            this._dataSourceType,
+            this._resultType
+	);
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype._rawDisplayValue = "";
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.getDisplayValue = function ()
+{
+	return this._rawDisplayValue;
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.setValue = function(value)
+{
+	if (this.getValue() == value)
+	{
+		// Optimization
+		return;
+	}
+	
+    if (value == null || value == '')
+    {
+    	this._rawDisplayValue = '';
+        this.setQuery('', '');
+    }
+    else
+	{
+        var serverMessage = new org.ametys.servercomm.ServerMessage('datainclusion', context.parameters.siteName + '/query/view/' + value, {}, org.ametys.servercomm.ServerComm.PRIORITY_SYNCHRONOUS, null, this, null);
+        var response = org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+        
+        if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key='PLUGINS_DATAINCLUSION_WIDGET_GET_DATASOURCE_ERROR'/>", response, 'org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget'))
+        {
+            return;
+        }
+        
+        var node = response.selectSingleNode('query');
+        var id = node.getAttribute('id');
+        var name = node.getAttribute('name');
+        
+        this._rawDisplayValue = name;
+        this.setQuery(id, name);
+	}
+}
+    
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.setQuery = function(id, name)
+{
+    // Update input field with the query id
+	org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.superclass.setValue.call(this, id);
+    
+    this._rawDisplayValue = name;
+    
+    if (this.rendered)
+    {
+        if (id == null || id == '')
+        {
+            this._setTooltip(null);
+            
+            var text = '&lt;span class="empty"&gt;' + "<i18n:text i18n:key="PLUGINS_DATAINCLUSION_WIDGET_ADD_BUTTON"/>" + '&lt;/span&gt;';
+            text = org.ametys.utils.Utils.getTruncatedText(this.displayField.el, text, this.displayField.getWidth());
+            this.displayField.el.update(text);
+            
+            this.deleteBtn.hide();
+            this.button.show();
+        }
+        else
+        {
+            var text = '&lt;span&gt;' + name + '&lt;/span&gt;';
+            text = org.ametys.utils.Utils.getTruncatedText(this.displayField.el, text, this.displayField.getWidth());
+            this.displayField.el.update(text);
+            
+            this.deleteBtn.show();
+            this.button.hide();
+            
+            this._setTooltip(name);
+        }
+    }
+    return this;
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.deleteQuery = function()
+{
+    this.setValue('');
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype._setTooltip = function(queryName)
+{
+    // Remove if exists
+    var tooltip = Ext.getCmp(this.id + '-tooltip');
+    if (tooltip)
+    {
+        tooltip.destroy();
+    }
+    
+    if (queryName != null)
+    {
+        var text = "&lt;div class='page-sitemape-tooltip'&gt;"
+            + "&lt;b&gt;" + queryName + "&lt;/b&gt;"
+            + "&lt;/div&gt;";
+        
+        // Create tooltip
+        new Ext.ToolTip({
+            id: this.id + '-tooltip',
+            target: this.id + '-display',
+            html: text
+        });
+    }
+}
+
+org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.prototype.setWidth = function(width)
+{
+	org.ametys.plugins.datainclusion.widgets.ChooseQueryWidget.superclass.setWidth.call(this, width);
+    // width - btn width - padding left - padding right
+    this.displayField.setWidth (width - 22 - 5 - 10);
+    
+    var text = this.displayField.el.dom.innerHTML;
+    text = org.ametys.utils.Utils.getTruncatedText (this.displayField.el, text, width - 22 - 5 - 10);
+    this.displayField.el.update (text);
+    
+}
Index: main/plugin-datainclusion/sitemap.xmap
===================================================================
--- main/plugin-datainclusion/sitemap.xmap	(revision 39323)
+++ main/plugin-datainclusion/sitemap.xmap	(working copy)
@@ -206,6 +206,8 @@
                     <map:parameter name="siteName" value="{1}"/>
                     <map:parameter name="nodeId" value="{request-param:nodeId}"/>
                     <map:parameter name="nodeType" value="{request-param:nodeType}"/>
+                    <map:parameter name="dataSourceType" value="{request-param:dataSourceType}"/>
+                    <map:parameter name="resultType" value="{request-param:resultType}"/>
                     <map:parameter name="full" value="true"/>
                 </map:generate>
                 <map:serialize type="xml"/>
@@ -216,6 +218,8 @@
                     <map:parameter name="siteName" value="{request-param:siteName}"/>
                     <map:parameter name="nodeId" value="{request-param:nodeId}"/>
                     <map:parameter name="nodeType" value="{request-param:nodeType}"/>
+                    <map:parameter name="dataSourceType" value="{request-param:dataSourceType}"/>
+                    <map:parameter name="resultType" value="{request-param:resultType}"/>
                     <map:parameter name="full" value="true"/>
                 </map:generate>
                 <map:serialize type="xml"/>
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/AbstractQuery.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/AbstractQuery.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/AbstractQuery.java	(working copy)
@@ -16,9 +16,13 @@
 package org.ametys.plugins.datainclusion.data;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.regex.Matcher;
 
+import org.apache.commons.lang.StringUtils;
+
 /**
  * An abstract query.<br>
  * Defines getters and setters for all base members of a query, and provides
@@ -37,7 +41,7 @@
     protected String _description;
     
     /** The query parameter names. */
-    protected List<String> _parameters;
+    protected Map<String, String> _parameters;
     
     /** The query result type. */
     protected ResultType _resultType;
@@ -103,29 +107,48 @@
     }
     
     /**
-     * Extract parameters from a query string in the form ${parameterName}.
+     * Extract parameters from a query string in the form ${parameterName[label]}.
      * @param query the query containing parameters.
-     * @return the parameter names as a Set.
+     * @return the parameter names as a map.
      */
-    protected List<String> _buildParameters(String query)
+    protected Map<String, String> _buildParameters(String query)
     {
-        List<String> params = new ArrayList<String>();
+        Map<String, String> params = new HashMap<String, String>();
         
         if (query != null)
         {
             Matcher matcher = Query.PARAMETER_MATCHER.matcher(query);
+            List<String> paramWithLabel = new ArrayList<String>();
             while (matcher.find())
             {
-                for (int i = 1; i <= matcher.groupCount(); i++)
+                if (matcher.groupCount() == 1)
                 {
-                    String paramName = matcher.group(i);
                     // Convert to lowercase to avoid browser problems.
-                    params.add(paramName.toLowerCase());
+                    String paramName = matcher.group(1).toLowerCase();
+                    if (!params.containsKey(paramName) || !paramWithLabel.contains(paramName))
+                    {
+                        params.put(paramName, paramName);
+                    }
+                }
+                else if (matcher.groupCount() > 2)
+                {
+                    // Convert to lowercase to avoid browser problems.
+                    String paramName = matcher.group(1).toLowerCase();
+                    String label = matcher.group(3);
+                    if (StringUtils.isBlank(label))
+                    {
+                        label = paramName;
+                        paramWithLabel.add(paramName);
+                    }
+                    
+                    if (!params.containsKey(paramName) || !paramWithLabel.contains(paramName))
+                    {
+                        params.put(paramName, label);
+                    }
                 }
             }
         }
         
         return params;
     }
-    
 }
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/Query.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/Query.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/Query.java	(working copy)
@@ -15,7 +15,6 @@
  */
 package org.ametys.plugins.datainclusion.data;
 
-import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
 
@@ -31,7 +30,7 @@
 {
     
     /** Parameter pattern. */
-    public static final String PARAMETER_PATTERN = "\\$\\{(\\w+)\\}";
+    public static final String PARAMETER_PATTERN = "\\$\\{(\\w+)(\\[([^\\]]+)\\])?\\}";
     
     /** Parameter matcher. */
     public static final Pattern PARAMETER_MATCHER = Pattern.compile(PARAMETER_PATTERN);
@@ -70,10 +69,10 @@
     String getType();
     
     /**
-     * Get the query parameter names.
-     * @return the query parameter names as a Collection.
+     * Get the map of query parameter names with label.
+     * @return the map of query parameter names with label.
      */
-    List<String> getParameterNames();
+    Map<String, String> getParametersLabel();
     
     /**
      * Get the query result type.
@@ -102,5 +101,4 @@
      * @throws DataInclusionException 
      */
     <R extends QueryResult> R execute(Map<String, String> parameterValues) throws DataInclusionException;
-    
 }
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/QueryDao.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/QueryDao.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/QueryDao.java	(working copy)
@@ -104,6 +104,16 @@
     Map<String, Query> getDataSourceQueries(String siteName, String dataSourceId) throws DataInclusionException;
     
     /**
+     * Get all the queries of a site.
+     * @param siteName the site name.
+     * @param dataSourceId 
+     * @param resultType the resultType
+     * @return the queries as a Map.
+     * @throws DataInclusionException 
+     */
+    Map<String, Query> getDataSourceQueries(String siteName, String dataSourceId, String resultType) throws DataInclusionException;
+    
+    /**
      * Get a query from its id.
      * @param siteName the site name.
      * @param id the query id.
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/jcr/JcrQueryDao.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/jcr/JcrQueryDao.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/jcr/JcrQueryDao.java	(working copy)
@@ -380,6 +380,37 @@
     }
     
     @Override
+    public Map<String, Query> getDataSourceQueries(String siteName, String dataSourceId, String resultType) throws DataInclusionException
+    {
+        try
+        {
+            // TODO Should be a made with a query instead of filtering.
+            Map<String, Query> queries = new HashMap<String, Query>();
+            
+            Node queriesNode = _getQueriesNode(siteName);
+            
+            NodeIterator queryNodes = queriesNode.getNodes();
+            while (queryNodes.hasNext())
+            {
+                Node node = queryNodes.nextNode();
+                
+                // Extract a Query object from the node and add it to the map.
+                Query query = _extractQuery(node);
+                if (query.getDataSource().getId().equals(dataSourceId) && resultType.equals(query.getResultType().toString()))
+                {
+                    queries.put(query.getId(), query);
+                }
+            }
+            
+            return queries;
+        }
+        catch (RepositoryException e)
+        {
+            throw new DataInclusionException("Error getting the data sources", e);
+        }
+    }
+    
+    @Override
     public Query getQuery(String siteName, String id) throws DataInclusionException
     {
         try
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/ldap/LdapDataSource.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/ldap/LdapDataSource.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/ldap/LdapDataSource.java	(working copy)
@@ -242,7 +242,7 @@
     {
 //        try
 //        {
-        return new InitialDirContext(_getContextEnv());
+        return new InitialDirContext(getContextEnv());
 //        }
 //        catch (NamingException e)
 //        {
@@ -254,7 +254,7 @@
      * Get the parameters for connecting to the LDAP server.
      * @return Parameters for connecting as a hashtable.
      */
-    protected Hashtable<String, String> _getContextEnv()
+    public Hashtable<String, String> getContextEnv()
     {
         Hashtable<String, String> env = new Hashtable<String, String>();
         
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/ldap/LdapQuery.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/ldap/LdapQuery.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/ldap/LdapQuery.java	(working copy)
@@ -104,7 +104,7 @@
     }
     
     @Override
-    public List<String> getParameterNames()
+    public Map<String, String> getParametersLabel()
     {
         if (_parameters == null)
         {
@@ -207,17 +207,17 @@
             
             Map<String, String> attributeMap = getAttributesAsMap();
             
-            int paramCount = getParameterNames().size();
+            int paramCount = getParametersLabel().size();
             Object[] values = new Object[paramCount];
             
             // Replace all the parameters by "{x}" placeholders and fill the parameter values array.
             int paramIndex = 0;
             String constraint = _constraint;
-            for (String paramName : getParameterNames())
+            for (String paramName : getParametersLabel().keySet())
             {
                 if (parameterValues.containsKey(paramName))
                 {
-                    constraint = constraint.replace("${" + paramName + "}", "{" + paramIndex + "}");
+                    constraint = constraint.replaceAll("\\$\\{" + paramName + "(\\[[^\\]]*\\])?\\}", "{" + paramIndex + "}");
                     values[paramIndex] = parameterValues.get(paramName);
                     paramIndex++;
                 }
@@ -235,7 +235,7 @@
             List<String> columnLabels = new ArrayList<String>(attributeMap.keySet());
             
             // Create the controls : the attributes to return are the values of the map.
-            SearchControls controls = _getSearchControls(attributeMap.values(), _dataSource.getScope());
+            SearchControls controls = getSearchControls(attributeMap.values(), _dataSource.getScope());
             
             // Execute the search.
             results = dirContext.search("", filter.toString(), values, controls);
@@ -252,7 +252,7 @@
         finally
         {
             // Cleanup LDAP resources.
-            _cleanup(dirContext, results);
+            cleanup(dirContext, results);
         }   
     }
     
@@ -262,7 +262,7 @@
      * @param scopeStr the scope as a String.
      * @return the search controls.
      */
-    protected SearchControls _getSearchControls(Collection<String> attributes, String scopeStr)
+    public SearchControls getSearchControls(Collection<String> attributes, String scopeStr)
     {
         // Paramètres de recherche
         SearchControls controls = new SearchControls();
@@ -306,7 +306,7 @@
      * @param context The connection to the database to close.
      * @param result  The result to close.
      */
-    protected void _cleanup(Context context, NamingEnumeration<SearchResult> result)
+    public void cleanup(Context context, NamingEnumeration<SearchResult> result)
     {
         if (result != null)
         {
@@ -333,7 +333,4 @@
             }
         }
     }
- 
-
-    
 }
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/ldap/LdapQueryResult.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/ldap/LdapQueryResult.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/ldap/LdapQueryResult.java	(working copy)
@@ -115,7 +115,7 @@
         // Extract the results.
         _rows = new ArrayList<QueryResultRow>();
         
-        while (results.hasMore())
+        while (results.hasMoreElements())
         {
             SearchResult result = results.next();
             
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/sql/SqlQuery.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/sql/SqlQuery.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/data/sql/SqlQuery.java	(working copy)
@@ -20,7 +20,6 @@
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Collections;
-import java.util.List;
 import java.util.Map;
 
 import org.ametys.plugins.datainclusion.data.AbstractQuery;
@@ -66,7 +65,7 @@
     }
     
     @Override
-    public List<String> getParameterNames()
+    public Map<String, String> getParametersLabel()
     {
         if (_parameters == null)
         {
@@ -118,7 +117,7 @@
             
             // Set the parameters.
             int paramIndex = 1;
-            for (String parameterName : getParameterNames())
+            for (String parameterName : getParametersLabel().keySet())
             {
                 if (parameterValues.containsKey(parameterName))
                 {
@@ -152,5 +151,4 @@
             ConnectionHelper.cleanup(connection);
         }
     }
-    
 }
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/queries/components/QueryGenerator.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/queries/components/QueryGenerator.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/queries/components/QueryGenerator.java	(working copy)
@@ -18,7 +18,9 @@
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
+import java.util.Map.Entry;
 
 import org.apache.avalon.framework.service.ServiceException;
 import org.apache.avalon.framework.service.ServiceManager;
@@ -90,7 +92,7 @@
     {
         AttributesImpl atts = new AttributesImpl();
         
-        Collection<String> queryParams = Collections.emptyList();
+        Map<String, String> queryParams = new HashMap<String, String>();
         DataSource dataSource = null;
         
         if (query != null)
@@ -102,7 +104,7 @@
             atts.addCDATAAttribute("name", query.getName());
             atts.addCDATAAttribute("description", query.getDescription());
             atts.addCDATAAttribute("resultType", query.getResultType().name());
-            queryParams = query.getParameterNames();
+            queryParams = query.getParametersLabel();
             dataSource = query.getDataSource();
             for (String confName : additionalConf.keySet())
             {
@@ -112,10 +114,13 @@
         }
         
         XMLUtils.startElement(contentHandler, "query", atts);
-        for (String parameter : queryParams)
+        for (Entry<String, String> entry : queryParams.entrySet())
         {
+            String parameter = entry.getKey();
+            String label = entry.getValue();
             AttributesImpl paramAtts = new AttributesImpl();
             paramAtts.addCDATAAttribute("name", parameter);
+            paramAtts.addCDATAAttribute("label", label);
             XMLUtils.createElement(contentHandler, "parameter", paramAtts);
         }
         if (dataSource != null && saxDataSource)
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/transformation/DataInclusionEnhancementHandler.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/transformation/DataInclusionEnhancementHandler.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/transformation/DataInclusionEnhancementHandler.java	(working copy)
@@ -316,7 +316,7 @@
     {
         Map<String, String> params = new HashMap<String, String>();
         
-        for (String param : query.getParameterNames())
+        for (String param : query.getParametersLabel().keySet())
         {
             String value = atts.getValue(_PARAMETER_PREFIX + param.toLowerCase());
             if (StringUtils.isNotBlank(value))
Index: main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/ui/QueryTreeGenerator.java
===================================================================
--- main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/ui/QueryTreeGenerator.java	(revision 39323)
+++ main/plugin-datainclusion/src/org/ametys/plugins/datainclusion/ui/QueryTreeGenerator.java	(working copy)
@@ -16,17 +16,11 @@
 package org.ametys.plugins.datainclusion.ui;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Map;
 
-import org.ametys.plugins.datainclusion.data.DataInclusionException;
-import org.ametys.plugins.datainclusion.data.DataSource;
-import org.ametys.plugins.datainclusion.data.DataSourceFactoryExtensionPoint;
-import org.ametys.plugins.datainclusion.data.Query;
-import org.ametys.plugins.datainclusion.data.QueryDao;
-import org.ametys.plugins.datainclusion.datasources.components.DataSourceGenerator;
-import org.ametys.plugins.datainclusion.queries.components.QueryGenerator;
-
 import org.apache.avalon.framework.service.ServiceException;
 import org.apache.avalon.framework.service.ServiceManager;
 import org.apache.cocoon.ProcessingException;
@@ -36,6 +30,14 @@
 import org.apache.commons.lang.StringUtils;
 import org.xml.sax.SAXException;
 
+import org.ametys.plugins.datainclusion.data.DataInclusionException;
+import org.ametys.plugins.datainclusion.data.DataSource;
+import org.ametys.plugins.datainclusion.data.DataSourceFactoryExtensionPoint;
+import org.ametys.plugins.datainclusion.data.Query;
+import org.ametys.plugins.datainclusion.data.QueryDao;
+import org.ametys.plugins.datainclusion.datasources.components.DataSourceGenerator;
+import org.ametys.plugins.datainclusion.queries.components.QueryGenerator;
+
 /**
  * Generate the data sources/query data model as a tree.
  * <ul>
@@ -75,8 +77,10 @@
         String siteName = parameters.getParameter("siteName", "");
         String nodeId = parameters.getParameter("nodeId", "");
         String nodeType = parameters.getParameter("nodeType", "");
+        String dataSourceType = parameters.getParameter("dataSourceType", "");
+        String resultType = parameters.getParameter("resultType", "");
         boolean full = parameters.getParameterAsBoolean("full", false);
-        
+
         try
         {
             contentHandler.startDocument();
@@ -91,7 +95,15 @@
             
             if ("root".equals(nodeType) || StringUtils.isBlank(nodeType))
             {
-                Collection<String> types = _dataSourceFactoryEP.getAllTypes();
+                Collection<String> types = null;
+                if (StringUtils.isNotBlank(dataSourceType))
+                {
+                    types = Arrays.asList(StringUtils.split(dataSourceType, ","));
+                }
+                else
+                {
+                    types = _dataSourceFactoryEP.getAllTypes();
+                }
                 
                 for (String typeId : types)
                 {
@@ -103,7 +115,16 @@
                     {
                         if (full)
                         {
-                            Map<String, Query> queries = _queryDao.getDataSourceQueries(siteName, dataSource.getId());
+                            Map<String, Query> queries = new HashMap<String, Query>();
+                            if (StringUtils.isBlank(resultType))
+                            {
+                                queries = _queryDao.getDataSourceQueries(siteName, dataSource.getId());
+                            }
+                            else
+                            {
+                                queries = _queryDao.getDataSourceQueries(siteName, dataSource.getId(), resultType);
+                            }
+                            
                             DataSourceGenerator.saxDataSource(contentHandler, dataSource, queries.values());
                         }
                         else
