Index: ivy.xml
===================================================================
--- ivy.xml	(revision 40266)
+++ ivy.xml	(working copy)
@@ -40,7 +40,7 @@
     <dependency org="org.ametys.tools" name="tools" rev="1.1.8" conf="deliver->default"/>
     <dependency org="atlassian" name="clover" rev="2.6.3" conf="deliver->default"/>
     
-  	<dependency org="org.ametys.web" name="web" branch="3.7.x" rev="latest.integration" conf="default->default;runtime->runtime;resources->resources"/>
+  	<dependency org="org.ametys.web" name="web" branch="3.8.x" rev="latest.integration" conf="default->default;runtime->runtime;resources->resources"/>
   	<dependency org="jquery" name="jquery-ui" rev="1.8.16" conf="runtime->default"/>
 
     <!-- test configuration -->
Index: main/plugin-forms/cms-ribbon.xml
===================================================================
--- main/plugin-forms/cms-ribbon.xml	(revision 40266)
+++ main/plugin-forms/cms-ribbon.xml	(working copy)
@@ -27,8 +27,11 @@
 							<control id="org.ametys.plugins.forms.OpenInNewWindow"/>
 						</layout>
 						<control id="org.ametys.plugins.forms.Layout"/>
-						<control id="org.ametys.plugins.forms.Receipt"/>
 						<control id="org.ametys.plugins.forms.Redirection"/>
+						<layout size="small" align="middle" cols="2">
+                            <control id="org.ametys.plugins.forms.FormsWorkflow" colspan="2"/>
+                            <control id="org.ametys.plugins.forms.Receipt"/>
+                        </layout>
 						<control id="org.ametys.plugins.forms.cms.EmailProcessing"/>
 						<control id="org.ametys.plugins.forms.FormConvertToExternal"/>
 						<control id="org.ametys.plugins.forms.FormRemove"/>
@@ -38,11 +41,14 @@
 							<control id="org.ametys.plugins.forms.cms.Name"/>
 							<control id="org.ametys.plugins.forms.OpenInNewWindow"/>
 						</layout>
-						<layout size="small" align="top">
+						<layout size="small" align="middle">
 							<control id="org.ametys.plugins.forms.Layout"/>
-							<control id="org.ametys.plugins.forms.Receipt"/>
 							<control id="org.ametys.plugins.forms.Redirection"/>
 						</layout>
+                        <layout size="small" align="middle" cols="2">
+                            <control id="org.ametys.plugins.forms.FormsWorkflow" colspan="2"/>
+                            <control id="org.ametys.plugins.forms.Receipt"/>
+                        </layout>
 						<control id="org.ametys.plugins.forms.cms.EmailProcessing"/>
 						<layout size="very-small" align="middle">
 							<control id="org.ametys.plugins.forms.FormConvertToExternal"/>
@@ -54,11 +60,12 @@
 							<control id="org.ametys.plugins.forms.cms.Name"/>
 							<control id="org.ametys.plugins.forms.OpenInNewWindow"/>
 						</layout>
-						<layout size="very-small" align="top">
-							<control id="org.ametys.plugins.forms.Layout"/>
-							<control id="org.ametys.plugins.forms.Receipt"/>
-							<control id="org.ametys.plugins.forms.Redirection"/>
-						</layout>
+						<layout size="small" align="top" cols="2">
+                            <control id="org.ametys.plugins.forms.FormsWorkflow" colspan="2"/>
+                            <control id="org.ametys.plugins.forms.Layout"/>
+                            <control id="org.ametys.plugins.forms.Redirection"/>
+                            <control id="org.ametys.plugins.forms.Receipt"  colspan="2"/>
+                        </layout>
 						<control id="org.ametys.plugins.forms.cms.EmailProcessing"/>
 						<layout size="very-small" align="middle">
 							<control id="org.ametys.plugins.forms.FormConvertToExternal"/>
@@ -815,12 +822,13 @@
 			</groups>
 		</tab>
         
-        <tab label="plugin.forms:RIBBON_TABS_TAB_FORM_FORM_LABEL" id="org.ametys.plugins.forms.FormsEntriesTab" contextualColor="5" contextualGroup="FORM" contextualLabel="plugin.forms:RIBBON_TABS_TAB_FORM_GROUP_LABEL">
+        <tab label="plugin.forms:RIBBON_TABS_TAB_FORM_FORM_LABEL" id="org.ametys.plugins.forms.FormEntriesNoWorkflow" contextualColor="5" contextualGroup="FORM" contextualLabel="plugin.forms:RIBBON_TABS_TAB_FORM_GROUP_LABEL">
             <groups>
                 <group label="plugin.forms:RIBBON_TABS_TAB_FORM_GROUP_FORM_LABEL" icon="">
                     <large/>
                     <medium>
                         <control id="org.ametys.plugins.forms.entries.ExportFormEntries"/>
+                        <control id="org.ametys.plugins.forms.OpenForm"/>
                         <control id="org.ametys.plugins.forms.entries.ViewRequest"/>
                     </medium>
                     <small/>
Index: main/plugin-forms/i18n/messages_en.xml
===================================================================
--- main/plugin-forms/i18n/messages_en.xml	(revision 40266)
+++ main/plugin-forms/i18n/messages_en.xml	(working copy)
@@ -64,6 +64,10 @@
     <message key="PLUGINS_FORMS_UITOOL_BUTTON_CLEAR_ENTRIES_DESCRIPTION">Delete all entries from the selected form(s).</message>
     <message key="PLUGINS_FORMS_UITOOL_BUTTON_CLEAR_ENTRIES_NO_ENTRIES_DESCRIPTION">There is no form entry to delete in the current selection.</message>
     
+    <message key="PLUGINS_FORMS_VIEW_FORM_ENTRIES_LABEL">Show entries</message>
+    <message key="PLUGINS_FORMS_VIEW_FORM_ENTRIES_DESCRIPTION">Click here in order to show the entries of the selected form.</message>
+    <message key="PLUGINS_FORMS_VIEW_FORM_ENTRIES_FOOTER"></message>
+    
 	<message key="PLUGINS_FORMS_FORMS_EDITOR_OPENINNEWWINDOW_LABEL">Submit in a new window</message>	
 	<message key="PLUGINS_FORMS_FORMS_EDITOR_OPENINNEWWINDOW_DESCRIPTION">When the visitor sumbits the form, a new window opens with the thank message or the errors depending on the case. He can then close this window to continue.</message>
 	<message key="PLUGINS_FORMS_FORMS_EDITOR_EXTERNAL_URL_LABEL">URL</message>	
@@ -314,9 +318,12 @@
     <message key="PLUGINS_FORMS_CHOOSE_FORM_DIALOG_NO_FORM_SELECTED">You must select a form to show.</message>
     <message key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_ERROR">An error occurred while getting form data.</message>
     <message key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_SUBMISSION_DATE">Submission datetime</message>
-    
+    <message key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_WORKFLOW">Workflow</message>
+    <message key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_WORKFLOW_NO_WORKFLOW">None</message>
+        
     <message key="PLUGINS_FORMS_EXPORT_COLUMN_ID">ID</message>
     <message key="PLUGINS_FORMS_EXPORT_COLUMN_DATE">Submitted at</message>
+    <message key="PLUGINS_FORMS_EXPORT_COLUMN_WORKFLOW_STEP">State of the workflow</message>
     
     <message key="PLUGINS_FORMS_BOOLEAN_YES">Yes</message>
     <message key="PLUGINS_FORMS_DATETIME_FORMAT">d/m/Y at H:i</message>
@@ -382,7 +389,8 @@
     <message key="RIBBON_TABS_TAB_FORM_FORM_LABEL">Form</message>
     <message key="RIBBON_TABS_TAB_FORM_GROUP_FORM_LABEL">Form</message>
     <message key="RIBBON_TABS_TAB_FORM_GROUP_ENTRY_LABEL">Entries</message>
-	
+    <message key="RIBBON_TABS_TAB_FORM_ENTRY_WORKFLOW_LABEL">Workflow</message>
+    	
 	<!-- Antivirus -->
 	<message key="PLUGINS_FORMS_ANTIVIRUS_ACTIVATED_PARAM">Antivirus activated</message>
 	<message key="PLUGINS_FORMS_ANTIVIRUS_ACTIVATED_PARAM_DESCRIPTION">Check if you want to activate antivirus analysis</message>
@@ -421,11 +429,60 @@
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS">Actions</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE">Destroy the table</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_DESC">Permanently remove the SQL table and its entries corresponding to the form data.</message>
-    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_NAME">Table name</message>
-    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_TOTAL">Amount of submissions</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_NAME">Table</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_TOTAL">Submissions</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_CONTENT_TITLE">Content</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_SITE_NAME">Site</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_LAST_CONTRIBUTOR">Contribut0r</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_STILL_EXISTS">Exists?</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_HELP_TITLE">Help</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_HELP_TEXT">This screen displays the list of forms that are stored in a data base but that are no longer used in the CMS.&lt;br/&gt;&lt;br/&gt;It also allows you to permanently remove the corresponding SQL tablesCet écran vous permet de visualiser les formulaires enregistrés en base de données mais qui ne sont plus référencés dans le CMS.&lt;br/&gt;&lt;br/&gt;Il vous permet aussi de supprimer de manière définitive les tables SQL correspondantes.Cet écran vous permet de visualiser les formulaires enregistrés en base de données mais qui ne sont plus référencés dans le CMS.&lt;br/&gt;&lt;br/&gt;Il vous permet aussi de supprimer de manière définitive les tables SQL correspondantes.</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_TITLE">Deletion</message>
-    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_TITLE_TEXT">Are you sure you want to delete this table?</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_TITLE_TEXT_SINGLE">Are you sure you want to delete this table?</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_TITLE_TEXT_SEVERAL">Are you sure you want to delete these tables ?</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_ERROR">An error has occurred, the deletion has not completed.</message>
+    
+    <!-- WORKFLOW -->
+    <!-- Editor -->
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_CHANGE_DIALOG_TITLE">Change workflow</message>
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_CHANGE_DIALOG_TEXT">You are about to change the workflow of this form. It means that if you save this content, the workflow of the saved submissions will be reset. The rest of the data will not be affected. Proceed anyway ?</message>
+    
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_LABEL">Workflow</message>
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_DESCRIPTION">You can select here the workflow to use for the entries of this form.</message>
+
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_NO_WORKFLOW">None</message>
+        
+    <!-- Default text for the form entries worflow menus -->
+    <message key="ENTRY_WORKFLOW_DESCRIPTION">entry(ies) is (are) in this state.</message>
+    <message key="ENTRY_WORKFLOW_NOSELECTION_DESCRIPTION">This button is deactivated because no entry is selected.</message>
+    <message key="ENTRY_WORKFLOW_REFRESH_DESCRIPTION">This button is deactivated while some verifications are running on the server.</message>
+    <message key="ENTRY_WORKFLOW_NOMATCH_DESCRIPTION">None of the selected elements is in this state.</message>
+    <message key="ENTRY_WORKFLOW_NOACTIONAVAILABLE_DESCRIPTION">This button is deactivated because there is no available action for any of the selected elements.</message>
+    <message key="ENTRY_WORKFLOW_NOMULTISELECT_DESCRIPTION">This button is deactivated because several elements are selected.&lt;br&gt;Select only one element to use this button</message>
+        
+    <message key="PLUGINS_FORMS_WORKFLOW_ERROR_DESC">An error has occurred while changing the workflow of one entry or more.</message>
+    <message key="PLUGINS_CMS_WORKFLOW_COMMENTS_DIALOG_TITLE">Workflow state change</message> 
+    <message key="PLUGINS_CMS_WORKFLOW_COMMENTS_DIALOG_MESSAGE">You can leave a comment for this action :</message> 
+    <message key="PLUGINS_CMS_WORKFLOW_COMMENTS_DIALOG_TEXTAREA">Comment</message> 
+    <message key="PLUGINS_CMS_WORKFLOW_COMMENTS_DIALOG_TEXTAREA_DESC">If the workflow of the selected entry allows it, the comment will be included in the notification e-mail sent to the involved users. Leave empty to use the default message.</message>
+    <message key="PLUGINS_CMS_WORKFLOW_COMMENTS_DIALOG_OK">OK</message>
+    <message key="PLUGINS_CMS_WORKFLOW_COMMENTS_DIALOG_CANCEL">Cancel</message>
+    
+    <!-- Need complements dialog -->
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_TITLE">Ask for further information</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_FROM_FULLNAME">Sender</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_TO">To</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_SUBJECT">Subject</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_SUBJECT_DEFAULT">Further information request</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_BODY">Body of the email</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_BODY_DEFAULT">Dear Sir/Madam,\n\nThe processing of your submission on our website is underway. However, we need further information concerning the following elements: \n[List incomplete elements here]\n\nPlease accept, Sir/Madam, our very best regards.\n\n</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_DO_SEND">Envoi de mail</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_OK">Ok</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_CANCEL">Annuler</message>
+    
+    <!-- Notification emails default keys -->
+    <message key="WORKFLOW_MAIL_SUBJECT_ACTION_NEED_COMPLEMENTS">[{0}] Further information request on an form answer</message>
+    <message key="WORKFLOW_MAIL_BODY_ACTION_NEED_COMPLEMENTS">L'utilisateur "{0}" a demandé des compléments sur le formulaire du contenu "{1}", sur la page "{3}" du site "{2}".</message>
+
+    
 </catalogue>
Index: main/plugin-forms/i18n/messages_fr.xml
===================================================================
--- main/plugin-forms/i18n/messages_fr.xml	(revision 40266)
+++ main/plugin-forms/i18n/messages_fr.xml	(working copy)
@@ -43,7 +43,7 @@
     
 	<message key="PLUGINS_SHOW_FORMS_UI_LABEL">Entrées du formulaire</message>
 	<message key="PLUGINS_SHOW_FORMS_UI_DESCRIPTION">Visualiser les entrées du formulaire</message>
-    
+	
     <!-- Forms list tool -->
     <message key="PLUGINS_FORMS_UITOOL_FORMS_LIST_LABEL">Formulaires</message>
     <message key="PLUGINS_FORMS_UITOOL_FORMS_LIST_DESCRIPTION">Interface de gestion des formulaires créés dans les contenus du site.</message>
@@ -60,10 +60,14 @@
 	<message key="UITOOL_FORM_RESULTS_DESCRIPTION">Outil de visualisation des donnés des formulaires</message>
     <message key="PLUGINS_FORMS_UITOOL_BUTTON_DELETE_ENTRY_LABEL">Supprimer</message>
     <message key="PLUGINS_FORMS_UITOOL_BUTTON_DELETE_ENTRY_DESCRIPTION">Supprime les entrées sélectionnées.</message>
-    <message key="PLUGINS_FORMS_UITOOL_BUTTON_CLEAR_ENTRIES_LABEL">Effacer tout</message>
+    <message key="PLUGINS_FORMS_UITOOL_BUTTON_CLEAR_ENTRIES_LABEL">Effacer tout</message>    
     <message key="PLUGINS_FORMS_UITOOL_BUTTON_CLEAR_ENTRIES_DESCRIPTION">Supprime toutes les entrées du ou des formulaires sélectionnés.</message>
     <message key="PLUGINS_FORMS_UITOOL_BUTTON_CLEAR_ENTRIES_NO_ENTRIES_DESCRIPTION">Il n'y aucune entrée de formulaire à supprimer dans la sélection courante.</message>
  
+    <message key="PLUGINS_FORMS_VIEW_FORM_ENTRIES_LABEL">Voir les entrées</message>
+    <message key="PLUGINS_FORMS_VIEW_FORM_ENTRIES_DESCRIPTION">Cliquez ici afin de visualiser les entrées du formulaire sélectionné.</message>
+    <message key="PLUGINS_FORMS_VIEW_FORM_ENTRIES_FOOTER"></message>
+ 
 	<message key="PLUGINS_FORMS_FORMS_EDITOR_OPENINNEWWINDOW_LABEL">Soumettre dans une nouvelle fenêtre</message>	
 	<message key="PLUGINS_FORMS_FORMS_EDITOR_OPENINNEWWINDOW_DESCRIPTION">Lorsque le visiteur soumet le formulaire une nouvelle fenêtre s'ouvre avec le message de remerciement ou les erreurs le cas échéant. Il peut ensuite fermer cette fenêtre et continuer sa navigation.</message>	
 	<message key="PLUGINS_FORMS_FORMS_EDITOR_EXTERNAL_URL_LABEL">URL</message>	
@@ -315,7 +319,7 @@
     
     <message key="PLUGINS_FORMS_EXPORT_COLUMN_ID">ID</message>
     <message key="PLUGINS_FORMS_EXPORT_COLUMN_DATE">Date de soumission</message>
-    
+    <message key="PLUGINS_FORMS_EXPORT_COLUMN_WORKFLOW_STEP">Etat du cycle de vie</message>
     
     <message key="PLUGINS_FORMS_BOOLEAN_YES">Oui</message>
     <message key="PLUGINS_FORMS_DATETIME_FORMAT">d/m/Y à H:i</message>
@@ -324,6 +328,8 @@
     
     <message key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_ERROR">Une erreur est survenue lors de la récupération des entrées du formulaire.</message>
 	<message key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_SUBMISSION_DATE">Soumis le</message>
+	<message key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_WORKFLOW">Cycle de vie</message>
+	<message key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_WORKFLOW_NO_WORKFLOW">Aucun</message>
 
     <message key="PLUGINS_FORMS_MAIL_RESULTS_SUBJECT">Un utilisateur a envoyé des données sur le formulaire {0}</message>
     <message key="PLUGINS_FORMS_MAIL_RESULTS_TEXT">Un utilisateur a envoyé des données sur le formulaire '{0}' :</message>
@@ -384,6 +390,7 @@
     <message key="RIBBON_TABS_TAB_FORM_FORM_LABEL">Formulaire</message>
     <message key="RIBBON_TABS_TAB_FORM_GROUP_FORM_LABEL">Formulaire</message>
     <message key="RIBBON_TABS_TAB_FORM_GROUP_ENTRY_LABEL">Entrées</message>
+	<message key="RIBBON_TABS_TAB_FORM_ENTRY_WORKFLOW_LABEL">Cycle de vie</message>
 	
 	<!-- Antivirus -->
 	<message key="PLUGINS_FORMS_ANTIVIRUS_ACTIVATED_PARAM">Antivirus activé</message>
@@ -423,11 +430,60 @@
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS">Actions</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE">Détruire la table</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_DESC">Supprime définitivement la table SQL et ses entrées correspondant aux données du formulaire.</message>
-    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_NAME">Nom de la table</message>
-    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_TOTAL">Nombre d'entrées</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_NAME">Table</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_TOTAL">Entrées</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_CONTENT_TITLE">Contenu</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_SITE_NAME">Site</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_LAST_CONTRIBUTOR">Contributeur</message>
+    <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_STILL_EXISTS">Existe?</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_HELP_TITLE">Aide</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_HELP_TEXT">Cet écran vous permet de visualiser les formulaires enregistrés en base de données mais qui ne sont plus référencés dans le CMS.&lt;br/&gt;&lt;br/&gt;Il vous permet aussi de supprimer de manière définitive les tables SQL correspondantes.</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_TITLE">Suppression</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_TITLE_TEXT">Etes-vous sûr de vouloir supprimer cette table ?</message>
     <message key="PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_ACTIONS_DELETE_TABLE_ERROR">Une erreur est survenue, la suppression n'a pas été réalisée.</message>
+    
+    <!-- WORKFLOW -->
+    
+    <!-- Editor -->  
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_CHANGE_DIALOG_TITLE">Changement de cycle de vie</message>
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_CHANGE_DIALOG_TEXT">Vous êtes sur le point de changer le cycle de vie du formulaire. Cela signifie que lorsque le contenu sera sauvegardé, le cycle de vie des réponses déjà enregistrées sera réinitialisé. Le reste des données ne sera pas affecté. Souhaitez-vous continuer ?</message>
+    
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_LABEL">Cycle de vie</message>
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_DESCRIPTION">Vous pouvez choisir ici le cycle de vie à utiliser pour les entrées de ce formulaire.</message>
+    
+    <message key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_NO_WORKFLOW">Aucun</message>
+    
+    <!-- Default text for the form entries worflow menus -->
+    <message key="ENTRY_WORKFLOW_DESCRIPTION">entrée(s) est (sont) dans cet état.</message>
+    <message key="ENTRY_WORKFLOW_NOSELECTION_DESCRIPTION">Ce bouton est désactivé car aucune entrée n'est sélectionnée.</message>
+    <message key="ENTRY_WORKFLOW_REFRESH_DESCRIPTION">Ce bouton est désactivé pendant que des vérifications sont en cours sur le serveur.</message>
+    <message key="ENTRY_WORKFLOW_NOMATCH_DESCRIPTION">Aucun du ou des éléments sélectionnés n'est dans cet état.</message>
+    <message key="ENTRY_WORKFLOW_NOACTIONAVAILABLE_DESCRIPTION">Ce bouton est désactivé car aucune action n'est disponible pour aucun du ou des éléments sélectionnés.</message>
+    <message key="ENTRY_WORKFLOW_NOMULTISELECT_DESCRIPTION">Ce bouton est désactivé car plusieurs éléments sont sélectionnés.&lt;br/&gt;Veuillez ne sélectionner qu'un seul élément pour utiliser ce bouton.</message>
+    <message key="PLUGINS_FORMS_WORKFLOW_ERROR_DESC">Une erreur est survenue lors du changement du cycle de vie d'une ou de plusieurs entrées.</message>    
+    
+    <!-- Comments on workflow step dialog -->    
+    <message key="PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_TITLE">Changement d'état</message> 
+    <message key="PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_MESSAGE">Vous pouvez entrer un commentaire pour cette action :</message> 
+    <message key="PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_TEXTAREA">Commentaire</message> 
+    <message key="PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_TEXTAREA_DESC">Si le cycle de vie de l'entrée de formulaire sélectionnée le prévoit, le commentaire sera inclus dans l'e-mail de notification de changement d'état envoyé aux personnes concernées. Laissez vide pour utiliser le message par défaut.</message>
+    <message key="PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_OK">OK</message>
+    <message key="PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_CANCEL">Annuler</message> 
+    
+    <!-- Need complements dialog -->
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_TITLE">Demande de compléments</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_FROM_FULLNAME">Expéditeur</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_TO">Pour</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_SUBJECT">Sujet</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_SUBJECT_DEFAULT">Demande de compléments</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_BODY">Corps de l'email</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_BODY_DEFAULT">Madame, Monsieur,\n\nLe traitement de votre soumission sur notre site web est en cours.\nToutefois, nous aurions besoin de compléments sur les éléments suivants : \n[Listez les éléments incomplets ici]\n\nVeuillez agréer, Madame, Monsieur, l'expression de nos salutations distinguées.\n\n</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_DO_SEND">Envoi de mail</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_OK">Ok</message>
+    <message key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_CANCEL">Annuler</message>
+    
+    <!-- Notification emails default keys -->
+    <message key="WORKFLOW_MAIL_SUBJECT_ACTION_NEED_COMPLEMENTS">[{0}] Attente de compléments sur une réponse de formulaire</message>
+    <message key="WORKFLOW_MAIL_BODY_ACTION_NEED_COMPLEMENTS">L'utilisateur "{0}" a demandé des compléments sur une réponse au formulaire du contenu "{1}", sur la page "{3}" du site "{2}".</message>
+    
 </catalogue>
Index: main/plugin-forms/pages/administrator/orphan-forms/view.xsl
===================================================================
--- main/plugin-forms/pages/administrator/orphan-forms/view.xsl	(revision 40266)
+++ main/plugin-forms/pages/administrator/orphan-forms/view.xsl	(working copy)
@@ -48,7 +48,11 @@
                        org.ametys.forms.administration.OrphanForms.addElement (
                            <xsl:value-of select="position()"/>,
                            "<xsl:value-of select="@name"/>",
-                           "<xsl:value-of select="@total"/>"
+                           "<xsl:value-of select="@total"/>",
+                           "<xsl:value-of select="@contentTitle"/>",
+                           "<xsl:value-of select="@siteName"/>",
+                           "<xsl:value-of select="@lastContributor"/>",
+                           "<xsl:value-of select="@stillExists"/>"
                        );
                    </xsl:for-each>
                    
Index: main/plugin-forms/plugin.xml
===================================================================
--- main/plugin-forms/plugin.xml	(revision 40266)
+++ main/plugin-forms/plugin.xml	(working copy)
@@ -22,10 +22,6 @@
         runtimeVersion="2.0" version="2.0">
 
      <config>
-        <!-- +
-             | CORE DATASOURCE
-             + -->
-        <!-- JDBC -->
         <param id="plugins.forms.antivirus.activated" type="boolean">
             <!-- 
                 This parameter activates antivirus analysis on file submit in forms
@@ -41,10 +37,6 @@
             <order>10</order>
         </param>
 
-        <!-- +
-             | CORE DATASOURCE
-             + -->
-        <!-- JDBC -->
         <param id="plugins.forms.antivirus.command" type="string">
             <!-- 
                 This parameter contains the command to analyze a file by the antivirus
@@ -94,8 +86,8 @@
                     <param name="default-description" i18n="true">PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_DESCRIPTION</param>
                     <param name="icon-small" file="true">img/administrator/icon_dock.png</param>
                     <param name="icon-medium" file="true">img/administrator/icon_medium.png</param>
-                    <param name="icon-large" file="true">img/administrator/icon_large.png</param>
-                </action>                      
+                    <param name="icon-large" file="true">img/administrator/icon_large.png</param>               
+                </action>                     
                 <Url>administrator/orphan-forms/view\.html</Url>
                 <css>
                     <file>css/view.css</file>                
@@ -1298,7 +1290,7 @@
     		    		
     		<extension id="org.ametys.plugins.forms.RegExpType"
     				   point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
-    				   class="org.ametys.cms.clientsideelement.styles.ParaStyleClientSideElement">
+    				   class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
 				<action class="org.ametys.cms.control.ComboBoxField">
 					<param name="action">org.ametys.forms.ApplyRegexpType</param>
 					<param name="selection-listener">org.ametys.forms.RegexpTypeListener</param>
@@ -1549,14 +1541,14 @@
                 </right>
             </extension>
             
-            <extension id="org.ametys.plugins.forms.FormsEntriesTab"
+            <extension id="org.ametys.plugins.forms.FormEntriesNoWorkflow"
                        point="org.ametys.cms.workspace.ribbon.RibbonTabsManager"
                        class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
-                <action class="org.ametys.cms.ribbon.tab.SelectionTab">
+                <action class="org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab">
     				<param name="target-type">^(content-form)$</param>
     			</action> 
     			<scripts>
-    				<file plugin="cms">js/org/ametys/cms/ribbon/tab/SelectionTab.js</file>
+    				<file>js/org/ametys/forms/workflow/FormEntriesWorkflowTab.js</file>
     			</scripts>   
             </extension>
             
@@ -1565,7 +1557,7 @@
                        class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
                 <action class="org.ametys.ribbon.control.button.ActionSelectionButton">
                     <param name="action">org.ametys.forms.entry.exportFormEntries</param>
-                    <param name="target-type">^content-form$</param>
+                    <param name="target-type">^(content-form)$</param>
                     <param name="enable-multiselection">false</param>
     
                     <param name="label" i18n="true">PLUGINS_FORMS_UITOOL_BUTTON_EXPORT_LABEL</param>
@@ -1585,8 +1577,8 @@
                        class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
                 <action class="org.ametys.ribbon.control.button.ActionSelectionButton">
                     <param name="action">org.ametys.forms.entry.DeleteEntry</param>
-                    <param name="target-type">^content-form$</param>
-                    <param name="subtarget-type">^form-entry$</param>
+                    <param name="target-type">^(content-form)$</param>
+                    <param name="subtarget-type">^(form-entry)$</param>
     
                     <param name="label" i18n="true">PLUGINS_FORMS_UITOOL_BUTTON_DELETE_ENTRY_LABEL</param>
                     <param name="default-description" i18n="true">PLUGINS_FORMS_UITOOL_BUTTON_DELETE_ENTRY_DESCRIPTION</param>
@@ -1605,7 +1597,7 @@
                        class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
                 <action class="org.ametys.forms.buttons.ClearFormEntriesButton">
                     <param name="action">org.ametys.forms.entry.ClearEntries</param>
-                    <param name="target-type">^content-form$</param>
+                    <param name="target-type">^(content-form)$</param>
                     
                     <param name="label" i18n="true">PLUGINS_FORMS_UITOOL_BUTTON_CLEAR_ENTRIES_LABEL</param>
                     <param name="default-description" i18n="true">PLUGINS_FORMS_UITOOL_BUTTON_CLEAR_ENTRIES_DESCRIPTION</param>
@@ -1627,7 +1619,7 @@
                        class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
                 <action class="org.ametys.ribbon.control.button.ActionSelectionButton">
                     <param name="action">org.ametys.forms.entry.ViewRequest</param>
-                    <param name="target-type">^content-form$</param>
+                    <param name="target-type">^(content-form)$</param>
                     <param name="enable-multiselection">false</param>
                     
                     <param name="label" i18n="true">PLUGINS_FORMS_UITOOL_BUTTON_VIEW_REQUEST_LABEL</param>
@@ -1672,7 +1664,7 @@
                     <param name="icon-large" file="true">img/forms_48.png</param>
                 </action>
                 <right>Forms_Rights_Forms_List_Handle</right>
-            </extension>
+            </extension>           
             
             <extension id="org.ametys.plugins.forms.FormsListFactory"
                        point="org.ametys.cms.workspace.uitool.UIToolsFactoriesManager"
@@ -1696,6 +1688,155 @@
                     <file>css/formslist-tool.css</file>
                 </css>
             </extension>
+            
+			<extension id="org.ametys.plugins.forms.OpenForm"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.ribbon.control.button.ActionSelectionButton">
+                    <param name="action">org.ametys.forms.OpenForm</param>
+                    <param name="target-type">^(content-form)$</param>
+                    <param name="subtarget-type">!^(form-entry)$</param>
+                    <param name="enable-multiselection">false</param>
+                    
+                    <param name="label" i18n="true">PLUGINS_FORMS_VIEW_FORM_ENTRIES_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_FORMS_VIEW_FORM_ENTRIES_DESCRIPTION</param>
+                    <param name="footer" i18n="true">PLUGINS_FORMS_VIEW_FORM_ENTRIES_FOOTER</param>
+                    
+                    <param name="icon-small" file="true">img/forms_16.png</param>
+                    <param name="icon-medium" file="true">img/forms_32.png</param>
+                    <param name="icon-large" file="true">img/forms_48.png</param>
+                </action>
+                <right>Forms_Rights_Forms_List_Handle</right>
+            </extension>
+        </extensions>
+	</feature>
+	
+	<!-- WORFLOW -->
+	<feature name="org.ametys.plugins.forms.workflow.ribbon">
+	    <extensions>
+        	<extension id="org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab.form-default"
+                       point="org.ametys.cms.workspace.ribbon.RibbonTabsManager"
+                       class="org.ametys.runtime.ui.impl.StaticContextualClientSideElement">
+                <action class="org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab">
+                    <param name="target-type">^(content-form)$</param>
+                    <param name="workflow-name">^(form-default)$</param>
+                </action> 
+                <scripts>
+                    <file>js/org/ametys/forms/workflow/FormEntriesWorkflowTab.js</file>
+                </scripts>   
+            </extension>
+            
+            <!-- Proposed -->
+            <extension id="org.ametys.plugins.forms.workflow.FormEntriesWorkflowButton.form-default.proposed"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu">
+                <action>
+                    <param name="menu-3-enable-multiselection">false</param>
+                </action>
+                
+                <workflow name="form-default">
+                    <step>1</step>
+                
+                    <actions mode="exclude">
+                        <action>4</action>
+                    </actions>
+                    
+                    <comments mode="include"/>
+                    
+                    <further-information mode="include">
+                        <action>3</action>
+                    </further-information>
+                </workflow>
+                <scripts>
+                    <file>js/org/ametys/forms/workflow/FurtherInformationDialog.i18n.js</file>
+                </scripts>
+            </extension>
+            
+            <!-- Refused -->
+            <extension id="org.ametys.plugins.forms.workflow.FormEntriesWorkflowButton.form-default.refused"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu">
+                <workflow name="form-default">
+                    <step>2</step>
+                
+                    <actions mode="include">
+                        <action>4</action>
+                    </actions>
+                    
+                    <comments mode="include"/>
+                    
+                    <further-information mode="include"/>
+                </workflow>
+            </extension>
+            
+            <!-- Validated -->
+            <extension id="org.ametys.plugins.forms.workflow.FormEntriesWorkflowButton.form-default.accepted"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu">
+                <workflow name="form-default">
+                    <step>3</step>
+                
+                    <actions mode="include">
+                        <action>4</action>
+                    </actions>
+                    
+                    <comments mode="include"/>
+                    
+                    <further-information mode="include"/>
+                </workflow>
+            </extension>
+            
+            <!-- Incomplete -->
+            <extension id="org.ametys.plugins.forms.workflow.FormEntriesWorkflowButton.form-default.incomplete"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu">
+                <workflow name="form-default">
+                    <step>4</step>
+                
+                    <actions mode="exclude"/>
+                    
+                    <comments mode="include"/>
+                    
+                    <further-information mode="include"/>
+                </workflow>
+            </extension>
         </extensions>
+    </feature>
+	
+	<feature name="org.ametys.plugins.forms.workflow.component">
+        <components>
+            <component role="org.ametys.plugins.forms.workflow.FormEntriesWorkflow"
+                       class="org.ametys.plugins.forms.workflow.FormEntriesWorkflow"
+                       logger="org.ametys.plugins.forms.workflow.FormEntriesWorkflow">
+            </component>
+            
+            <component role="org.ametys.plugins.forms.workflow.RequestInformationFunction"
+                       class="org.ametys.plugins.forms.workflow.RequestInformationFunction"
+                       logger="org.ametys.plugins.forms.workflow.RequestInformationFunction">
+            </component>
+        </components>
+    </feature>
+    
+    <feature name="org.ametys.plugins.forms.workflow.editor">
+        <extensions>
+            <extension id="org.ametys.plugins.forms.FormsWorkflow"
+                       point="org.ametys.cms.workspace.ribbon.RibbonControlsManager"
+                       class="org.ametys.plugins.forms.workflow.FormWorkflowClientSideElement">
+                <action class="org.ametys.plugins.forms.workflow.FormWorkflowButton">
+                    <param name="action">org.ametys.plugins.forms.workflow.FormWorkflowButton._applyWorkflow</param>
+                    <param name="selection-listener">org.ametys.plugins.forms.workflow.FormWorkflowListener</param>
+                    
+                    <param name="width">150</param>
+                    <param name="width-small">130</param>
+                    <param name="label-width">80</param>
+
+                    <param name="label" i18n="true">PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_LABEL</param>
+                    <param name="default-description" i18n="true">PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_DESCRIPTION</param>
+                </action>
+                <scripts>
+                    <file>js/org/ametys/forms/workflow/FormWorkflowButton.i18n.js</file>
+                </scripts>
+            </extension>
+        </extensions>	
 	</feature>
 </plugin>
Index: main/plugin-forms/resources/js/org/ametys/forms/Components.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/Components.i18n.js	(revision 40266)
+++ main/plugin-forms/resources/js/org/ametys/forms/Components.i18n.js	(working copy)
@@ -1557,7 +1557,6 @@
 
 	org.ametys.forms._currentRegexpTypeNode = input;
 
-	var off = target == null || node == null;
 	if (!input || input.getAttribute("input_text") == null)
 	{
 		combo.getElements().each(function(element) {element.setDisabled(true);});
Index: main/plugin-forms/resources/js/org/ametys/forms/Conversions.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/Conversions.i18n.js	(revision 40266)
+++ main/plugin-forms/resources/js/org/ametys/forms/Conversions.i18n.js	(working copy)
@@ -40,7 +40,8 @@
 	tag.handleAttribute("form_receipt_body");
 	tag.handleAttribute("form_redirect");
 	
-	tag.handleAttribute("layout")
+	tag.handleAttribute("layout");
+	tag.handleAttribute("form_workflow");
 
 	var lbltag = org.ametys.cms.editor.Editor.handleTag("label");
 	lbltag.handleAttribute("for");
@@ -209,6 +210,7 @@
 			regexpGetContentFormFull, 
 			function (s) 
 			{
+				
 				return s.substring(0, s.length - 6) + "&lt;/form&gt;";
 			}
 	);
@@ -230,6 +232,7 @@
 				var receiptSubject = (type != '' &amp;&amp; (/form_receipt_subject="([^"]*)"/.test(s))) ? (' receipt_subject="' + RegExp.$1 + '"') : '';
 				var receiptBody = (type != '' &amp;&amp; (/form_receipt_body="([^"]*)"/.test(s))) ? (' receipt_body="' + RegExp.$1 + '"') : '';
 				var redirect = (type != '' &amp;&amp; (/form_redirect="([^"]*)"/.test(s))) ? (' redirect="' + RegExp.$1 + '"') : '';
+				var workflow = (type != '' &amp;&amp; (/form_workflow="([^"]*)"/.test(s))) ? (' workflow="' + RegExp.$1 + '"') : '';
 				
                 var cssClass = '';
                 var handledCssClass = org.ametys.forms.getHandledLayoutClass();
@@ -251,7 +254,7 @@
 				/layout="([^"]*)"/.test(s);
 				var layout = RegExp.$1;
 
-				return '&lt;form' + type + target + label + action + id + redirect + receiptTo + receiptFrom + receiptSubject + receiptBody + processingEmails + cssClass + ' layout="' + layout + '"&gt;';
+				return '&lt;form' + type + target + label + action + id + redirect + receiptTo + receiptFrom + receiptSubject + receiptBody + processingEmails + workflow + cssClass + ' layout="' + layout + '"&gt;';
 			}
 	);
 
@@ -376,15 +379,16 @@
 				var receiptSubject = (type != '' &amp;&amp; (/receipt_subject="([^"]*)"/.test(s))) ? (' form_receipt_subject="' + RegExp.$1 + '"') : '';
 				var receiptBody = (type != '' &amp;&amp; (/receipt_body="([^"]*)"/.test(s))) ? (' form_receipt_body="' + RegExp.$1 + '"') : '';
 				var redirect = (type != '' &amp;&amp; (/redirect="([^"]*)"/.test(s))) ? (' form_redirect="' + RegExp.$1 + '"') : '';
-
+				var formWorkflow = (type != '' &amp;&amp; (/workflow="([^"]*)"/.test(s))) ? (' form_workflow="' + RegExp.$1 + '"') : '';
+				
 				/layout="([^"]*)"/.test(s);
 				var layout = RegExp.$1;
-                
+
                 /class="([^"]*)"/.test(s);
                 var cssClass = RegExp.$1;
 
 				regexpSetContentForm.test(s);
-				return '&lt;div form="form" class="form' + (type != '' ? ' cmsForm' : '') + (cssClass != '' ? ' ' + cssClass : '') + '"' + type + id + target + processingEmails + redirect + receiptTo + receiptFrom + receiptSubject + receiptBody + action + label + ' layout="' + layout + '"&gt;';
+				return '&lt;div form="form" class="form' + (type != '' ? ' cmsForm' : '') + (cssClass != '' ? ' ' + cssClass : '') + '"' + type + id + target + processingEmails + redirect + receiptTo + receiptFrom + receiptSubject + receiptBody + action + label + formWorkflow + ' layout="' + layout + '"&gt;';
 			}
 	);
 
@@ -434,7 +438,7 @@
 				var multiple = / multiple="true"/.test(s) ? ' multiple="multiple"' : '';
 				var mandatory = / mandatory="true"/.test(s) ? ' mandatory="mandatory"' : '';
                 var placeholder = / placeholder="([^"]*)"/.test(s) ? ' form_placeholder="' + RegExp.$1 + '"' : '';
-                
+
 				var width = / width="([^"]*)"/.test(s) ? ' width="' + RegExp.$1 + '"' : '';
 				var height = / height="([^"]*)"/.test(s) ? ' height="' + RegExp.$1 + '"' : '';
 				
@@ -466,7 +470,7 @@
 				var width = / width="([^"]*)"/.test(s) ? ' width="' + RegExp.$1 + '"' : '';
 				var height = / height="([^"]*)"/.test(s) ? ' height="' + RegExp.$1 + '"' : '';
 				var placeholder = / placeholder="([^"]*)"/.test(s) ? ' form_placeholder="' + RegExp.$1 + '"' : '';
-                
+				
 				return org.ametys.forms._createHTML(name, id, 'class="form_captcha" captcha="captcha"' + width + height + placeholder);
 			}
 	);
Index: main/plugin-forms/resources/js/org/ametys/forms/Forms.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/Forms.i18n.js	(revision 40266)
+++ main/plugin-forms/resources/js/org/ametys/forms/Forms.i18n.js	(working copy)
@@ -45,9 +45,6 @@
 }
 
 
-
-
-
 /**
  * Insert an external form
  */
Index: main/plugin-forms/resources/js/org/ametys/forms/FormsActions.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/FormsActions.i18n.js	(revision 40266)
+++ main/plugin-forms/resources/js/org/ametys/forms/FormsActions.i18n.js	(working copy)
@@ -26,9 +26,9 @@
     org.ametys.forms.ChooseForm.act(org.ametys.ribbon.RibbonManager.getInstance().getControls()[controlId], org.ametys.forms.buttons.Action.showSelectedForm);
 }
 
-org.ametys.forms.buttons.Action.showSelectedForm = function(id, encodedLabel, label)
+org.ametys.forms.buttons.Action.showSelectedForm = function(id, encodedLabel, workflowName)
 {
-	org.ametys.uitool.ToolsManager.getInstance().openTool('uitool-form-entries', { id: id, encodedLabel: encodedLabel, label: label});
+	org.ametys.uitool.ToolsManager.getInstance().openTool('uitool-form-entries', { id: id, encodedLabel: encodedLabel, workflowName: workflowName});
 }
 
 org.ametys.forms.ChooseForm = {};
@@ -220,9 +220,9 @@
         
         var formId = selection[0].id;
         var formEncodedLabel = selection[0].get('encodedName');
-        var formLabel = selection[0].get('name');
+        var formWorkflowName = selection[0].get('workflowName');
         
-        org.ametys.forms.ChooseForm._callback(formId, formEncodedLabel, formLabel);
+        org.ametys.forms.ChooseForm._callback(formId, formEncodedLabel, formWorkflowName);
     }
     else
     {
@@ -296,7 +296,7 @@
     	                        org.ametys.servercomm.ServerComm.PRIORITY_MAJOR,
     	                        org.ametys.forms.entry.DeleteEntry._deleteEntriesCb, 
     	                        this, 
-    	                        [entryIds, formIds[0]]
+    	                        [entryIds, formIds[0], formTarget.getParameters().workflowName]
     	                    );
     	                    org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
     	                }
@@ -314,15 +314,23 @@
         return;
     }
     
-    var targets = [];
+    var targetParams = {};
+    targetParams.id = args[1];
     
-    var entryIds = args[0];
-    for (var i=0; i &lt; entryIds.length; i++)
+    if (args[2])
 	{
-    	targets.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('form-entry', {'id': entryIds[i], 'form': args[1] }));
+    	targetParams.workflowName = args[2];
 	}
     
-    var message = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_DELETED, null, targets);
+	var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('content-form', targetParams);
+	
+	var entryIds = args[0];
+	for (var i=0; i &lt; entryIds.length; i++)
+    {
+		target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('form-entry', {'id': entryIds[i]}));
+    }
+    
+    var message = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_DELETED, null, [target]);
     org.ametys.messagebus.MessageBus.getInstance().fireMessages([message]);
 }
 
@@ -488,3 +496,11 @@
     form.findField("tableName").setValue(request.getAttribute('tableName'));
     form.findField("request").setValue(request[org.ametys.servercomm.ServerComm.xmlTextContent]);
 }
+
+org.ametys.forms.OpenForm = function()
+{
+	var targets = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+	var contentFormTarget = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, function(target) {return target.getType() == 'content-form';});
+	
+	org.ametys.forms.buttons.Action.showSelectedForm(contentFormTarget.getParameters().id, contentFormTarget.getParameters().encodedLabel, contentFormTarget.getParameters().workflowName);
+}
Index: main/plugin-forms/resources/js/org/ametys/forms/FormsListTool.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/FormsListTool.i18n.js	(revision 40266)
+++ main/plugin-forms/resources/js/org/ametys/forms/FormsListTool.i18n.js	(working copy)
@@ -29,10 +29,11 @@
     var mapping = Ext.data.Record.create([
         {name: 'id', mapping: '@id'},
         {name: 'encodedLabel', mapping: '@encodedLabel'},
-        {name: 'name', mapping: 'name'},
+        {name: 'workflowName', mapping: '@workflowName'},
+		{name: 'name', mapping: 'name'},
         {name: 'contentName'},
         {name: 'contentId'},
-        {name: 'contentTitle'},
+        {name: 'contentTitle', mapping: 'contentTitle'},
         {name: 'answersAmount', mapping: 'answersAmount'},
         {name: 'lastAnswer', mapping: 'lastAnswer'}
     ]);
@@ -53,7 +54,7 @@
                 id: '@id'
             },
             mapping
-        ),
+        )
     });
     
     this._xmlStore.on('beforeload', this._onBeforeLoad, this);
@@ -130,8 +131,9 @@
 	var id = record.get('id');
 	var label = record.get('name');
 	var encodedLabel = record.get('encodedLabel');
+	var workflowName = record.get('workflowName');
 	
-	org.ametys.uitool.ToolsManager.getInstance().openTool ("uitool-form-entries", { id: id, encodedLabel: encodedLabel, label: label});
+	org.ametys.uitool.ToolsManager.getInstance().openTool ("uitool-form-entries", { id: id, encodedLabel: encodedLabel, label: label, workflowName: workflowName});
 }
 
 /**
@@ -152,11 +154,16 @@
     
     for (var i=0; i &lt; records.length; i++)
     {
-    	targets.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('content-form', {
-    		'id': records[i].data.id, 
-    		'encodedLabel': records[i].data.encodedLabel,
-    		'hasEntries': records[i].data.answersAmount > 0
-		}));
+    	var params = {};
+    	params.id = records[i].data.id;
+    	params.encodedLabel = records[i].data.encodedLabel;
+    	params.hasEntries = records[i].data.answersAmount > 0;
+    	if (records[i].data.workflowName)
+		{
+    		params.workflowName = records[i].data.workflowName;
+		}
+    	
+    	targets.push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('content-form', params));
     }
 	
     var event = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, targets);
@@ -333,6 +340,7 @@
     return '&lt;a class="link-content" href="javascript:org.ametys.uitool.ToolsManager.getInstance().openTool(\'uitool-content\', {\'id\': \'' + record.get('contentId') + '\'})"&gt;' + value + '&lt;/a&gt;';
 }
 
+
 /**
  * Listens when the store is about to load or reload.
  * @private
Index: main/plugin-forms/resources/js/org/ametys/forms/FormsTool.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/FormsTool.i18n.js	(revision 40266)
+++ main/plugin-forms/resources/js/org/ametys/forms/FormsTool.i18n.js	(working copy)
@@ -60,18 +60,25 @@
 
 org.ametys.forms.tool.FormsTool.prototype._sendCurrentSelection = function ()
 {
-	var records = this._resultGrid.getSelectionModel().getSelections();
+	var records = this._resultGrid.getSelectionModel().getSelections(); 
 	var hasEntries = this._resultGrid.getStore().getTotalCount() > 0;
 	
-	var formTarget = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('content-form', {
-			'id': this._formId, 
-			'encodedLabel': this._formEncodedLabel,
-			'hasEntries': hasEntries
-	});
+	var formTargetParams = {};
+	
+	formTargetParams.id = this._formId;
+	formTargetParams.encodedLabel = this._formEncodedLabel;
+	formTargetParams.hasEntries = hasEntries;
+	
+	if (this._workflowName)
+	{
+		formTargetParams.workflowName = this._workflowName;
+	}
+	
+	var formTarget = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('content-form', formTargetParams);
 	
     for (var i = 0; i &lt; records.length; i++)
     {
-		formTarget.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('form-entry', {'id': records[i].data.entryId, 'form': this._formId}));
+		formTarget.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('form-entry', {'id': records[i].data.entryId}));
     }
     
     var event = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED, null, [formTarget]);
@@ -80,10 +87,12 @@
 
 org.ametys.forms.tool.FormsTool.prototype._formId;
 org.ametys.forms.tool.FormsTool.prototype._formEncodedLabel;
+org.ametys.forms.tool.FormsTool.prototype._workflowName;
 org.ametys.forms.tool.FormsTool.prototype.setParams = function (params)
 {
 	this._formId = params.id;
 	this._formEncodedLabel = params.encodedLabel;
+	this._workflowName = params.workflowName;
 	
 	this.refresh();
 }
@@ -108,20 +117,29 @@
 {
     for (var i = messages.length - 1; i >= 0; i--)
     {
-        if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_DELETED)
+        if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_DELETED 
+           || messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_WORKFLOWCHANGED)
         {
             var targets = messages[i].getTargets();
-            var allTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(targets, function(target) { return target.getType() == "form-entry";});
+            var allContentFormTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(targets, function(target) { return target.getType() == "content-form";});
 
-            if (allTargets != null)
+            if (allContentFormTargets != null)
             {
-                for (var j=0; j &lt; allTargets.length; j++)
+                for (var j=0; j &lt; allContentFormTargets.length; j++)
                 {
-                    if (this._formId == allTargets[j].getParameters().form)
-                    {
-                    	this.outOfDate(false);
-                    	return;
-                    }
+                	// We need the content form target to have the form id
+                	var formId = allContentFormTargets[j].getParameters().id;
+                	
+                	// The workflow changing or deleted event type is only applicable on form entries
+            		var allFormEntriesTargets = org.ametys.messagebus.message.MessageTargetHelper.findAll(allContentFormTargets, function(target) { return target.getType() == "form-entry";});
+                	if (allFormEntriesTargets != null)
+            		{
+	                    if (this._formId == formId)
+	                    {
+	                    	this.outOfDate(false);
+	                    	return;
+	                    }
+                	}
                 }
             }
         }   
@@ -217,6 +235,8 @@
 		}
 	}
 	
+	fields.push({'name': 'workflowStep', mapping: '@workflowStep'});
+	
 	return fields;
 }
 
@@ -266,6 +286,18 @@
 		
 		columns.push(column);
 	}
+	
+	// Workflow id
+	columns.push({
+		header: "<i18n:text i18n:key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_WORKFLOW"/>",
+		id: "workflowStep",
+		dataIndex: "workflowStep",
+		renderer: org.ametys.forms.tool.FormsTool._renderWorkflow,
+		sortable: true,
+		hideable: true,
+		width: 120
+	});
+	
 	return columns;
 }
 
@@ -429,3 +461,15 @@
 	var entryId = record.data.entryId;
 	return "&lt;a class='download' href='" + getPluginDirectUrl('forms') + "/" + context.parameters.siteName + "/download/" + this._formId + "/" + entryId + "/" + fieldId + "/" + value + "'&gt;" + "&lt;img src='" + iconPath  + "'/&gt;" + value + "&lt;/a&gt;";
 }
+
+org.ametys.forms.tool.FormsTool._renderWorkflow = function (value, p, record, rowIndex, colIndex, store)
+{
+	if (value == null || value == '')
+	{
+		return "<i18n:text i18n:key="PLUGINS_FORMS_CHOOSE_SHOW_FORM_WORKFLOW_NO_WORKFLOW"/>";
+	}
+	else
+	{
+		return value;
+	}
+}
Index: main/plugin-forms/resources/js/org/ametys/forms/OrphanForms.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/OrphanForms.i18n.js	(revision 40266)
+++ main/plugin-forms/resources/js/org/ametys/forms/OrphanForms.i18n.js	(working copy)
@@ -13,7 +13,6 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-
 Ext.namespace('org.ametys.forms.administration.OrphanForms');
 
 org.ametys.forms.administration.OrphanForms.initialize = function (pluginName)
@@ -28,7 +27,11 @@
 		id:0,
         fields: [
            {name: 'name'},
-           {name: 'total'}
+           {name: 'total'},
+           {name: 'contentTitle'},
+           {name: 'siteName'},
+           {name: 'lastContributor'},
+           {name: 'stillExists'}
         ]
 	});
 	
@@ -43,8 +46,12 @@
         },
         
         columns: [
-            {header: "<i18n:text i18n:key='PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_NAME'/>", width: 200, dataIndex: 'name'},
-            {header: "<i18n:text i18n:key='PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_TOTAL'/>", width: 100, dataIndex: 'total'}
+            {header: "<i18n:text i18n:key='PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_NAME'/>", width: 260, dataIndex: 'name'},
+            {header: "<i18n:text i18n:key='PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_TOTAL'/>", width: 80, dataIndex: 'total'},
+            {header: "<i18n:text i18n:key='PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_CONTENT_TITLE'/>", width: 100, dataIndex: 'contentTitle'},
+            {header: "<i18n:text i18n:key='PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_SITE_NAME'/>", width: 80, dataIndex: 'siteName'},
+            {header: "<i18n:text i18n:key='PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_LAST_CONTRIBUTOR'/>", width: 110, dataIndex: 'lastContributor'},
+            {header: "<i18n:text i18n:key='PLUGINS_FORMS_ADMINISTRATOR_ORPHAN_FORMS_COL_STILL_EXISTS'/>", width: 100, dataIndex: 'stillExists', renderer: org.ametys.forms.administration.OrphanForms._renderBoolean}
         ]
     });
 	
@@ -72,11 +79,15 @@
 	});
 }
 
-org.ametys.forms.administration.OrphanForms.addElement = function (id, name, total)
+org.ametys.forms.administration.OrphanForms.addElement = function (id, name, total, contentTitle, siteName, lastContributor, stillExists)
 {
 	org.ametys.forms.administration.OrphanForms._listView.addElement(id, {
 		'name': name,
-		'total': total
+		'total': total,
+		'contentTitle': contentTitle,
+		'siteName': siteName,
+		'lastContributor': lastContributor,
+		'stillExists': stillExists
 	});	
 }
 
@@ -125,6 +136,18 @@
 	org.ametys.forms.administration.OrphanForms._actions.hide();
 }
 
+org.ametys.forms.administration.OrphanForms._renderBoolean = function (value, p, record)
+{
+	if (value == 'true')
+	{
+		return '&lt;img src="' + getPluginResourcesUrl('forms') + '/img/ok_16.png" title="' + "<i18n:text i18n:key="PLUGINS_FORMS_BOOLEAN_YES"/>" + '"/&gt;'
+	}
+	else
+	{
+		return "";
+	}
+}
+
 
 
 
Index: main/plugin-forms/resources/js/org/ametys/forms/workflow/FormEntriesWorkflowMenu.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/workflow/FormEntriesWorkflowMenu.i18n.js	(revision 0)
+++ main/plugin-forms/resources/js/org/ametys/forms/workflow/FormEntriesWorkflowMenu.i18n.js	(revision 0)
@@ -0,0 +1,537 @@
+/*
+ *  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.forms.workflow');
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu = function(id, configuration, pluginName) 
+{
+	org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.superclass.constructor.call(this, id, configuration, pluginName);
+};
+
+Ext.extend(org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu, org.ametys.ribbon.control.button.ActionButton, {});
+
+/**
+ * @private
+ * @property {Object} _lastParams The params received on the last refresh
+ */
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype._lastParams;
+
+/**
+ * @private
+ * @property {String[]} _currentEntryIds The ids of the entries currently selected 
+ */
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype._currentEntryIds;
+
+/**
+ * @private
+ * @propery {String} _worklowName the name of the workflow of this menu
+ */
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype._workflowName;
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype.createControl = function(size, colspan)
+{
+	var element = org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.superclass.createControl.call(this, size, colspan);
+	element._menuItems = [];
+	
+	org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._workflowName = this.getConfiguration()['workflow-name'];
+	
+	var menuCount = parseInt(this.getConfiguration()["menu-size"]); 
+	if (menuCount &gt; 0)
+	{
+		var items = [];
+		for (var i=0;  i &lt; menuCount; i++)
+		{
+			var actionId = this.getConfiguration()["menu-" + i];
+			element._menuItems[actionId] = new Ext.menu.Item ({
+				actionId: actionId,
+				text: this.getConfiguration()["menu-" + actionId + "-label"],
+				tooltip: this.getConfiguration()["menu-" + actionId + "-description"],
+				handler: eval(this.getConfiguration()["menu-" + actionId + "-action"]),
+				hasComment: this.getConfiguration()["menu-" + actionId + "-comment"] == 'true',
+				hasFurtherInfo: this.getConfiguration()["menu-" + actionId + "-further-information"] == 'true',
+				multiselectionEnabled: this.getConfiguration()["menu-" + actionId + "-enable-multiselection"] == 'true',
+				workflowActionPlugin: this.getConfiguration()["menu-" + actionId + "-actionPlugin"] || 'forms',
+				workflowActionUrl: this.getConfiguration()["menu-" + actionId + "-actionUrl"] || 'do-action',
+				ribbonControl: this
+			}); 
+			items.push(element._menuItems[actionId]);
+		}
+		
+		element.menu = new Ext.menu.Menu({
+			items: items
+		});
+	}
+	
+	return element;
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._testTarget = function (target)
+{
+	return target.getType() == 'form-entry';
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._testContentFormTarget = function (target)
+{
+	return target.getType() == 'content-form' &amp;&amp; target.getParameters().workflowName == org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._workflowName;
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype.onMessage = function (messages)
+{
+	var alreadyRefreshed = false;
+	
+	// SELECTION
+	for (var i=messages.length - 1; i &gt;= 0; i--)
+	{
+		if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_SELECTIONCHANGED)
+		{
+			var targets = messages[i].getTargets();
+			var contentFormTarget = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._testContentFormTarget.createDelegate(this));
+			
+			if (contentFormTarget)
+			{
+				var allFormEntryTargetsObject = org.ametys.messagebus.message.MessageTargetHelper.findAll([contentFormTarget], org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._testTarget.createDelegate(this))
+				var allFormEntryTargetsIds = this._convertToId(allFormEntryTargetsObject);
+			
+				// No content selected
+				if (allFormEntryTargetsIds.length == 0)
+				{
+					this.getElements().each(
+						function(element) 
+						{ 
+							element.setDisabled(true); 
+							element.toggle(false);
+						}
+					);
+					this._updateTip(this.getConfiguration()["noselection-description"]);
+					
+					this._currentEntryIds = [];
+				}
+				// A single target selected or multiselection is enabled
+				else if (allFormEntryTargetsIds.length == 1 || this.getConfiguration()["enable-multiselection"] == "true")
+				{
+					if (!this._areTheSame(this._currentEntryIds, allFormEntryTargetsIds))
+					{
+						this._currentEntryIds = allFormEntryTargetsIds;
+					
+						this._updateTip(this.getConfiguration()["refreshing-description"]);
+						
+						this.getElements().each(
+							function(element) 
+							{
+								element.refreshing();
+								element.setDisabled(true); 
+								element.toggle(false);
+							}
+						);
+						
+						org.ametys.ribbon.RibbonManager.getInstance().refreshElement('control', this, {formId: contentFormTarget.getParameters().id, entryIds: allFormEntryTargetsIds});
+					}
+				}
+				// Too many entries selected
+				else
+				{
+					this.getElements().each(function(element) { element.setDisabled(true); });
+					this._updateTip(this.getConfiguration()["multiselection-disabled-description"]);
+					
+					this._currentEntryIds = [];
+				}
+				
+				return;
+			}
+		}
+	}
+	
+	// WORKFLOW
+	for (var i=0; i &lt; messages.length; i++)
+	{
+		if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_WORKFLOWCHANGED
+			|| messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_WORKFLOWCHANGING)
+		{
+			var targets = messages[i].getTargets();
+			var contentFormTarget = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._testContentFormTarget.createDelegate(this));
+			
+			if (contentFormTarget)
+			{
+				var allFormEntryTargetsObject = org.ametys.messagebus.message.MessageTargetHelper.findAll([contentFormTarget], org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._testTarget.createDelegate(this))
+				var allFormEntryTargetsIds = this._convertToId(allFormEntryTargetsObject);
+				
+				var currentSelection = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+				
+				for (var t=0; currentSelection != null &amp;&amp; t &lt; allFormEntryTargetsIds.length; t++)
+				{
+					var currentSelectionIds = this._convertToId(org.ametys.messagebus.message.MessageTargetHelper.findAll(currentSelection, org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._testTarget.createDelegate(this) ));
+					
+					for (var c=0; c &lt; currentSelectionIds.length; c++)
+					{
+						if (currentSelectionIds[c] == allFormEntryTargetsIds[t])
+						{
+							this._updateTip(this.getConfiguration()["refreshing-description"]);
+							
+							this.getElements().each(
+								function(element) 
+								{ 
+									element.setDisabled(true); 
+									element.toggle(false);
+									element.refreshing();
+								}
+							);
+							
+							if (messages[i].getType().getName() == org.ametys.ribbon.RibbonManager.EVENTTYPE_WORKFLOWCHANGED)
+							{
+								if (alreadyRefreshed == false)
+								{
+									alreadyRefreshed = true;
+									var formEntryTarget = org.ametys.messagebus.message.MessageTargetHelper.findFirst(targets, org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._testTarget.createDelegate(this));
+									org.ametys.ribbon.RibbonManager.getInstance().refreshElement('control', this, {formId: contentFormTarget.getParameters().id, entryIds: currentSelectionIds});
+								}
+							}
+							
+							return;
+						}
+					}
+				}
+			}
+		}
+	}
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype._updateTip = function(tooltip)
+{
+	var begin = this.getConfiguration()["default-description"];
+	if (begin == null)
+	{
+		begin = "";
+	}
+	if (tooltip == null)
+	{
+		tooltip = "";
+	}
+	
+	var finalTooltip = begin + ((begin != "" &amp;&amp; tooltip != "") ? "&lt;br/&gt;&lt;br/&gt;" : "") + tooltip
+	
+	this.updateTooltip(finalTooltip, context.contextPath + this.getConfiguration()['icon-medium']);
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype.configurationError = function(response)
+{
+	this.getElements().each(
+		function(element) 
+		{ 
+			element.setDisabled(true); 
+			element.stopRefreshing();
+		}
+	);
+	this._updateTip(this.getConfiguration()["noselection-description"]);
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype._areTheSame = function (tab1, tab2)
+{
+	if (tab1 == tab2)
+	{
+		return true;
+	}
+
+	if (tab1 == null || tab2 == null || tab1.length != tab2.length)
+	{
+		return false;
+	}	
+	
+	for (var i=0; i &lt; tab1.length; i++)
+	{
+		if (tab2.indexOf(tab1[i]) == -1)
+		{
+			return false;
+		}
+	}
+	
+	return true;
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype._convertToId = function (targets)
+{
+	var targetsIds = [];
+	for (var i = 0; i &lt; targets.length; i++)
+	{
+		targetsIds.push(targets[i].getParameters()['id']);
+	}
+	return targetsIds;
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype.configurationChanged = function(params)
+{
+	this.getElements().each(function(element){element.stopRefreshing();});
+	
+	this._lastParams = params;
+	
+	var count = parseInt(params["entry-count"]);
+	var tooltip = "";
+	
+	// Action map. Key is action id, Value is an array of content name
+	var menuCount = parseInt(this.getConfiguration()["menu-size"]); 
+	var action = {};
+	for (var i=0;  i &lt; menuCount; i++)
+	{
+		var actionId = this.getConfiguration()["menu-" + i];
+		action[actionId] = [];
+	}
+	
+	var hasActionEnabled = false;
+	var noMatchingEntries = false;
+	
+	if (count &gt; 0)
+	{
+		tooltip += count + ' ' + this.getConfiguration()["entryselected-description"];
+
+		for (var i=0; i &lt; count; i++)
+		{
+			// loop on action for this entry
+			if (menuCount &gt; 0)
+			{
+				for (var actionId in action)
+				{
+					var canDoAction = "true" == params["entry-" + i + "-action-" + actionId];
+					if (canDoAction)
+					{
+						action[actionId].push(params["entry-" + i + "-id"])
+					}
+				}
+			}
+		}
+		
+		for (var actionId in action)
+		{
+			this.getElements().each(
+				function(element) 
+				{
+					var menuItem = element._menuItems[actionId]; 
+					
+					var disable = count &gt; 1 &amp;&amp; !menuItem.multiselectionEnabled;
+					menuItem.setDisabled(disable || action[actionId].length == 0);
+					menuItem.entryIds = action[actionId];
+					hasActionEnabled = hasActionEnabled || action[actionId].length != 0;
+				}
+			);
+		}
+
+		if (!hasActionEnabled)
+		{
+			tooltip += '&lt;br/&gt;&lt;br/&gt;';
+			tooltip += this.getConfiguration()["noaction-available-description"];
+		}
+		
+	}
+	else
+	{
+		tooltip = this.getConfiguration()["nomatch-description"];
+		noMatchingEntries = true;
+	}
+	
+	this._updateTip(tooltip);
+	this.getElements().each(
+		function(element) 
+		{ 
+			element.allowDepress = false;
+			element.toggle(count &gt; 0);
+			element.setDisabled((!hasActionEnabled &amp;&amp; menuCount &gt; 0) || noMatchingEntries); 
+		}
+	);
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.act = function()
+{
+	// nothing
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.menuAct = function(item, event)
+{
+    if (item &amp;&amp; item.hasComment)
+    {
+		// Show the comment dialog box to the user 
+		org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.showCommentBox(item);
+    }
+    else
+    {
+    	// Direct call to the workflow action, without comment.
+    	org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.doMenuAct(item, '');
+    }
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.doMenuAct = function(item, comment)
+{
+    var params = item.ribbonControl._lastParams;
+    var currentSelection = org.ametys.ribbon.RibbonManager.getInstance().getCurrentSelectionTargets();
+    var formTarget = org.ametys.messagebus.message.MessageTargetHelper.findFirst(currentSelection, org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._testContentFormTarget);
+    var formId = formTarget.getParameters().id;
+    
+	if (item &amp;&amp; item.hasFurtherInfo)
+	{
+	    // Show the further information dialog box to the user 
+		org.ametys.plugins.forms.workflow.FurtherInformationDialog(formId, item, comment, org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.doMenuAct2);
+	}
+	else
+	{
+		org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.doMenuAct2(formId, item, comment);
+	}
+	
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.doMenuAct2 = function(formId, item, comment, furtherInfo)
+{
+	var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('content-form', {
+			'id': formId, 
+			'workflowName': org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._workflowName
+	});
+	
+	var  entryIds = item.entryIds;
+	for (var i=0; i &lt; entryIds.length; i++)
+	{
+		target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('form-entry', {'id': entryIds[i]}));
+    }
+	
+	
+	var plugin = item.workflowActionPlugin;
+	var url = item.workflowActionUrl + '/' + item.actionId;
+	
+	org.ametys.servercomm.ServerComm.getInstance().suspend();
+	
+	for (var i=0; i &lt; entryIds.length; i++)
+	{
+		var params = {};
+		params.comment = comment;
+		params.formId = formId;
+		params.furtherInfo = Ext.util.JSON.encode(furtherInfo);
+		params.entryId = entryIds[i];
+		
+		var serverMessage = new org.ametys.servercomm.ServerMessage(plugin, url, params, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, item.ribbonControl.workflowActionCallback, item.ribbonControl, {entryId: entryIds[i], max: entryIds.length, formId: formId});
+		org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+	}
+	org.ametys.servercomm.ServerComm.getInstance().restart();
+	
+	var event = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_WORKFLOWCHANGING, null, [target]);
+	org.ametys.messagebus.MessageBus.getInstance().fireMessages([event]);
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype.cumulate = 0;
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype.cumulatedIds = [];
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.prototype.workflowActionCallback = function(response, args)
+{
+	this.cumulatedIds.push(args.entryId);
+
+	if (org.ametys.servercomm.ServerComm.handleBadResponse("<i18n:text i18n:key='PLUGINS_FORMS_WORKFLOW_ERROR_DESC'/>", response, "org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu"))
+	{
+		// we have to continue regardless of the result, because we have to rollback the workflowchanging state
+	}
+
+	this.cumulate++;
+	
+	if (this.cumulate == args.max)
+	{
+		var target = org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('content-form', {
+				'id': args.formId, 
+				'workflowName': org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu._workflowName
+		});
+		
+		// Send the event for all contents so discard the next callback
+		if (this.cumulatedIds.length &gt; 0)
+		{
+			for (var i = 0; i &lt; this.cumulatedIds.length; i++)
+			{
+				target.getSubTargets().push(org.ametys.messagebus.bus.MessageBuilder.getInstance().createTarget('form-entry', {'id': this.cumulatedIds[i]}));
+			}
+	    }
+			
+		var event = org.ametys.messagebus.bus.MessageBuilder.getInstance().createMessage(org.ametys.ribbon.RibbonManager.EVENTTYPE_WORKFLOWCHANGED, null, [target]);
+		org.ametys.messagebus.MessageBus.getInstance().fireMessages([event]);
+		
+		this.cumulatedIds = [];
+		this.cumulate = 0;
+	}
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.showCommentBox = function(item)
+{
+    org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.item = item;
+    
+    if (org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentDialog == null)
+    {
+        org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentForm = new Ext.form.FormPanel({
+            region: 'center',
+            border: false,
+            autoScroll: true,
+            
+            defaultType: 'textfield',
+            items: [
+                new org.ametys.HtmlContainer({
+                    cls: 'workflow-comment-dialog-help',
+                    html: "&lt;p&gt;<i18n:text i18n:key='PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_MESSAGE'/>&lt;/p&gt;"
+                }),
+                new org.ametys.form.TextAreaField({
+                    name: 'comment',
+                    desc: "<i18n:text i18n:key='PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_TEXTAREA_DESC'/>",
+                    width: 360,
+                    height: 140
+                })
+            ],
+            
+            labelWidth: 5,
+            html: ''
+        });
+        
+        var id = Ext.id();
+        org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentDialog = new org.ametys.DialogBox({
+            layout: 'fit',
+            
+            title: "<i18n:text i18n:key='PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_TITLE'/>",
+            icon: getPluginResourcesUrl('forms') + '/img/form-entry_16.png',
+            cls: 'workflow-comment-dialog',
+            
+            width: 450,
+            height: 250,
+            autoScroll: false,
+            
+            items: [ org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentForm ],
+            
+            defaultButton: id,
+            closeAction: 'hide',
+            buttonAlign: 'center',
+            buttons : [{
+                text: "<i18n:text i18n:key='PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_OK'/>",
+                handler: org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentCallback,
+                id: id
+            },{
+                text: "<i18n:text i18n:key='PLUGINS_FORMS_WORKFLOW_COMMENTS_DIALOG_CANCEL'/>",
+                handler: org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentCancel
+            }]
+        });
+    }
+    
+    // Empty the comment textarea.
+    org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentForm.getForm().findField('comment').setValue('');
+    
+    org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentDialog.show();
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentCallback = function(button, event)
+{
+    var values = org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentForm.getForm().getValues();
+    org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentDialog.hide();
+    org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.doMenuAct(org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.item, values.comment);
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentCancel = function(button, event)
+{
+    org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.commentDialog.hide();
+}
Index: main/plugin-forms/resources/js/org/ametys/forms/workflow/FormEntriesWorkflowTab.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/workflow/FormEntriesWorkflowTab.js	(revision 0)
+++ main/plugin-forms/resources/js/org/ametys/forms/workflow/FormEntriesWorkflowTab.js	(revision 0)
@@ -0,0 +1,73 @@
+/*
+ *  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.forms.workflow');
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab = function(id, configuration, pluginName, tabPanel) 
+{
+	org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab.superclass.constructor.call(this, id, configuration, pluginName, tabPanel);
+	
+	if (configuration['workflow-name'] != null)
+	{
+	    this._workflowNameRegexp = new RegExp(configuration['workflow-name']);
+	}
+}
+
+Ext.extend(org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab, org.ametys.cms.ribbon.tab.SelectionTab, {});
+
+/**
+ * @private
+ * @property {RegExp} _workflowNameRegexp The workflow name regular expression to match
+ */
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab.prototype._workflowNameRegexp;
+
+/**
+ * @private
+ * Test a target. Does it match ?
+ * @return {org.ametys.messagebus.message.MessageTarget} The target to check
+ */
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab.prototype._testTarget = function(target)
+{
+	var matchSelection = org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab.superclass._testTarget.call(this, target);
+	if (!matchSelection)
+	{
+		return false;
+	}
+	
+	var contentFormTarget = org.ametys.messagebus.message.MessageTargetHelper.findFirst([target], this._testContentFormTarget.createDelegate(this));
+	if (contentFormTarget)
+	{
+		var workflowName = contentFormTarget.getParameters().workflowName;
+		if (this._workflowNameRegexp != null && !this._workflowNameRegexp.test(workflowName))
+		{
+			// Wrong tab
+			return false;
+		}
+		
+		if (this._workflowNameRegexp == null && workflowName)
+		{
+			// Do not display the tab with no workflow in that case
+			return false;
+		}
+	}
+	
+	return true;
+}
+
+org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab.prototype._testContentFormTarget = function(target)
+{
+	return target.getType() == 'content-form';
+}
\ No newline at end of file
Index: main/plugin-forms/resources/js/org/ametys/forms/workflow/FormWorkflowButton.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/workflow/FormWorkflowButton.i18n.js	(revision 0)
+++ main/plugin-forms/resources/js/org/ametys/forms/workflow/FormWorkflowButton.i18n.js	(revision 0)
@@ -0,0 +1,82 @@
+/*org.ametys.plugins.forms.workflow
+ *  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.forms.workflow');
+
+/**
+ * This widget list the available workflows for a form.
+ */
+org.ametys.plugins.forms.workflow.FormWorkflowButton = function(id, configuration, pluginName)
+{
+	org.ametys.plugins.forms.workflow.FormWorkflowButton.superclass.constructor.call(this, id, configuration, pluginName);
+}
+
+Ext.extend(org.ametys.plugins.forms.workflow.FormWorkflowButton, org.ametys.cms.control.ComboBoxField, {});
+
+org.ametys.plugins.forms.workflow.FormWorkflowButton._applyWorkflow = function (combo, record, index)
+{
+	var newValue = record.data.id;
+	if (org.ametys.plugins.forms.workflow.FormWorkflowButton._currentValue != newValue)
+	{
+		Ext.Msg.confirm(
+				"<i18n:text i18n:key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_CHANGE_DIALOG_TITLE"/>", 
+				"<i18n:text i18n:key="PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_CHANGE_DIALOG_TEXT"/>", 
+				function (answer) {
+					if (answer != 'yes')
+					{
+						combo.setValue(org.ametys.plugins.forms.workflow.FormWorkflowButton._currentValue);
+					}
+					else
+					{
+						org.ametys.plugins.forms.workflow.FormWorkflowButton._currentNode.setAttribute("form_workflow", newValue);
+						org.ametys.plugins.forms.workflow.FormWorkflowButton._currentValue = newValue;
+					}
+				}, 
+				this
+		);
+	}
+}
+
+org.ametys.plugins.forms.workflow.FormWorkflowListener = function(ribbonCombo, target, node)
+{
+	var off = target == null || node == null;
+	if (off)
+	{
+		ribbonCombo.getElements().each(function(combo) {combo.setDisabled(true);});
+		return;
+	}
+	
+	var formNode = org.ametys.forms._getForm(node);
+	if (formNode != null)
+	{
+		org.ametys.plugins.forms.workflow.FormWorkflowButton._currentNode = formNode;
+	
+		ribbonCombo.getElements().each(
+			function(combo) 
+			{
+				combo.setDisabled(false);
+	
+				var formWorkflow = formNode.getAttribute("form_workflow");
+				var value = formWorkflow != null ? formWorkflow : combo.getStore().getRange()[0].data.id;
+				formNode.setAttribute("form_workflow", value);
+				
+				combo.setValue(value);
+	
+				org.ametys.plugins.forms.workflow.FormWorkflowButton._currentValue = value;
+			}
+		);
+	}
+}
Index: main/plugin-forms/resources/js/org/ametys/forms/workflow/FurtherInformationDialog.i18n.js
===================================================================
--- main/plugin-forms/resources/js/org/ametys/forms/workflow/FurtherInformationDialog.i18n.js	(revision 0)
+++ main/plugin-forms/resources/js/org/ametys/forms/workflow/FurtherInformationDialog.i18n.js	(revision 0)
@@ -0,0 +1,209 @@
+/*
+ *  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.forms.workflow');
+
+/**
+ * Create the further information dialog box.
+ */
+org.ametys.plugins.forms.workflow.FurtherInformationDialog = function(formId, item, comment, callback)
+{
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._callback = callback;
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._formId = formId;
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._item = item;
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._comment = comment;
+	
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._initialize();
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._form.getForm().reset();
+	
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._setValues(item.entryIds[0]);
+	
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._box.show();
+}
+
+org.ametys.plugins.forms.workflow.FurtherInformationDialog._initialize = function()
+{
+	if (org.ametys.plugins.forms.workflow.FurtherInformationDialog._box != null)
+	{
+		return;
+	}
+	
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldDoSend = new Ext.form.Checkbox ({
+		 fieldLabel: "<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_DO_SEND"/>",
+		 name: "do-send",
+		 width: 285,
+		 allowBlank: false,
+		 listeners: {'check': org.ametys.plugins.forms.workflow.FurtherInformationDialog._onSendMailCheck},
+		 value: false
+	});
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldFrom= new Ext.form.TextField ({
+		 fieldLabel: "<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_FROM_FULLNAME"/>",
+		 name: "from-fullname",
+		 width: 285,
+		 allowBlank: false,
+		 value: "",
+		 disabled: true
+	});
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldTo = new Ext.form.ComboBox ({
+		 fieldLabel: "<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_TO"/>",
+		 name: "to",
+		 width: 285,
+		 mode: 'local',
+		 forceSelection : true,
+		 triggerAction: 'all',		
+		 editable: false,
+		 valueField: 'id',
+ 		 displayField: 'text',
+ 		 disabled: true,
+ 		 allowBlank: false,
+		 
+		 store: new Ext.data.ArrayStore({
+	        fields: [
+	            'id',
+	            'text'
+	        ],
+	        data: []
+		 }),
+		 value: ""
+	});
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldSubject = new Ext.form.TextField ({
+		 fieldLabel: "<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_SUBJECT"/>",
+		 name: "subject",
+		 width: 285,
+		 allowBlank: false,
+		 disabled: true,
+		 value: ""
+	});
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldBody = new Ext.form.TextArea ({
+		 fieldLabel: "<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_FIELD_BODY"/>",
+		 name: "body",
+		 width: 285,
+		 allowBlank: false,
+		 height: 200,
+		 disabled: true,
+		 value: ""
+	});
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._form = new Ext.FormPanel( {
+		formId : 'further-info-form',
+		labelWidth: 90,
+		border: false,
+		items: [ org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldDoSend,
+		         org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldFrom,
+		         org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldTo, 
+		         org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldSubject, 
+		         org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldBody ]
+	});
+	
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._box = new org.ametys.DialogBox ({
+		title: "<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_TITLE"/>",
+		icon: getPluginResourcesUrl("forms") + "/img/edition/mailreceipt_16.png",
+
+		cls: 'text-dialog',
+		width: 425,
+		height: 400,
+		
+		items: [ org.ametys.plugins.forms.workflow.FurtherInformationDialog._form ],
+			
+		closeAction: 'hide',
+		defaultButton: org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldBody,
+		buttons : [ 
+		    {
+				text : "<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_OK"/>",
+				handler: org.ametys.plugins.forms.workflow.FurtherInformationDialog._ok
+		    }, 
+		    {
+				text : "<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_CANCEL"/>",
+				handler: org.ametys.plugins.forms.workflow.FurtherInformationDialog._cancel
+		    }
+		]
+	});
+}
+
+org.ametys.plugins.forms.workflow.FurtherInformationDialog._setValues = function()
+{
+	var entryId = org.ametys.plugins.forms.workflow.FurtherInformationDialog._item.entryIds[0];
+	var formId = org.ametys.plugins.forms.workflow.FurtherInformationDialog._formId;
+	
+	// Server call
+	var serverMessage = new org.ametys.servercomm.ServerMessage('forms', 'further-information', {formId: formId, entryId: entryId}, org.ametys.servercomm.ServerComm.PRIORITY_MAJOR, org.ametys.plugins.forms.workflow.FurtherInformationDialog._setValuesCb, this, null);
+	org.ametys.servercomm.ServerComm.getInstance().send(serverMessage);
+}
+
+org.ametys.plugins.forms.workflow.FurtherInformationDialog._setValuesCb = function(response, args)
+{
+	var furtherInfo = response.selectSingleNode("further-information");
+
+	var currentUser = furtherInfo.selectSingleNode("current-user");
+	var currentUserFullName = currentUser.getAttribute("fullname");
+	
+	var senderStandardizedName = currentUserFullName + " &lt;" + currentUser.getAttribute("email") + "&gt;";
+	
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldFrom.setValue(senderStandardizedName);
+	
+	var emails = furtherInfo.selectSingleNode("emails").selectNodes("email");
+
+	var emailsInfo = [];
+	Ext.each(emails, function(emailNode) {
+		emailsInfo.push([emailNode.getAttribute("value"), emailNode.getAttribute("displayValue")]);
+	});
+	
+	var hasEmails = !Ext.isEmpty(emailsInfo);
+	
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldDoSend.setValue(hasEmails);
+	
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldTo.getStore().removeAll();
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldTo.getStore().loadData(emailsInfo);
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldTo.setValue(hasEmails ? emailsInfo[0][0] : null);
+	
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldSubject.setValue("<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_SUBJECT_DEFAULT"/>");
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldBody.setValue("<i18n:text i18n:key="PLUGINS_FORMS_NEED_COMPLEMENTS_DIALOG_BODY_DEFAULT"/>" + currentUserFullName);
+}
+
+org.ametys.plugins.forms.workflow.FurtherInformationDialog._onSendMailCheck = function (field, checked)
+{
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldTo.setDisabled(!checked);
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldSubject.setDisabled(!checked);
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldBody.setDisabled(!checked);
+}
+
+org.ametys.plugins.forms.workflow.FurtherInformationDialog._ok = function()
+{
+	if (org.ametys.plugins.forms.workflow.FurtherInformationDialog._form.getForm().isValid())																									
+	{
+		var values = {};
+
+		var sendMail = org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldDoSend.getValue();
+		if (sendMail)
+		{
+			values.recipient = org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldTo.getValue();
+			values.subject = org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldSubject.getValue();
+			values.body = org.ametys.plugins.forms.workflow.FurtherInformationDialog._fieldBody.getValue();
+		}
+		
+		org.ametys.plugins.forms.workflow.FurtherInformationDialog._box.hide();
+		
+		org.ametys.plugins.forms.workflow.FurtherInformationDialog._callback(
+			org.ametys.plugins.forms.workflow.FurtherInformationDialog._formId,
+			org.ametys.plugins.forms.workflow.FurtherInformationDialog._item,
+			org.ametys.plugins.forms.workflow.FurtherInformationDialog._comment,
+			values
+		);
+	}
+}
+
+org.ametys.plugins.forms.workflow.FurtherInformationDialog._cancel = function()
+{
+	org.ametys.plugins.forms.workflow.FurtherInformationDialog._box.hide();
+}
\ No newline at end of file
Index: main/plugin-forms/sitemap.xmap
===================================================================
--- main/plugin-forms/sitemap.xmap	(revision 40266)
+++ main/plugin-forms/sitemap.xmap	(working copy)
@@ -29,6 +29,9 @@
             <map:generator name="forms-list" src="org.ametys.plugins.forms.data.FormsListGenerator" logger="org.ametys.plugins.forms.data.FormsListGenerator"/>
         
             <map:generator name="administrator-orphan-forms" src="org.ametys.plugins.forms.OrphanFormsGenerator" logger="org.ametys.plugins.forms.OrphanFormsGenerator"/>
+            
+            <map:generator name="form-workflows-ribbon" src="org.ametys.plugins.forms.workflow.FormEntriesWorkflowsGenerator" logger="org.ametys.plugins.forms.workflow.FormEntriesWorkflowsGenerator"/>
+            <map:generator name="further-info" src="org.ametys.plugins.forms.workflow.FurtherInformationGenerator" logger="org.ametys.plugins.forms.workflow.FurtherInformationGenerator"/>
         </map:generators>
         
         <map:actions>
@@ -42,6 +45,7 @@
             
             <map:action name="administrator-is-system-user" src="org.ametys.runtime.plugins.core.right.actions.IsAdminAction" logger="org.ametys.runtime.plugins.web.administrator.sitemap.action.isAdmin"/>
             <map:action name="delete-form" src="org.ametys.plugins.forms.DeleteFormTableAction" logger="org.ametys.plugins.forms.DeleteFormTableAction"/>
+            <map:action name="do-action" src="org.ametys.plugins.forms.workflow.FormEntriesWorkflowAction" logger="org.ametys.plugins.forms.workflow.FormEntriesWorkflowAction"/>
         </map:actions>
         
         <map:serializers>
@@ -70,6 +74,10 @@
                 <map:generate type="content-forms"/>
                 <map:serialize type="xml"/>
             </map:match>
+            <map:match pattern="ribbon-part">
+                <map:generate type="form-workflows-ribbon"/>
+                <map:serialize type="xml"/>
+            </map:match>
         </map:pipeline>
         
 		<map:pipeline>
@@ -241,6 +249,39 @@
                 <map:serialize type="xml"/>
             </map:match>
             
+            <map:match pattern="further-information">
+                <map:generate type="further-info"/>
+                <map:serialize type="xml"/>    
+            </map:match>
+            
+            <map:match pattern="do-action/*">
+                <map:act type="do-action">
+                    <map:parameter name="actionId" value="{1}"/>
+                    <map:parameter name="entryId" value="{parent-context-attr:entryId}"/>
+                    <map:parameter name="formId" value="{parent-context-attr:formId}"/>
+                    <map:parameter name="furtherInfo" value="{parent-context-attr:furtherInfo}"/>
+                </map:act>
+                <map:generate type="action-result"/>
+                <map:serialize type="xml" status-code="500"/>
+            </map:match>
+            
+            <map:match pattern="resources_workflow/**">
+                <map:read src="context://WEB-INF/param/workflow_resources/{1}"/>
+            </map:match>
+            
+            <map:match pattern="ribbon.xml">
+                <map:aggregate element="root" label="xml">
+                    <map:part src="cms-ribbon.xml"/>
+                    <map:part src="cocoon:/ribbon-part"/>
+                </map:aggregate>
+                <map:transform type="xslt" src="stylesheets/ribbon/ribbon.xsl" />
+                <map:transform type="i18n">
+                    <map:parameter name="locale" value="{locale:locale}"/>
+                    <map:parameter name="plugin" value="{request-attr:pluginName}"/>
+                </map:transform>
+                <map:serialize type="xml"/>
+            </map:match>
+            
             <!-- ********************* ADMIN ************************* -->
             <map:match pattern="administrator/**">
                 <map:act type="administrator-is-system-user"/>
Index: main/plugin-forms/src/org/ametys/plugins/forms/DeleteFormEntriesAction.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/DeleteFormEntriesAction.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/DeleteFormEntriesAction.java	(working copy)
@@ -15,6 +15,7 @@
  */
 package org.ametys.plugins.forms;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -29,23 +30,24 @@
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang.StringUtils;
 
-import org.ametys.plugins.forms.jcr.FormPropertiesManager;
 import org.ametys.plugins.forms.table.FormTableManager;
+import org.ametys.plugins.forms.workflow.FormEntriesWorkflow;
+import org.ametys.plugins.workflow.store.JdbcWorkflowStore;
 
 /**
  * Action for deleting one, multiple or all entries for a form.
  */
 public class DeleteFormEntriesAction extends ServiceableAction
 {
-    private FormPropertiesManager _formPropertiesManager;
     private FormTableManager _formTableManager;
+    private FormEntriesWorkflow _formEntriesWorkflow;
     
     @Override
     public void service(ServiceManager serviceManager) throws ServiceException
     {
         super.service(serviceManager);
-        _formPropertiesManager = (FormPropertiesManager) serviceManager.lookup(FormPropertiesManager.ROLE);
         _formTableManager = new FormTableManager();
+        _formEntriesWorkflow = (FormEntriesWorkflow) serviceManager.lookup(FormEntriesWorkflow.ROLE);
     }
     
     @SuppressWarnings("unchecked")
@@ -65,23 +67,29 @@
         
         for (String formId : formIds)
         {
-            Form form = _formPropertiesManager.getForm(siteName, formId);
-            if (form == null)
-            {
-                throw new ProcessingException("The form of ID '" + formId + " can't be found in the site '" + siteName + "'.");
-            }
-            
+            List<Integer> workflowIds = new ArrayList<Integer> (); 
+            List<Integer> entriesIds = (List<Integer>) jsParameters.get("ids");
             if (mode.equals("delete"))
             {
-                List<Integer> entriesIds = (List<Integer>) jsParameters.get("ids");
                 for (Integer entryId : entriesIds)
                 {
-                    _formTableManager.deleteSubmission(form, entryId);
+                    workflowIds.addAll(_formTableManager.getWorkflowIds(formId, entryId));
+                    _formTableManager.deleteSubmission(formId, entryId);
                 }
             }
             else if (mode.equals("clear"))
             {
-                _formTableManager.deleteSubmission(form, null);
+                workflowIds.addAll(_formTableManager.getWorkflowIds(formId, null));
+                _formTableManager.deleteSubmission(formId, null);
+            }
+            
+            // Delete the corresponding workflow instances and their history
+            JdbcWorkflowStore workflowStore = (JdbcWorkflowStore) _formEntriesWorkflow.getConfiguration().getWorkflowStore();
+            
+            for (Integer workflowId : workflowIds)
+            {
+                workflowStore.clearHistory(workflowId);
+                workflowStore.deleteInstance(workflowId);
             }
         }
         
Index: main/plugin-forms/src/org/ametys/plugins/forms/DeleteFormTableAction.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/DeleteFormTableAction.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/DeleteFormTableAction.java	(working copy)
@@ -1,53 +1,74 @@
 /*
- *  Copyright 2011 Anyware Services
+ * 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
+ * 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
+ * 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.
+ * 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.
  */
 package org.ametys.plugins.forms;
 
+import java.util.List;
 import java.util.Map;
 
 import org.apache.avalon.framework.activity.Initializable;
 import org.apache.avalon.framework.parameters.Parameters;
-import org.apache.cocoon.acting.AbstractAction;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.acting.ServiceableAction;
 import org.apache.cocoon.environment.ObjectModelHelper;
 import org.apache.cocoon.environment.Redirector;
 import org.apache.cocoon.environment.SourceResolver;
 
 import org.ametys.plugins.forms.table.FormTableManager;
+import org.ametys.plugins.forms.workflow.FormEntriesWorkflow;
+import org.ametys.plugins.workflow.store.JdbcWorkflowStore;
 
 /**
  * Delete a list of forms' tables
  */
-public class DeleteFormTableAction extends AbstractAction implements Initializable
+public class DeleteFormTableAction extends ServiceableAction implements Initializable
 {
     /** The manager for form SQL Tables */
     private FormTableManager _formTableManager;
     
+    /** The workflow component for form entries */
+    private FormEntriesWorkflow _formEntriesWorkflow;
+    
     @Override
     public void initialize() throws Exception
     {
         _formTableManager = new FormTableManager();
     }
     
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        _formEntriesWorkflow = (FormEntriesWorkflow) serviceManager.lookup(FormEntriesWorkflow.ROLE);
+    }
+    
     @SuppressWarnings("unchecked")
     public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
     {
         Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
         String tableName = (String) jsParameters.get("name");
         
-        _formTableManager.dropTable(tableName);
+        List<Integer> workflowIds = _formTableManager.getWorkflowIds(tableName.substring(FormTableManager.TABLE_PREFIX.length()), null);
+        JdbcWorkflowStore workflowStore = (JdbcWorkflowStore) _formEntriesWorkflow.getConfiguration().getWorkflowStore();
+        for (Integer workflowId : workflowIds)
+        {
+            workflowStore.clearHistory(workflowId);
+            workflowStore.deleteInstance(workflowId);
+        }
         
+        _formTableManager.dropTable(tableName);
         return EMPTY_MAP;
     }
 }
Index: main/plugin-forms/src/org/ametys/plugins/forms/Form.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/Form.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/Form.java	(working copy)
@@ -28,13 +28,10 @@
  */
 public class Form
 {
-    
     /** The form id. */
     protected String _id;
-    
     /** The form label. */
     protected String _label;
-    
     /** The field which indicates the email to send an acknowledgement of receipt. */
     protected String _receiptFieldId;
     /** The email address of the sender. Can be empty to use default one. */
@@ -45,7 +42,9 @@
     protected String _receiptFieldBody;
     /** The id of the page where to redirect to. Can be empty */
     protected String _redirectTo;
-
+    /** The workflow of this form's submissions */
+    protected String _workflowName;
+    
     /** The form emails. */
     protected Set<String> _notificationEmails;
     
@@ -63,7 +62,7 @@
      */
     public Form()
     {
-        this("", "", "", "", "", "", new HashSet<String>(), "", new ArrayList<Field>(), new ArrayList<Fieldset>(), "");
+        this("", "", "", "", "", "", new HashSet<String>(), "", new ArrayList<Field>(), new ArrayList<Fieldset>(), "", "");
     }
     
     /**
@@ -80,7 +79,7 @@
      * @param fieldsets the form fieldsets.
      * @param redirectTo the id of the page where to redirect to
      */
-    public Form(String id, String label, String receiptFieldId, String receiptFieldFromAddress, String receiptFieldSubject, String receiptFieldBody, Set<String> emails, String contentId, Collection<Field> fields, Collection<Fieldset> fieldsets, String redirectTo)
+    public Form(String id, String label, String receiptFieldId, String receiptFieldFromAddress, String receiptFieldSubject, String receiptFieldBody, Set<String> emails, String contentId, Collection<Field> fields, Collection<Fieldset> fieldsets, String redirectTo, String workflowName)
     {
         this._id = id;
         this._label = label;
@@ -93,6 +92,7 @@
         this._fields = new ArrayList<Field>(fields);
         this._fieldsets = new ArrayList<Fieldset>(fieldsets);
         this._redirectTo = redirectTo;
+        this._workflowName = workflowName;
     }
 
     /**
@@ -306,4 +306,22 @@
     {
         _redirectTo = redirectTo;
     }
+    
+    /**
+     * Retrieve the name of the workflow of this form's entries
+     * @return the name of the workflow used
+     */
+    public String getWorkflowName()
+    {
+        return _workflowName;
+    }
+    
+    /**
+     * Set the name of the workflow of this form's entries
+     * @param workflowName the name of the workflow to use
+     */
+    public void setWorkflowName(String workflowName)
+    {
+        _workflowName = workflowName;
+    }
 }
Index: main/plugin-forms/src/org/ametys/plugins/forms/FormManager.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/FormManager.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/FormManager.java	(working copy)
@@ -18,8 +18,10 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import org.apache.avalon.framework.activity.Initializable;
@@ -37,13 +39,20 @@
 import org.xml.sax.SAXException;
 
 import org.ametys.cms.repository.Content;
+import org.ametys.plugins.forms.data.FieldValue;
+import org.ametys.plugins.forms.data.UserEntry;
 import org.ametys.plugins.forms.jcr.FormPropertiesManager;
 import org.ametys.plugins.forms.table.FormTableManager;
+import org.ametys.plugins.forms.workflow.FormEntriesWorkflow;
 import org.ametys.plugins.forms.workflow.FormParser;
 import org.ametys.plugins.repository.metadata.CompositeMetadata;
 import org.ametys.plugins.repository.metadata.RichText;
+import org.ametys.plugins.workflow.store.JdbcWorkflowStore;
 import org.ametys.web.repository.content.WebContent;
 
+import com.opensymphony.workflow.StoreException;
+import com.opensymphony.workflow.WorkflowException;
+
 /**
  * Class to handle forms in contents
  */
@@ -58,6 +67,7 @@
     private DOMParser _parser;
     private FormParser _formParser;
     private FormTableManager _formTableManager;
+    private FormEntriesWorkflow _formEntriesWorkflow;
     
     @Override
     public void service(ServiceManager smanager) throws ServiceException
@@ -66,6 +76,7 @@
         XPathProcessor processor = (XPathProcessor) smanager.lookup(XPathProcessor.ROLE);
         _formParser = new FormParser(processor);
         _formPropertiesManager = (FormPropertiesManager) smanager.lookup(FormPropertiesManager.ROLE);
+        _formEntriesWorkflow = (FormEntriesWorkflow) smanager.lookup(FormEntriesWorkflow.ROLE);
     }
     
     @Override
@@ -79,8 +90,9 @@
      * @param content the content.
      * @throws SAXException if a SAX exception occurs during content parsing.
      * @throws IOException if an I/O exception occurs during content parsing.
+     * @throws WorkflowException if an exception occurs during the reset of the form entries workflow
      */
-    public void processContentForms(Content content) throws SAXException, IOException
+    public void processContentForms(Content content) throws SAXException, IOException, WorkflowException
     {
         String contentName = content.getName();
         String siteName = "";
@@ -109,13 +121,37 @@
             {
                 try
                 {
-                    // Parse the form node.
+                    // Parse the new form from the form node.
                     Form form = _formParser.parseForm(formNode);
                     forms.add(form);
-                    
-                    // Try to retrieve the old form properties from the repository.
+
                     Form oldForm = _formPropertiesManager.getForm(siteName, form.getId());
                     
+                    String newWorkflowName = StringUtils.defaultString(form.getWorkflowName());
+                    if (oldForm != null)
+                    {
+                        String oldWorkflowName = oldForm.getWorkflowName();
+                        if (!newWorkflowName.equals(StringUtils.defaultString(oldWorkflowName)))
+                        {
+                            // The workflow has switched
+                            boolean dropColumn = false;
+                            boolean addColumn = false;
+                            if (StringUtils.isEmpty(newWorkflowName) && _formTableManager.hasWorkflowIdColumn(form.getId()))
+                            {
+                                // from an existing workflow to no workflow
+                                dropColumn = true;
+                            }
+                            else if (!_formTableManager.hasWorkflowIdColumn(form.getId()))
+                            {
+                                // from no workflow to an existing workflow
+                                addColumn = true;
+                            }
+                            
+                            // update/delete the concerned tables
+                            _resetWorkflowTables(form, dropColumn, addColumn);
+                        }
+                    }
+                    
                     // TODO Give oldForm to the form table manager to compare old/new state (in particular for radio buttons.)
                     
                     // Create the table only if it was given a label.
@@ -145,7 +181,7 @@
             }
         }
         
-        removeUnusedForms(content, contentName, forms);
+        _removeUnusedForms(content, contentName, forms);
         
         if (getLogger().isDebugEnabled())
         {
@@ -154,12 +190,62 @@
     }
     
     /**
+     * Remove the workflow tables and the workflow id column if needed
+     * @param form the form 
+     * @param dropColumn true to drop the workflow id column
+     * @throws FormsException if an error occurs while retrieving the workflow instances ids 
+     * @throws WorkflowException if an error occurs during the reset of the workflow of the form entries 
+     */
+    private void _resetWorkflowTables(Form form, boolean dropColumn, boolean addColumn) throws FormsException, WorkflowException
+    {
+        Map<String, FieldValue> columns = _formTableManager.getColumns(form);
+        List<UserEntry> submissions = _formTableManager.getSubmissions(form, columns, 0, Integer.MAX_VALUE, null);
+        try
+        {
+            // Add the column just now to get the proper submissions
+            if (addColumn)
+            {
+                _formTableManager.addWorkflowIdColumn(form.getId());
+            }
+            
+            // Delete the corresponding workflow instances and their history
+            JdbcWorkflowStore workflowStore = (JdbcWorkflowStore) _formEntriesWorkflow.getConfiguration().getWorkflowStore();
+            for (UserEntry submission : submissions)
+            {
+                Integer workflowId = submission.getWorkflowId();
+                if (workflowId != null)
+                {
+                    workflowStore.clearHistory(workflowId);
+                    workflowStore.deleteInstance(workflowId);
+                }
+
+                if (!dropColumn)
+                {
+                    String workflowName = form.getWorkflowName();
+                    int initialActionId = _formEntriesWorkflow.getInitialAction(workflowName);
+                    long newWorkflowId = _formEntriesWorkflow.initialize(workflowName, initialActionId, Collections.EMPTY_MAP);
+                    _formTableManager.setWorkflowId(form, submission.getId(), newWorkflowId);
+                }
+            }
+            
+            if (dropColumn)
+            {
+                _formTableManager.dropWorkflowIdColumn(form.getId());
+            }
+        }
+        catch (StoreException e)
+        {
+            getLogger().error("Unable to retrieve the workflow store", e);
+        }
+    }   
+
+    /**
      * Remove the unused forms
      * @param content The content
      * @param contentName The name of the content
      * @param forms The forms submitted
      */
-    public void removeUnusedForms(Content content, String contentName, List<Form> forms)
+    private void _removeUnusedForms(Content content, String contentName, List<Form> forms)
     {
         try
         {
Index: main/plugin-forms/src/org/ametys/plugins/forms/OrphanFormsGenerator.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/OrphanFormsGenerator.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/OrphanFormsGenerator.java	(working copy)
@@ -18,6 +18,9 @@
 import java.io.IOException;
 import java.util.List;
 
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+
 import org.apache.avalon.framework.service.ServiceException;
 import org.apache.avalon.framework.service.ServiceManager;
 import org.apache.cocoon.ProcessingException;
@@ -35,10 +38,10 @@
 public class OrphanFormsGenerator extends ServiceableGenerator
 {
     /** Component gathering methods to query forms in JCR */
-    FormPropertiesManager _formPropertiesManager;
+    private FormPropertiesManager _formPropertiesManager;
     
     /** Component gathering methods to manipulate SQL forms */
-    FormTableManager _formTableManager;
+    private FormTableManager _formTableManager;
     
     @Override
     public void service(ServiceManager serviceManager) throws ServiceException
@@ -63,6 +66,35 @@
                 form = _formPropertiesManager.getForm(formId);
                 if (form == null)
                 {
+                    Node frozenContentNode = _formPropertiesManager.getMostRecentFormFrozenContent(formId);
+                    if (frozenContentNode != null)
+                    {
+                        String contentUUId = frozenContentNode.getProperty("jcr:frozenUuid").getString();
+                        String title = frozenContentNode.getProperty("ametys:title").getString();
+                        String contributor = frozenContentNode.getProperty("ametys:contributor").getString();
+                        
+                        attrs.addCDATAAttribute("contentTitle", title);
+                        if (frozenContentNode.hasProperty("ametys:site"))
+                        {
+                            attrs.addCDATAAttribute("siteName", frozenContentNode.getProperty("ametys:site").getString());
+                        }
+                        
+                        attrs.addCDATAAttribute("lastContributor", contributor);
+                        
+                        boolean stillExists = true;
+                        try
+                        {
+                            frozenContentNode.getSession().getNodeByIdentifier(contentUUId);
+                        }
+                        catch (ItemNotFoundException e)
+                        {
+                            stillExists = false;
+                        }
+                        
+                        attrs.addCDATAAttribute("stillExists", Boolean.toString(stillExists));
+                    }
+                
+                    // Table attrs
                     attrs.addCDATAAttribute("name", tableName);
                     attrs.addCDATAAttribute("total", Integer.toString(_formTableManager.getTotalSubmissions(formId)));
                     
Index: main/plugin-forms/src/org/ametys/plugins/forms/data/ContentFormsGenerator.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/data/ContentFormsGenerator.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/data/ContentFormsGenerator.java	(working copy)
@@ -90,6 +90,7 @@
                         // replace '/' character with ' ', otherwise the url wont get a match in cocoon 
                         atts.addCDATAAttribute("encodedLabel", new URI(null, null,  URIDecoder.decode(form.getLabel()).replace('/', ' '), null).getRawPath());
                         
+                        atts.addCDATAAttribute("workflowName", form.getWorkflowName());
                         XMLUtils.createElement(contentHandler, "form", atts);
                     }
                     
Index: main/plugin-forms/src/org/ametys/plugins/forms/data/FormEntriesGenerator.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/data/FormEntriesGenerator.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/data/FormEntriesGenerator.java	(working copy)
@@ -38,7 +38,12 @@
 import org.ametys.plugins.forms.FormsException;
 import org.ametys.plugins.forms.jcr.FormPropertiesManager;
 import org.ametys.plugins.forms.table.FormTableManager;
+import org.ametys.plugins.forms.workflow.FormEntriesWorkflow;
 import org.ametys.plugins.repository.AmetysObjectResolver;
+import org.ametys.runtime.util.I18nUtils;
+import org.ametys.runtime.util.I18nizableText;
+
+import com.opensymphony.workflow.spi.Step;
 
 /**
  * Searches the list of forms contained in a given set of contents.
@@ -60,12 +65,20 @@
     /** The ametys object resolver. */
     protected AmetysObjectResolver _resolver;
     
+    /** The workflow component for form entries. */
+    protected FormEntriesWorkflow _formEntriesWorkflow;
+    
+    /** Component gathering utility methods to manipulate internationalizable text */
+    protected I18nUtils _i18nUtils;
+    
     @Override
     public void service(ServiceManager serviceManager) throws ServiceException
     {
         super.service(serviceManager);
         _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
         _formPropertiesManager = (FormPropertiesManager) serviceManager.lookup(FormPropertiesManager.ROLE);
+        _formEntriesWorkflow = (FormEntriesWorkflow) serviceManager.lookup(FormEntriesWorkflow.ROLE);
+        _i18nUtils = (I18nUtils) serviceManager.lookup(I18nUtils.ROLE);
         _formTableManager = new FormTableManager();
     }
     
@@ -92,7 +105,7 @@
             }
             
             Map<String, FieldValue> columns = _formTableManager.getColumns(form);
-            List<UserEntry> entries = _formTableManager.getSubmissions(form, columns, start, limit);
+            List<UserEntry> entries = _formTableManager.getSubmissions(form, columns, start, limit, null);
             int totalSubmissions = _formTableManager.getTotalSubmissions(form.getId());
             
             contentHandler.startDocument();
@@ -151,6 +164,17 @@
         atts.addCDATAAttribute("id", Integer.toString(entry.getId()));
         atts.addCDATAAttribute("submission-date", _DATE_FORMAT.format(entry.getCreationDate()));
         
+        // Workflow step
+        Integer workflowId = entry.getWorkflowId();
+        if (workflowId != null)
+        {
+            String workflowName = _formEntriesWorkflow.getWorkflowName(workflowId);
+            Step step = (Step) _formEntriesWorkflow.getCurrentSteps(workflowId).iterator().next();
+            
+            String stepName = _formEntriesWorkflow.getStepName(workflowName, step.getStepId());
+            atts.addCDATAAttribute("workflowStep", _i18nUtils.translate(new I18nizableText("application", stepName)));
+        }
+        
         XMLUtils.startElement(contentHandler, "entry", atts);
         
         for (FieldValue fdValue : entry.getValues())
@@ -182,7 +206,7 @@
             
             XMLUtils.createElement(contentHandler, "field", atts, displayValue);
         }
-        
+
         XMLUtils.endElement(contentHandler, "entry");
     }
     
Index: main/plugin-forms/src/org/ametys/plugins/forms/data/FormsListGenerator.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/data/FormsListGenerator.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/data/FormsListGenerator.java	(working copy)
@@ -52,14 +52,14 @@
     
     /** The form data manager */
     private FormTableManager _formTableManager;
-
+    
     /** The Ametys object resolver */
     protected AmetysObjectResolver _resolver;
     
     @Override
     public void service(ServiceManager serviceManager) throws ServiceException
     {
-        _formPropertiesManager = (FormPropertiesManager) serviceManager.lookup(FormPropertiesManager.ROLE); 
+        _formPropertiesManager = (FormPropertiesManager) serviceManager.lookup(FormPropertiesManager.ROLE);
         _formTableManager = new FormTableManager();
         _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
     }
@@ -94,11 +94,12 @@
                     try
                     {
                         Map<String, FieldValue> columns = _formTableManager.getColumns(form);
-                        List<UserEntry> entries = _formTableManager.getSubmissions(form, columns, 0, Integer.MAX_VALUE);
+                        List<UserEntry> entries = _formTableManager.getSubmissions(form, columns, 0, Integer.MAX_VALUE, null);
                         int totalSubmissions = _formTableManager.getTotalSubmissions(form.getId());
                         Date lastSubmissionDate = _getLastSubmissionDate(entries);
                         attrs.addCDATAAttribute("id", form.getId());
                         attrs.addCDATAAttribute("encodedLabel", new URI(null, null,  URIDecoder.decode(form.getLabel()).replace('/', ' '), null).getRawPath());
+                        attrs.addCDATAAttribute("workflowName", form.getWorkflowName());
                         
                         XMLUtils.startElement(contentHandler, "form", attrs);
                         
Index: main/plugin-forms/src/org/ametys/plugins/forms/data/UserEntry.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/data/UserEntry.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/data/UserEntry.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  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.
@@ -32,17 +32,22 @@
     /** The entry values. */
     protected List<FieldValue> _values;
     
+    /** The workflow id of the entry */
+    protected Integer _workflowId;
+    
     /**
      * Constructor with parameters.
-     * @param id
-     * @param creationDate
-     * @param values
+     * @param id the id of the user enrty
+     * @param creationDate the date at which this entry was submitted
+     * @param values the form values
+     * @param workflowId the id of the corresponding workflow instance
      */
-    public UserEntry(int id, Date creationDate, List<FieldValue> values)
+    public UserEntry(int id, Date creationDate, List<FieldValue> values, Integer workflowId)
     {
         this._id = id;
         this._creationDate = creationDate;
         this._values = values;
+        this._workflowId = workflowId;
     }
 
     /**
@@ -99,4 +104,21 @@
         this._values = values;
     }
     
+    /**
+     * Set the workflow id of this entry
+     * @param workflowId the id of the workflow of this entry
+     */
+    public void setWorkflowId(Integer workflowId)
+    {
+        this._workflowId = workflowId;
+    }
+    
+    /**
+     * Get the workflow id of this user entry
+     * @return the workflow id of this entry
+     */
+    public Integer getWorkflowId()
+    {
+        return _workflowId;
+    }
 }
Index: main/plugin-forms/src/org/ametys/plugins/forms/jcr/FormPropertiesManager.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/jcr/FormPropertiesManager.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/jcr/FormPropertiesManager.java	(working copy)
@@ -64,9 +64,6 @@
     /** The avalon component ROLE. */
     public static final String ROLE = FormPropertiesManager.class.getName();
     
-    /** JCR relative path to root node. */
-    public static final String ROOT_REPO = AmetysObjectResolver.ROOT_REPO;
-    
     /** Plugins root node name. */
     public static final String PLUGINS_NODE = "ametys-internal:plugins";
     
@@ -85,6 +82,9 @@
     /** "Label" property name. */
     public static final String FORM_PROPERTY_LABEL = RepositoryConstants.NAMESPACE_PREFIX + ":label";
     
+    /** "last modified" property name. */
+    public static final String CONTENT_LAST_MODIFIED_PROPERTY_LABEL = RepositoryConstants.NAMESPACE_PREFIX + ":lastModified";
+    
     /** "Receipt field ID" property name. */
     public static final String FORM_PROPERTY_RECEIPT_FIELD_ID = RepositoryConstants.NAMESPACE_PREFIX + ":receipt-field-id";
     
@@ -103,6 +103,9 @@
     /** "Emails" property name. */
     public static final String FORM_PROPERTY_EMAILS = RepositoryConstants.NAMESPACE_PREFIX + ":notification-emails";
     
+    /** "Workflow name" property name. */
+    public static final String FORM_PROPERTY_WORKFLOW_NAME = RepositoryConstants.NAMESPACE_PREFIX + ":workflow-name";
+    
     /** "ID" field property name. */
     public static final String FIELD_PROPERTY_ID = RepositoryConstants.NAMESPACE_PREFIX + ":id";
     
@@ -124,14 +127,18 @@
     /** The Site manager. */
     protected SiteManager _siteManager;
     
+    /** The resolver for ametys objects */
+    protected AmetysObjectResolver _resolver;
+
     /** The plugin name. */
     protected String _pluginName;
-
+    
     @Override
     public void service(ServiceManager serviceManager) throws ServiceException
     {
         _repository = (Repository) serviceManager.lookup(AbstractRepository.ROLE);
         _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE);
+        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
     }
     
     @Override
@@ -202,8 +209,47 @@
     }
     
     /**
+     * Get the most recent frozen content that contain the form of the given id 
+     * @param formId the id of the form
+     * @return the list of frozen content nodes containing the form
+     * @throws FormsException if an error occurs while retrieving the forms' frozen content nodes
+     */
+    public Node getMostRecentFormFrozenContent(String formId) throws FormsException
+    {
+        Session session = null;
+        try
+        {
+            session = _repository.login();
+            
+            String xpathQuery = "//element(" + formId + ", nt:frozenNode)/../.. order by @" + CONTENT_LAST_MODIFIED_PROPERTY_LABEL + " descending";
+            
+            QueryManager queryManager = session.getWorkspace().getQueryManager();
+            Query query = queryManager.createQuery(xpathQuery, Query.XPATH);
+            NodeIterator nodeIterator = query.execute().getNodes();
+            
+            if (nodeIterator.hasNext())
+            {
+                return (Node) nodeIterator.next();
+            }
+            
+            return null;
+        }
+        catch (RepositoryException e)
+        {
+            throw new FormsException("Error executing the query to find the frozen content nodes for the form '" + formId + "'.", e);
+        }
+        finally
+        {
+            if (session != null)
+            {
+                session.logout();
+            }
+        }
+    }
+    
+    /**
      * Get the forms in a specified content.
-     * @param content the content.
+     * @param content the c_resolverontent.
      * @return the forms as a list.
      * @throws FormsException if an error occurs.
      */
@@ -302,6 +348,50 @@
     }    
     
     /**
+     * Get the content corresponding to the form with the given id
+     * @param formId the id of the form
+     * @return the {@link Content} corresponding to the form with the given id
+     * @throws FormsException if something goes wrong when either querying the form JCR node or finding its parent {@link Content} 
+     */
+    public Content getFormContent(String formId) throws FormsException
+    {
+        Session session = null;
+        try
+        {
+            session = _repository.login();
+            
+            // Build the query
+            String xpathQuery = "//element(*, ametys:content)/" + FORMS_NODE + "/*[@" + FORM_PROPERTY_ID + " = '" + formId + "']";
+            
+            // Execute
+            QueryManager queryManager = session.getWorkspace().getQueryManager();
+            Query query = queryManager.createQuery(xpathQuery, Query.XPATH);
+            NodeIterator nodeIterator = query.execute().getNodes();
+            
+            if (nodeIterator.hasNext())
+            {
+                Node node = nodeIterator.nextNode().getParent().getParent();
+                Content content = (Content) _resolver.resolve(node, false);
+                return content;
+            }
+            
+            return null;
+        }
+        catch (RepositoryException e)
+        {
+            throw new FormsException("Error executing the query to find the content containing the form of id " + formId, e);
+        }
+        finally
+        {
+            if (session != null)
+            {
+                session.logout();
+            }
+        }
+        
+    }
+    
+    /**
      * Extract all the form objects from a node
      * @param node the node
      * @return the forms list of this node
@@ -449,6 +539,7 @@
             String receiptFieldFromAddress = _getSingleProperty(formNode, FORM_PROPERTY_RECEIPT_FROM_ADDRESS, "");
             String redirectTo = _getSingleProperty(formNode, FORM_PROPERTY_REDIRECT_TO, "");
             Collection<String> emails = _getMultipleProperty(formNode, FORM_PROPERTY_EMAILS);
+            String workflowName = _getSingleProperty(formNode, FORM_PROPERTY_WORKFLOW_NAME, "");
             
             form = new Form();
             
@@ -460,6 +551,7 @@
             form.setReceiptFieldFromAddress(receiptFieldFromAddress);
             form.setNotificationEmails(new HashSet<String>(emails));
             form.setRedirectTo(redirectTo);
+            form.setWorkflowName(workflowName);
             
             _extractFields(formNode, form);
         }
@@ -563,6 +655,7 @@
         formNode.setProperty(FORM_PROPERTY_RECEIPT_FROM_ADDRESS, form.getReceiptFieldFromAddress());
         formNode.setProperty(FORM_PROPERTY_EMAILS, emailArray);
         formNode.setProperty(FORM_PROPERTY_REDIRECT_TO, form.getRedirectTo());
+        formNode.setProperty(FORM_PROPERTY_WORKFLOW_NAME, form.getWorkflowName());
     }
     
     /**
@@ -751,9 +844,9 @@
     }
     
     /**
-     * Remove a form
+     * Remove a form 
      * @param form The form to remove
-     * @throws FormsException
+     * @throws FormsException if an error occurs when removing the form
      */
     public void remove(Form form) throws FormsException
     {
Index: main/plugin-forms/src/org/ametys/plugins/forms/processing/GetContentAction.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/processing/GetContentAction.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/processing/GetContentAction.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  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.
Index: main/plugin-forms/src/org/ametys/plugins/forms/processing/ProcessFormAction.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/processing/ProcessFormAction.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/processing/ProcessFormAction.java	(working copy)
@@ -69,6 +69,7 @@
 import org.ametys.plugins.forms.jcr.FormPropertiesManager;
 import org.ametys.plugins.forms.table.DbTypeHelper;
 import org.ametys.plugins.forms.table.FormTableManager;
+import org.ametys.plugins.forms.workflow.FormEntriesWorkflow;
 import org.ametys.plugins.repository.AmetysObjectResolver;
 import org.ametys.plugins.repository.UnknownAmetysObjectException;
 import org.ametys.runtime.captcha.CaptchaHelper;
@@ -81,6 +82,8 @@
 import org.ametys.web.repository.page.Page;
 import org.ametys.web.site.SiteConfigurationExtensionPoint;
 
+import com.opensymphony.workflow.WorkflowException;
+
 /**
  * Action that processes the user submitted data on a form.
  */
@@ -145,6 +148,9 @@
     /** The URI prefix handler */
     protected URIPrefixHandler _prefixHandler;
     
+    /** The workflow component for form entries */
+    protected FormEntriesWorkflow _formEntriesWorkflow;
+    
     @Override
     public void service(ServiceManager serviceManager) throws ServiceException
     {
@@ -154,7 +160,8 @@
         _sourceResolver = (SourceResolver) serviceManager.lookup(SourceResolver.ROLE);
         _ametysObjectResolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
         _siteConfiguration = (SiteConfigurationExtensionPoint) serviceManager.lookup(SiteConfigurationExtensionPoint.ROLE);
-        _prefixHandler = (URIPrefixHandler) manager.lookup(URIPrefixHandler.ROLE);
+        _prefixHandler = (URIPrefixHandler) serviceManager.lookup(URIPrefixHandler.ROLE);
+        _formEntriesWorkflow = (FormEntriesWorkflow) serviceManager.lookup(FormEntriesWorkflow.ROLE);
     }
 
     @Override
@@ -190,7 +197,7 @@
         }
 
         _insertInput(form, input);
-
+        
         _sendEmails(form, input, siteName);
 
         if (StringUtils.isNotEmpty(form.getRedirectTo()))
@@ -396,8 +403,9 @@
      * @param form the Form object.
      * @param input the user input.
      * @throws FormsException if an error occurs.
+     * @throws WorkflowException if an error occurs when initializing the workflow of the form entry
      */
-    protected void _insertInput(Form form, Map<String, FieldValue> input) throws FormsException
+    protected void _insertInput(Form form, Map<String, FieldValue> input) throws FormsException, WorkflowException
     {
         final String tableName = FormTableManager.TABLE_PREFIX + form.getId();
 
@@ -454,7 +462,14 @@
                     values.append(", ");
                 }
             }
-
+            
+            if (_formTableManager.hasWorkflowIdColumn(form.getId()))
+            {
+                // Ensure compatibility with form entries that remained without workflow
+                sql.append(", ").append(FormTableManager.WORKFLOW_ID_FIELD);
+                values.append(", ?");
+            }
+              
             sql.append(") VALUES (").append(values).append(")");
 
             if (getLogger().isDebugEnabled())
@@ -488,10 +503,11 @@
      * @param stmt the prepared statement.
      * @param dbType the database type.
      * @throws SQLException if a SQL error occurs.
+     * @throws WorkflowException if an exception occurs when initializing the workflow of the form entry
      */
-    protected void _setParameters(Form form, Map<String, FieldValue> input, PreparedStatement stmt, DatabaseType dbType) throws SQLException
+    protected void _setParameters(Form form, Map<String, FieldValue> input, PreparedStatement stmt, DatabaseType dbType) throws SQLException, WorkflowException
     {
-        // First two are id (auto-increment) and test;
+        // First two are id (auto-increment) and creation date
         int index = 1;
         if (DbTypeHelper.insertIdentity(dbType) && !DatabaseType.DATABASE_ORACLE.equals(dbType))
         {
@@ -549,6 +565,16 @@
             }
             index++;
         }
+        
+        // Ensure compatibility with form entries that remained without workflow
+        if (_formTableManager.hasWorkflowIdColumn(form.getId()))
+        {
+            String workflowName = form.getWorkflowName();
+            int initialActionId = _formEntriesWorkflow.getInitialAction(workflowName);
+            
+            long workflowInstanceId = _formEntriesWorkflow.initialize(form.getWorkflowName(), initialActionId, Collections.EMPTY_MAP);
+            stmt.setLong(index, workflowInstanceId);
+        }
     }
 
     /**
Index: main/plugin-forms/src/org/ametys/plugins/forms/table/DbTypeHelper.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/table/DbTypeHelper.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/table/DbTypeHelper.java	(working copy)
@@ -216,6 +216,31 @@
     }
     
     /**
+     * Get the integer type identifier corresponding to the database type.
+     * @param dbType the database type.
+     * @return the integer type identifier (to be included in CREATE statement).
+     */
+    public static String getIntegerType(DatabaseType dbType)
+    {
+        switch (dbType)
+        {
+            case DATABASE_MYSQL:
+                break;
+            case DATABASE_DERBY:
+                break;
+            case DATABASE_ORACLE:
+                break;
+            case DATABASE_POSTGRES:
+                break;
+            case DATABASE_UNKNOWN:
+                break;
+            default:
+                break;
+        }
+        return "INTEGER";
+    }
+    
+    /**
      * Get the date and time type identifier corresponding to the database type.
      * @param dbType the database type.
      * @return the real date and time type identifier (to be included in CREATE statement).
@@ -447,5 +472,4 @@
         
         return buff.toString();
     }
-    
 }
Index: main/plugin-forms/src/org/ametys/plugins/forms/table/FormTableManager.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/table/FormTableManager.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/table/FormTableManager.java	(working copy)
@@ -25,6 +25,7 @@
 import java.sql.Timestamp;
 import java.sql.Types;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -59,7 +60,10 @@
 
     /** Creation date field name. */
     public static final String CREATION_DATE_FIELD = "creationDate";
-
+    
+    /** Workflow id column name. */
+    public static final String WORKFLOW_ID_FIELD = "workflowId"; 
+    
     /** Suffix for filename column. */
     public static final String FILE_NAME_COLUMN_SUFFIX = "-filename";
 
@@ -179,18 +183,19 @@
      * Get all the submissions for a form.
      * 
      * @param form the form object.
-     * @param columns
+     * @param columns the columns
      * @param offset The number of results to ignore.
      * @param length The maximum number of results to return.
+     * @param entryIds the ids of the submissions to retrieve
      * @return the list of submissions.
      * @throws FormsException if an error occurs.
      */
-    public List<UserEntry> getSubmissions(Form form, Map<String, FieldValue> columns, int offset, int length) throws FormsException
+    public List<UserEntry> getSubmissions(Form form, Map<String, FieldValue> columns, int offset, int length, List<Integer> entryIds) throws FormsException
     {
         List<UserEntry> entries = new ArrayList<UserEntry>();
 
         final String tableName = TABLE_PREFIX + form.getId();
-
+        
         Connection con = null;
         PreparedStatement stmt = null;
         ResultSet rs = null;
@@ -290,9 +295,18 @@
                             break;
                     }
                 }
+                
+                Integer workflowId = null;
+                if (hasWorkflowIdColumn(form.getId()))
+                {
+                    workflowId = rs.getInt(WORKFLOW_ID_FIELD);
+                }
 
-                UserEntry entry = new UserEntry(id, creationDate, entryValues);
-                entries.add(entry);
+                if (entryIds == null || entryIds.contains(id))
+                {
+                    UserEntry entry = new UserEntry(id, creationDate, entryValues, workflowId);
+                    entries.add(entry);
+                }
             }
         }
         catch (SQLException e)
@@ -592,7 +606,7 @@
 
             // Move the columns that were deleted or replaced.
             _moveColumns(con, modifications.getColumnsToRemove(), existingColumns, tableName);
-
+            
             // Add the new columns.
             _addColumns(con, modifications.getColumnsToAdd(), tableName);
 
@@ -650,7 +664,7 @@
             }
 
             currentColumns = _getExistingColumns(con, tableName);
-
+            
             for (String newColumn : newColumns.keySet())
             {
                 String filteredColumn = DbTypeHelper.filterName(dbType, newColumn);
@@ -748,6 +762,7 @@
 
         modifications.setColumnsToAdd(columnsToAdd);
         modifications.setColumnsToRemove(columnsToRemove);
+        
 
         return modifications;
     }
@@ -840,7 +855,7 @@
             throw e;
         }
     }
-
+    
     private void _addColumns(Connection con, Map<String, FieldValue> columnsToAdd, String tableName) throws SQLException
     {
         DatabaseType dbType = ConnectionHelper.getDatabaseType(con);
@@ -960,16 +975,148 @@
         }
         return sqlType;
     }
+    
+    /**
+     * Add a workflow id column to the given table
+     * @param formId the id of the form to alter
+     * @throws FormsException if an error occurs
+     */
+    public void addWorkflowIdColumn(String formId) throws FormsException
+    {
+        final String tableName = TABLE_PREFIX + formId;
+        
+        Connection con = null;
+        PreparedStatement stmt = null;
+        
+        try
+        {
+            // Connect to the database.
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);
+            DatabaseType dbType = ConnectionHelper.getDatabaseType(con);
+            StringBuilder sql = new StringBuilder();
+
+            sql.append("ALTER TABLE ").append(DbTypeHelper.escape(dbType, tableName));
+            sql.append(" ADD ").append(WORKFLOW_ID_FIELD).append(" ");
+            sql.append(DbTypeHelper.getIntegerType(dbType));
+
+            if (__logger.isDebugEnabled())
+            {
+                __logger.debug("Adding column: " + sql.toString());
+            }
+
+            stmt = con.prepareStatement(sql.toString());
+            stmt.executeUpdate();
+        }
+        catch (SQLException e)
+        {
+            __logger.error("Error while adding the workflow id column for the table " + tableName, e);
+            throw new FormsException("Error while adding a column to the table " + tableName, e);
+        }
+        finally
+        {
+            // Clean up connection resources
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }
+    }
+    
+    /**
+     * Delete the workflow id column of the given table
+     * @param formId the id of the form to alter
+     * @throws FormsException if an error occurs
+     */
+    public void dropWorkflowIdColumn(String formId) throws FormsException
+    {
+        final String tableName = TABLE_PREFIX + formId;
+        
+        Connection con = null;
+        PreparedStatement stmt = null;
+        
+        try
+        {
+            // Connect to the database.
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);
+            DatabaseType dbType = ConnectionHelper.getDatabaseType(con);
+            StringBuilder sql = new StringBuilder();
+
+            sql.append("ALTER TABLE ").append(DbTypeHelper.escape(dbType, tableName));
+            sql.append(" DROP COLUMN ").append(WORKFLOW_ID_FIELD).append(";");
+
+            if (__logger.isDebugEnabled())
+            {
+                __logger.debug("Deleting column: " + sql.toString());
+            }
+
+            stmt = con.prepareStatement(sql.toString());
+            stmt.executeUpdate();
+        }
+        catch (SQLException e)
+        {
+            __logger.error("Error while deleting the workflow id column for the table " + tableName, e);
+            throw new FormsException("Error while deleting a column from the table " + tableName, e);
+        }
+        finally
+        {
+            // Clean up connection resources
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }
+    }
+
+    /**
+     * Set a new workflow id to the column that has the given old workflow id
+     * @param form the form
+     * @param entryId the id of the entry
+     * @param newWorkflowId the new workflow id
+     * @throws FormsException if an error occurs
+     */
+    public void setWorkflowId(Form form, long entryId, long newWorkflowId) throws FormsException
+    {
+        final String tableName = TABLE_PREFIX + form.getId();
+
+        Connection con = null;
+        PreparedStatement stmt = null;
+        
+        try
+        {
+            // Connect to the database.
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);
+            DatabaseType dbType = ConnectionHelper.getDatabaseType(con);
+            
+            String query = "UPDATE " + DbTypeHelper.escape(dbType, tableName);
+            query += " SET "  + WORKFLOW_ID_FIELD + " = ?";
+            query += " WHERE " + ID_FIELD + " = ?";
+
+            stmt = con.prepareStatement(query);
+
+            stmt.setLong(1, newWorkflowId);
+            stmt.setLong(2, entryId);
 
+            // Execute the query.
+            stmt.executeUpdate();
+        }
+        catch (SQLException e)
+        {
+            __logger.error("Error while resetting the workflow id for the table " + tableName, e);
+            throw new FormsException("Error while deleting entry for table " + tableName, e);
+        }
+        finally
+        {
+            // Clean up connection resources
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }
+    }
+    
     /**
      * Delete the submission from a form.
-     * @param form the form object.
+     * @param formId the id of the form.
      * @param entryId the entry to delete. If null, all the entries are deleted.
      * @throws FormsException if an error occurs.
      */
-    public void deleteSubmission(Form form, Integer entryId) throws FormsException
+    public void deleteSubmission(String formId, Integer entryId) throws FormsException
     {
-        final String tableName = TABLE_PREFIX + form.getId();
+        final String tableName = TABLE_PREFIX + formId;
 
         Connection con = null;
         PreparedStatement stmt = null;
@@ -1009,6 +1156,103 @@
             ConnectionHelper.cleanup(con);
         }
     }
+    
+    /**
+     * Get the workflow id of the given form entry or of all the entries
+     * @param formId the id of the form
+     * @param entryId the entry to get the workflow id from. If null, all the workflow ids are returned
+     * @throws FormsException if an error occurs.
+     * @return the list of workflow ids
+     */
+    public List<Integer> getWorkflowIds(String formId, Integer entryId) throws FormsException
+    {
+        if (!hasWorkflowIdColumn(formId))
+        {
+            return Collections.EMPTY_LIST;
+        }
+        
+        List<Integer> workflowIds = new ArrayList<Integer> ();
+
+        final String tableName = TABLE_PREFIX + formId;
+        
+        Connection con = null;
+        PreparedStatement stmt = null;
+        ResultSet rs = null;
+        
+        try
+        {
+            // Connect to the database.
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);
+            DatabaseType dbType = ConnectionHelper.getDatabaseType(con);
+            
+            String query = "SELECT workflowId FROM " + DbTypeHelper.escape(dbType, tableName);
+            if (entryId != null)
+            {
+                query += " WHERE " + ID_FIELD + " = ?";
+            }
+
+            stmt = con.prepareStatement(query);
+
+            if (entryId != null)
+            {
+                stmt.setInt(1, entryId);
+            }
+            
+            // Execute the query.
+            rs = stmt.executeQuery();
+
+            // Extract the result(s).
+            while (rs.next())
+            {
+                workflowIds.add(rs.getInt(1));
+            }
+        }
+        catch (SQLException e)
+        {
+            __logger.error("Error while getting workflow ids from the table " + tableName, e);
+            throw new FormsException("Error while getting workflow ids from the table " + tableName, e);
+        }
+        finally
+        {
+            // Clean up connection resources
+            ConnectionHelper.cleanup(rs);
+            ConnectionHelper.cleanup(stmt);
+            ConnectionHelper.cleanup(con);
+        }
+
+        return workflowIds;
+    }
+    
+    /**
+     * Does the given form have a workflow id column in its SQL table ?
+     * @param formId the id of the form
+     * @return true if the form has a workflow id column, false otherwise
+     */
+    public boolean hasWorkflowIdColumn (String formId)
+    {
+        boolean hasWorkflowId = true;
+        
+        Connection con = null;
+        final String tableName = TABLE_PREFIX + formId;
+        
+        try
+        {
+            con = ConnectionHelper.getConnection(ConnectionHelper.CORE_POOL_NAME);
+            Map<String, DbColumn> currentColumns = _getExistingColumns(con, tableName);        
+            
+            // Check if the column contains a workflow id
+            if (!currentColumns.containsKey(WORKFLOW_ID_FIELD))
+            {
+                hasWorkflowId = false;
+            }
+            
+            return hasWorkflowId;
+        }
+        finally 
+        {
+            ConnectionHelper.cleanup(con);
+        }
+    }
 
     /**
      * Class storing modifications to do on columns.
@@ -1021,7 +1265,7 @@
 
         /** The columns to remove. */
         protected Map<String, FieldValue> _columnsToAdd;
-
+        
         /**
          * Build a DbColumnModifications object.
          */
@@ -1076,7 +1320,6 @@
         {
             this._columnsToAdd = columnsToAdd;
         }
-
     }
 
     /**
Index: main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEditionFunction.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEditionFunction.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEditionFunction.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  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.
@@ -68,7 +68,7 @@
             LockableAmetysObject lockableContent = (LockableAmetysObject) content;
             if (lockableContent.isLocked() && !LockHelper.isLockOwner(lockableContent, login))
             {
-                throw new WorkflowException("User '" + login + "' try to save content '" + content.getName() + "' but it is locked by another user");
+                throw new WorkflowException("User '" + login + "' is trying to save the content '" + content.getName() + "' but it is locked by another user.");
             }
             
             _checkLock (content);
@@ -76,19 +76,19 @@
         }
         catch (RepositoryException e)
         {
-            throw new WorkflowException("Unable to check lock for content " + content.getName() + "", e);
+            throw new WorkflowException("Unable to check the lock for the content " + content.getName() + "", e);
         }
         catch (AmetysRepositoryException e)
         {
-            throw new WorkflowException("Unable to edit content " + content.getName() + " from the repository", e);
+            throw new WorkflowException("Unable to edit the content " + content.getName() + " from the repository", e);
         }
         catch (SAXException e)
         {
-            _logger.error("An exception occured while parsing the form", e);
+            _logger.error("An exception occurred while parsing the form", e);
         }
         catch (IOException e)
         {
-            _logger.error("An exception occured while parsing the form", e);
+            _logger.error("An exception occurred while parsing the form", e);
         }
     }
     
Index: main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflow.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflow.java	(revision 0)
+++ main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflow.java	(revision 0)
@@ -0,0 +1,39 @@
+/*
+ *  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.
+ */
+package org.ametys.plugins.forms.workflow;
+
+import org.apache.avalon.framework.service.ServiceException;
+
+import org.ametys.plugins.workflow.Workflow;
+import org.ametys.plugins.workflow.store.JdbcWorkflowStore;
+
+import com.opensymphony.workflow.spi.WorkflowStore;
+
+/**
+ * Singleton for manipulating workflow instances on form entries. 
+ */
+public class FormEntriesWorkflow extends Workflow
+{
+    /** The Avalon Role */
+    @SuppressWarnings("hiding")
+    public static final String ROLE = FormEntriesWorkflow.class.getName();
+    
+    @Override
+    protected WorkflowStore _getWorkflowStore() throws ServiceException
+    {
+        return (WorkflowStore) _manager.lookup(JdbcWorkflowStore.ROLE);
+    }
+}
Index: main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflowAction.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflowAction.java	(revision 0)
+++ main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflowAction.java	(revision 0)
@@ -0,0 +1,162 @@
+/*
+ *  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.
+ */
+package org.ametys.plugins.forms.workflow;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.avalon.framework.parameters.Parameters;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.environment.Redirector;
+import org.apache.cocoon.environment.Request;
+import org.apache.commons.collections.MapUtils;
+
+import org.ametys.cms.repository.Content;
+import org.ametys.cms.workflow.AbstractContentWorkflowComponent;
+import org.ametys.plugins.forms.Form;
+import org.ametys.plugins.forms.data.FieldValue;
+import org.ametys.plugins.forms.data.UserEntry;
+import org.ametys.plugins.forms.jcr.FormPropertiesManager;
+import org.ametys.plugins.forms.table.FormTableManager;
+import org.ametys.plugins.workflow.cocoon.WorkflowAction;
+import org.ametys.runtime.user.CurrentUserProvider;
+import org.ametys.runtime.user.User;
+import org.ametys.runtime.user.UsersManager;
+import org.ametys.runtime.util.JSONUtils;
+import org.ametys.runtime.util.cocoon.ActionResultGenerator;
+
+import com.opensymphony.workflow.InvalidInputException;
+import com.opensymphony.workflow.WorkflowException;
+
+/**
+ * Action for firing a transition on a form entry workflow.<p>
+ * The following parameters are supported:
+ * <dl>
+ *  <dt>actionId
+ *  <dd>the id of the action to fire
+ * </dl>
+ */
+public class FormEntriesWorkflowAction extends WorkflowAction
+{
+    /** Component gathering utility methods to work with the forms JCR representations */
+    private FormPropertiesManager _formPropertiesManager;
+    
+    /** Helper to manipulation the forms SQL tables */
+    private FormTableManager _formTableManager;
+    
+    /** Workflow component for form entries */
+    private FormEntriesWorkflow _formEntriesWorkflow;
+    
+    /** Helper to retrieve the current user */
+    private CurrentUserProvider _currentUserProvider;
+    
+    /** The users manager */
+    private UsersManager _usersManager;
+
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        super.service(serviceManager);
+        _formEntriesWorkflow = (FormEntriesWorkflow) serviceManager.lookup(FormEntriesWorkflow.ROLE);
+        _formPropertiesManager = (FormPropertiesManager) serviceManager.lookup(FormPropertiesManager.ROLE);
+        _formTableManager = new FormTableManager();
+        _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
+        _usersManager = (UsersManager) serviceManager.lookup(UsersManager.ROLE);
+    }
+    
+    @Override
+    protected Map _act(Redirector redirector, Map objectModel, String source, Parameters parameters, int actionId, Map inputs) throws InvalidInputException, WorkflowException
+    {
+        _formEntriesWorkflow.doAction(_getWorkflowId(objectModel, source, parameters), actionId, inputs);
+        return _getActionResult(redirector, objectModel, source, parameters);
+    }
+    
+    @Override
+    protected long _getWorkflowId(Map objectModel, String source, Parameters parameters)
+    {
+        try
+        {
+            String entryId = parameters.getParameter("entryId");
+            String formId = parameters.getParameter("formId");
+        
+            Form form = _formPropertiesManager.getForm(formId);
+            Map<String, FieldValue> columns = _formTableManager.getColumns(form);
+           
+            List<UserEntry> submissions = _formTableManager.getSubmissions(form, columns, 0, Integer.MAX_VALUE, new ArrayList<Integer>(Arrays.asList(Integer.valueOf(entryId))));
+        
+            if (submissions.isEmpty())
+            {
+                throw new RuntimeException("Unable to retrieve the workflow id for the entry '" + entryId + "' of the form '" + formId + "'.");
+            }
+            else
+            {
+                return submissions.get(0).getWorkflowId();
+            }
+        }
+        catch (Exception e)
+        {
+            throw new RuntimeException("Missing parameter workflowId", e);
+        }
+    }
+    
+    @Override
+    protected Map<String, Object> _getInputs(Redirector redirector, Map objectModel, String source, Parameters parameters) throws Exception
+    {
+        Map<String, Object> inputs = super._getInputs(redirector, objectModel, source, parameters);
+        
+        String formId = parameters.getParameter("formId");
+        Content formContent = _formPropertiesManager.getFormContent(formId);
+        // Provide the content key
+        inputs.put(AbstractContentWorkflowComponent.CONTENT_KEY, formContent);
+        
+        // Provide a map for providing data to the generator
+        Request request = ObjectModelHelper.getRequest(objectModel);
+        Map<String, Object> result = new HashMap<String, Object>();
+        request.setAttribute(ActionResultGenerator.MAP_REQUEST_ATTR, result);
+        inputs.put(AbstractContentWorkflowComponent.RESULT_MAP_KEY, result);
+        
+        // Set the workflow comment into the inputs.
+        String comment = request.getParameter("comment");
+        inputs.put("comment", comment);
+        
+        // Set the values for the information request function if a mail is to be sent
+        String furtherInfoAsJSON = request.getParameter("furtherInfo");
+        Map<String, Object> furtherInfo = JSONUtils.parse(furtherInfoAsJSON); 
+        if (MapUtils.isNotEmpty(furtherInfo))
+        {
+            // Retrieve the current user name and email
+            String login = _currentUserProvider.getUser();
+            User user = _usersManager.getUser(login);
+            
+            inputs.put(RequestInformationFunction.SENDER, user.getEmail());
+            inputs.put(RequestInformationFunction.RECIPIENT, furtherInfo.get("recipient"));
+            inputs.put(RequestInformationFunction.SUBJECT, furtherInfo.get("subject"));
+            inputs.put(RequestInformationFunction.BODY, furtherInfo.get("body"));
+        }
+        else
+        {
+            inputs.put(RequestInformationFunction.SEND_MAIL, "false");
+        }
+        
+        
+        return inputs;
+    }
+}
Index: main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflowMenu.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflowMenu.java	(revision 0)
+++ main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflowMenu.java	(revision 0)
@@ -0,0 +1,445 @@
+/*
+ *  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.
+ */
+package org.ametys.plugins.forms.workflow;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+
+import org.ametys.cms.repository.Content;
+import org.ametys.cms.workflow.AbstractContentWorkflowComponent;
+import org.ametys.plugins.forms.Form;
+import org.ametys.plugins.forms.FormsException;
+import org.ametys.plugins.forms.data.FieldValue;
+import org.ametys.plugins.forms.data.UserEntry;
+import org.ametys.plugins.forms.jcr.FormPropertiesManager;
+import org.ametys.plugins.forms.table.FormTableManager;
+import org.ametys.runtime.ui.impl.StaticContextualClientSideElement;
+import org.ametys.runtime.util.I18nizableText;
+
+import com.opensymphony.workflow.spi.Step;
+
+/**
+ * This element creates a toggle button representing a workflow state.
+ * A menu represents the available workflow actions. 
+ */
+public class FormEntriesWorkflowMenu extends StaticContextualClientSideElement
+{
+    /** Name of the workflow */
+    private String _workflowName;
+    /** Step id */
+    private int _workflowStepId;
+    /** Step name */
+    private String _workflowStepName;
+    /** Actions */
+    private int[] _workflowActionsIds;
+    /** Actions */
+    private Set<Integer> _commentActionsIds;
+    /** Actions */
+    private Set<Integer> _furtherInformationActionsIds;
+    /** Workflow component for form entries */
+    private FormEntriesWorkflow _formEntriesWorkflow;
+    
+    /** The manager of form SQL tables */
+    private FormTableManager _formTableManager;
+    
+    /** The manager of forms informations stored in JCR */
+    private FormPropertiesManager _formPropertiesManager;
+    
+    @Override
+    public void configure(Configuration configuration) throws ConfigurationException
+    {
+        super.configure(configuration);
+        
+        _configureWorkflowItems(configuration);
+        
+        _basicScriptParameters();
+        _advancedScriptParameters();
+        _workflowActionScriptParameters();
+    }
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        _formEntriesWorkflow = (FormEntriesWorkflow) serviceManager.lookup(FormEntriesWorkflow.ROLE);
+        _formPropertiesManager = (FormPropertiesManager) serviceManager.lookup(FormPropertiesManager.ROLE);
+        _formTableManager = new FormTableManager();
+    }
+    
+    
+    private void _configureWorkflowItems(Configuration configuration) throws ConfigurationException
+    {
+        _workflowName = configuration.getChild("workflow").getAttribute("name");
+        _workflowStepId = configuration.getChild("workflow").getChild("step").getValueAsInteger();
+        _workflowStepName = _formEntriesWorkflow.getStepName(_workflowName, _workflowStepId);
+        
+        int[] allActionsId = _formEntriesWorkflow.getAllActionsFromStep(_workflowName, _workflowStepId);
+        
+        // Actions
+        Configuration actionsConfiguration = configuration.getChild("workflow").getChild("actions", false);
+        if (actionsConfiguration == null)
+        {
+            _workflowActionsIds = allActionsId;
+        }
+        else
+        {
+            boolean includeMode = "include".equals(actionsConfiguration.getAttribute("mode", "include"));
+            
+            Set<Integer> listedActions = new LinkedHashSet<Integer>();
+            for (Configuration actionConfiguration : actionsConfiguration.getChildren("action"))
+            {
+                int actionId = actionConfiguration.getValueAsInteger();
+                listedActions.add(actionId);
+            }
+            
+            // The set of available actions for this step
+            Set<Integer> availableActions = new LinkedHashSet<Integer>();
+
+            for (int i = 0; i < allActionsId.length; i++)
+            {
+                int actionId = allActionsId[i];
+                
+                boolean inList = listedActions.contains(actionId);
+                if (includeMode == inList)
+                {
+                    availableActions.add(actionId);
+                }
+            }
+
+            // Converting to int array
+            Integer[] actionsId = availableActions.toArray(new Integer[availableActions.size()]);
+            _workflowActionsIds = new int[actionsId.length];
+            for (int i = 0; i < actionsId.length; i++)
+            {
+                _workflowActionsIds[i] = actionsId[i];
+            }
+        }
+        
+        // Comments
+        Configuration commentsConfiguration = configuration.getChild("workflow").getChild("comments", false);
+        if (commentsConfiguration == null)
+        {
+            _commentActionsIds = new HashSet<Integer>();
+            for (int i = 0; i < allActionsId.length; i++)
+            {
+                _commentActionsIds.add(allActionsId[i]);
+            }
+        }
+        else
+        {
+            boolean includeMode = "include".equals(commentsConfiguration.getAttribute("mode", "include"));
+            
+            Set<Integer> listedActions = new LinkedHashSet<Integer>();
+            for (Configuration actionConfiguration : commentsConfiguration.getChildren("action"))
+            {
+                int actionId = actionConfiguration.getValueAsInteger();
+                listedActions.add(actionId);
+            }
+            
+            // The set of available actions for this step
+            Set<Integer> availableActions = new LinkedHashSet<Integer>();
+            
+            for (int i = 0; i < allActionsId.length; i++)
+            {
+                int actionId = allActionsId[i];
+                
+                boolean inList = listedActions.contains(actionId);
+                if (includeMode == inList)
+                {
+                    availableActions.add(actionId);
+                }
+            }
+            
+            // Converting to int array
+            Integer[] actionsId = availableActions.toArray(new Integer[availableActions.size()]);
+            _commentActionsIds = new HashSet<Integer>();
+            for (int i = 0; i < actionsId.length; i++)
+            {
+                _commentActionsIds.add(actionsId[i]);
+            }
+        }
+        
+        // Further information
+        Configuration furtherInfoConfiguration = configuration.getChild("workflow").getChild("further-information", false);
+        if (furtherInfoConfiguration == null)
+        {
+            _furtherInformationActionsIds = new HashSet<Integer>();
+            for (int i = 0; i < allActionsId.length; i++)
+            {
+                _furtherInformationActionsIds.add(allActionsId[i]);
+            }
+        }
+        else
+        {
+            boolean includeMode = "include".equals(furtherInfoConfiguration.getAttribute("mode", "include"));
+            
+            Set<Integer> listedActions = new LinkedHashSet<Integer>();
+            for (Configuration actionConfiguration : furtherInfoConfiguration.getChildren("action"))
+            {
+                int actionId = actionConfiguration.getValueAsInteger();
+                listedActions.add(actionId);
+            }
+            
+            // The set of available actions for this step
+            Set<Integer> availableActions = new LinkedHashSet<Integer>();
+            
+            for (int i = 0; i < allActionsId.length; i++)
+            {
+                int actionId = allActionsId[i];
+                
+                boolean inList = listedActions.contains(actionId);
+                if (includeMode == inList)
+                {
+                    availableActions.add(actionId);
+                }
+            }
+            
+            // Converting to int array
+            Integer[] actionsId = availableActions.toArray(new Integer[availableActions.size()]);
+            _furtherInformationActionsIds = new HashSet<Integer>();
+            for (int i = 0; i < actionsId.length; i++)
+            {
+                _furtherInformationActionsIds.add(actionsId[i]);
+            }
+        }
+    }
+    
+    private void _basicScriptParameters()
+    {
+        // Action
+        if (this._initialParameters.get("action") == null)
+        {
+            this._initialParameters.put("action", new I18nizableText("org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.act"));
+        }
+        
+        // Label
+        if (this._initialParameters.get("label") == null)
+        {
+            this._initialParameters.put("label", new I18nizableText("application", _workflowStepName));
+        }
+        
+        // Description
+        if (this._initialParameters.get("default-description") == null)
+        {
+            this._initialParameters.put("default-description", new I18nizableText("application", _workflowStepName + "_DESCRIPTION"));
+        }
+        
+        // Footer
+        if (this._initialParameters.get("footer") == null)
+        {
+            this._initialParameters.put("footer", new I18nizableText("application", _workflowStepName + "_FOOTER"));
+        }
+        
+        // Icons
+        String[] icons = new String[]{"-small", "-medium", "-large"};
+        for (String icon : icons)
+        {
+            if (this._initialParameters.get("icon" + icon) == null)
+            {
+                I18nizableText workflowStepName = new I18nizableText("application", _workflowStepName);
+                if ("application".equals(workflowStepName.getCatalogue()))
+                {
+                    this._initialParameters.put("icon" + icon, new I18nizableText("/plugins/forms/resources_workflow/" + workflowStepName.getKey() + icon + ".png"));
+                }
+                else
+                {
+                    String pluginName = workflowStepName.getCatalogue().substring("plugin.".length());
+                    this._initialParameters.put("icon" + icon, new I18nizableText("/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + icon + ".png"));
+                }
+            }
+        }
+    }
+    
+    private void _advancedScriptParameters()
+    {
+        // Workflow name
+        if (this._initialParameters.get("workflow-step") == null)
+        {
+            this._initialParameters.put("workflow-step", new I18nizableText(Integer.toString(_workflowStepId)));
+        }
+
+        // Step id
+        if (this._initialParameters.get("workflow-name") == null)
+        {
+            this._initialParameters.put("workflow-name", new I18nizableText(_workflowName));
+        }
+
+        // Multi selection
+        if (this._initialParameters.get("enable-multiselection") == null)
+        {
+            this._initialParameters.put("enable-multiselection", new I18nizableText("true"));
+        }
+
+        // descriptions
+        if (this._initialParameters.get("entryselected-description") == null)
+        {
+            this._initialParameters.put("entryselected-description", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_DESCRIPTION"));
+        }
+        if (this._initialParameters.get("noselection-description") == null)
+        {
+            this._initialParameters.put("noselection-description", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_NOSELECTION_DESCRIPTION"));
+        }
+        if (this._initialParameters.get("refreshing-description") == null)
+        {
+            this._initialParameters.put("refreshing-description", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_REFRESH_DESCRIPTION"));
+        }
+        if (this._initialParameters.get("nomatch-description") == null)
+        {
+            this._initialParameters.put("nomatch-description", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_NOMATCH_DESCRIPTION"));
+        }
+        if (this._initialParameters.get("noaction-available-description") == null)
+        {
+            this._initialParameters.put("noaction-available-description", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_NOACTIONAVAILABLE_DESCRIPTION"));
+        }
+        if (this._initialParameters.get("multiselection-disabled-description") == null)
+        {
+            this._initialParameters.put("multiselection-disabled-description", new I18nizableText("plugin.forms", "ENTRY_WORKFLOW_NOMULTISELECT_DESCRIPTION"));
+        }
+    }
+    
+    private void _workflowActionScriptParameters()
+    {
+        this._initialParameters.put("menu-size", new I18nizableText(Integer.toString(_workflowActionsIds.length)));
+        
+        for (int i = 0; i < _workflowActionsIds.length; i++)
+        {
+            int actionId = _workflowActionsIds[i];
+            String actionName = _formEntriesWorkflow.getActionName(_workflowName, actionId);
+            
+            this._initialParameters.put("menu-" + i, new I18nizableText(Integer.toString(actionId)));
+            
+            if (this._initialParameters.get("menu-" + actionId + "-label") == null)
+            {
+                this._initialParameters.put("menu-" + actionId + "-label", new I18nizableText("application", actionName));
+            }
+            if (this._initialParameters.get("menu-" + actionId + "-description") == null)
+            {
+                this._initialParameters.put("menu-" + actionId + "-description", new I18nizableText("application", actionName + "_DESCRIPTION"));
+            }
+            if (this._initialParameters.get("menu-" + actionId + "-action") == null)
+            {
+                this._initialParameters.put("menu-" + actionId + "-action", new I18nizableText("org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu.menuAct"));
+            }
+            if (this._initialParameters.get("menu-" + actionId + "-comment") == null)
+            {
+                this._initialParameters.put("menu-" + actionId + "-comment", new I18nizableText(String.valueOf(_commentActionsIds.contains(actionId))));
+            }
+            if (this._initialParameters.get("menu-" + actionId + "-further-information") == null)
+            {
+                this._initialParameters.put("menu-" + actionId + "-further-information", new I18nizableText(String.valueOf(_furtherInformationActionsIds.contains(actionId))));
+            }
+            if (this._initialParameters.get("menu-" + actionId + "-enable-multiselection") == null)
+            {
+                this._initialParameters.put("menu-" + actionId + "-enable-multiselection", new I18nizableText("true"));
+            }
+        }
+    }
+    
+    @Override
+    protected String _configureClass(Configuration configuration) throws ConfigurationException
+    {
+        String jsClassName = configuration.getAttribute("class", "org.ametys.plugins.forms.workflow.FormEntriesWorkflowMenu");
+        if (getLogger().isDebugEnabled())
+        {
+            getLogger().debug("Js class configured is '" + jsClassName + "'");
+        }
+        return jsClassName;        
+    }
+    
+    @Override
+    protected Script _configureScript(Configuration configuration) throws ConfigurationException
+    {
+        Script script = super._configureScript(configuration);
+        script.getScriptFiles().add("/plugins/forms/resources/js/org/ametys/forms/workflow/FormEntriesWorkflowMenu.i18n.js");
+        return script;
+    }
+    
+    @Override
+    @SuppressWarnings("unchecked")
+    public Map<String, I18nizableText> getCurrentParameters(Map<String, Object> parameters)
+    {
+        Map<String, I18nizableText> results = new HashMap<String, I18nizableText>();
+        
+        int count = 0;
+        
+        String formId = (String) parameters.get("formId");
+        List<Integer> formEntryIds = (List<Integer>) parameters.get("entryIds");
+        
+        List<UserEntry> allEntries = new ArrayList<UserEntry> ();
+        try
+        {
+            Form form = _formPropertiesManager.getForm(formId);
+            Map<String, FieldValue> columns = _formTableManager.getColumns(form);
+           
+            allEntries = _formTableManager.getSubmissions(form, columns, 0, Integer.MAX_VALUE, formEntryIds);
+
+            for (UserEntry entry : allEntries)
+            {
+                Integer workflowId = entry.getWorkflowId();
+                
+                List<Step> steps = _formEntriesWorkflow.getCurrentSteps(workflowId);
+                List<Integer> stepIds = new ArrayList<Integer>();
+                for (Step step : steps)
+                {
+                    stepIds.add(step.getStepId());
+                }
+                
+                if (stepIds.contains(_workflowStepId))
+                {
+                    Map<String, Object> transientVars = new HashMap<String, Object>();
+                    
+                    // Used to check the rights on the form's content
+                    Content formContent = _formPropertiesManager.getFormContent(form.getId());
+                    transientVars.put(AbstractContentWorkflowComponent.CONTENT_KEY, formContent);
+                    
+                    int[] availableActions = _formEntriesWorkflow.getAvailableActions(workflowId, transientVars);
+                    Arrays.sort(availableActions);
+                    
+                    results.put("entry-" + count + "-id", new I18nizableText(Integer.toString(entry.getId())));
+                    results.put("entry-" + count + "-workflowId", new I18nizableText(Integer.toString(workflowId)));
+                    
+                    for (int i = 0; i < this._workflowActionsIds.length; i++)
+                    {
+                        int actionId = this._workflowActionsIds[i];
+                        
+                        boolean activeAction = Arrays.binarySearch(availableActions, actionId) >= 0;
+                        
+                        results.put("entry-" + count + "-action-" + actionId, new I18nizableText(Boolean.toString(activeAction)));
+                    }
+                    
+                    count++;
+                }
+            }
+        }
+        catch (FormsException e)
+        {
+            getLogger().error("Error getting forms for a content.", e);
+        }
+
+        results.put("entry-count", new I18nizableText(Integer.toString(count)));
+        return results;
+    }
+}
Index: main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflowsGenerator.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflowsGenerator.java	(revision 0)
+++ main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormEntriesWorkflowsGenerator.java	(revision 0)
@@ -0,0 +1,130 @@
+/*
+ *  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.
+ */
+package org.ametys.plugins.forms.workflow;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.cms.workspace.ribbon.RibbonTabsManager;
+
+import com.opensymphony.workflow.loader.StepDescriptor;
+import com.opensymphony.workflow.loader.WorkflowDescriptor;
+
+/**
+ * This class generate the ids of the extensions used for each workflow tab
+ */
+public class FormEntriesWorkflowsGenerator extends ServiceableGenerator
+{
+    /** The Workflow component for form entries */
+    private FormEntriesWorkflow _formEntriesWorkflow;
+    
+    /** The ribbon tabs manager */
+    private RibbonTabsManager _ribbonTabsManager;
+    
+    /** The beginning of the id every workflow tab extension */
+    private static final String __FORM_WORKFLOW_TAB_EXTENSION_ID_BEGIN = "org.ametys.plugins.forms.workflow.FormEntriesWorkflowTab";
+    
+    /** The beginning of the id every workflow state button */
+    private static final String __FORM_WORKFLOW_STATE_EXTENSION_ID_BEGIN = "org.ametys.plugins.forms.workflow.FormEntriesWorkflowButton";
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        _formEntriesWorkflow = (FormEntriesWorkflow) serviceManager.lookup(FormEntriesWorkflow.ROLE);
+        _ribbonTabsManager = (RibbonTabsManager) serviceManager.lookup(RibbonTabsManager.ROLE);
+    }
+    
+    @Override
+    public void generate() throws IOException, SAXException, ProcessingException
+    {
+        String[] formWorkflowNames = _formEntriesWorkflow.getWorkflowNames();
+        
+        AttributesImpl attrs = new AttributesImpl();
+        
+        contentHandler.startDocument();
+        
+        for (String workflowName : formWorkflowNames)
+        {
+            if (workflowName.startsWith("form-"))
+            {
+                String workflowTabExtensionId = __FORM_WORKFLOW_TAB_EXTENSION_ID_BEGIN + "." + workflowName;
+                
+                if (_ribbonTabsManager.getExtension(workflowTabExtensionId) == null)
+                {
+                    getLogger().warn("There is no tab defined for the id '" + workflowTabExtensionId + "'. "
+                            + "Manipulating the workflow '" + workflowName + "' on form entries is therefore inacessible.");
+                }
+                else
+                {
+                    AttributesImpl workflowTabAtts = new AttributesImpl();
+                    workflowTabAtts.addCDATAAttribute("tabId", workflowTabExtensionId);
+    
+                    AttributesImpl groupAtts = new AttributesImpl();
+                    groupAtts.addCDATAAttribute("label", "plugin.forms:RIBBON_TABS_TAB_FORM_ENTRY_WORKFLOW_LABEL");
+                    
+                    
+                    List<String> workflowStepNames = _getWorkflowStepsNames(workflowName);
+                    XMLUtils.startElement(contentHandler, "workflow-tab", workflowTabAtts);
+                    XMLUtils.startElement(contentHandler, "group", groupAtts);
+                    XMLUtils.createElement(contentHandler, "large");
+                    XMLUtils.startElement(contentHandler, "medium");
+                    for (String stepName : workflowStepNames)
+                    {
+                        attrs.addCDATAAttribute("id", __FORM_WORKFLOW_STATE_EXTENSION_ID_BEGIN + '.' + workflowName + '.' + stepName);
+                        XMLUtils.createElement(contentHandler, "control", attrs);
+                        attrs.clear();
+                    }
+                    XMLUtils.endElement(contentHandler, "medium");
+                    XMLUtils.createElement(contentHandler, "small");
+                    
+                    XMLUtils.endElement(contentHandler, "group");
+                    XMLUtils.endElement(contentHandler, "tab-extensions");
+                    
+                    workflowTabAtts.clear();
+                }
+            }
+        }
+        
+        contentHandler.endDocument();
+    }
+    
+    @SuppressWarnings("cast")
+    private List<String> _getWorkflowStepsNames(String workflowName)
+    {
+        List<String> workflowStepNames = new ArrayList<String> ();
+        WorkflowDescriptor workflowDescriptor = _formEntriesWorkflow.getWorkflowDescriptor(workflowName);
+        List<StepDescriptor> stepDescriptors = (List<StepDescriptor>) workflowDescriptor.getSteps();
+        
+        for (StepDescriptor stepDescriptor : stepDescriptors)
+        {
+            String stepKey = stepDescriptor.getName();
+            String stepName = stepKey.substring(stepKey.lastIndexOf("_") + 1, stepKey.length());
+
+            workflowStepNames.add(stepName.toLowerCase());
+        }
+        
+        return workflowStepNames;
+    }
+}
Index: main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormParser.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormParser.java	(revision 40266)
+++ main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormParser.java	(working copy)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2010 Anyware Services
+ *  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.
@@ -130,6 +130,7 @@
             String receiptFieldBody = StringUtils.defaultString(_getAttribute(formNode, "receipt_body"));
             String redirectTo = StringUtils.defaultString(_getAttribute(formNode, "redirect"));
             String emails = StringUtils.defaultString(_getAttribute(formNode, "processing_emails"));
+            String workflowName = StringUtils.defaultString(_getAttribute(formNode, "workflow")); 
             
             emails = URLDecoder.decode(emails, "UTF-8");
             
@@ -152,6 +153,7 @@
             form.setReceiptFieldBody(receiptFieldBody);
             form.setNotificationEmails(emailSet);
             form.setRedirectTo(redirectTo);
+            form.setWorkflowName(workflowName);
             
         }
         catch (UnsupportedEncodingException e)
@@ -170,7 +172,7 @@
      * @param attributeName the name of the attribute
      * @return the value of an attribute of an input
      */
-    protected String _getAttribute(Node node, String attributeName)
+    public String _getAttribute(Node node, String attributeName)
     {
         NamedNodeMap attributes = node.getAttributes();
         Node idAttribute = attributes.getNamedItem(attributeName);
Index: main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormWorkflowClientSideElement.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormWorkflowClientSideElement.java	(revision 0)
+++ main/plugin-forms/src/org/ametys/plugins/forms/workflow/FormWorkflowClientSideElement.java	(revision 0)
@@ -0,0 +1,65 @@
+/*
+ *  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.
+ */
+package org.ametys.plugins.forms.workflow;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+
+import org.ametys.runtime.ui.impl.StaticContextualClientSideElement;
+import org.ametys.runtime.util.I18nizableText;
+
+/**
+ * This static client side allows to gather the form-dedicated workflows listed in the 'workflows.xml' file
+ */
+public class FormWorkflowClientSideElement extends StaticContextualClientSideElement
+{
+    /** The Workflow component for form entries */
+    private FormEntriesWorkflow _formEntriesWorkflow;
+    
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {   
+        _formEntriesWorkflow = (FormEntriesWorkflow) serviceManager.lookup(FormEntriesWorkflow.ROLE);
+    }   
+    
+    @Override
+    public Map<String, I18nizableText> getParameters(Map<String, Object> contextualParameters)
+    {
+        Map<String, I18nizableText> initialParameters = new HashMap<String, I18nizableText>(super.getParameters(contextualParameters));
+        String[] workflowNames = _formEntriesWorkflow.getWorkflowNames();
+        
+        int i = 0;
+        initialParameters.put(i + "-label", new I18nizableText("plugin.forms", "PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_NO_WORKFLOW"));
+        i++;
+        
+        for (String workflowName : workflowNames)
+        {
+            if (workflowName.startsWith("form-"))
+            {
+                initialParameters.put(i + "-value", new I18nizableText(workflowName));
+                initialParameters.put(i + "-label", new I18nizableText("application", "WORKFLOW_" + workflowName));
+
+                i++;
+            }
+        }
+        initialParameters.put("count", new I18nizableText(Integer.toString(i)));
+        
+        return initialParameters;
+    }
+}
Index: main/plugin-forms/src/org/ametys/plugins/forms/workflow/FurtherInformationGenerator.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/workflow/FurtherInformationGenerator.java	(revision 0)
+++ main/plugin-forms/src/org/ametys/plugins/forms/workflow/FurtherInformationGenerator.java	(revision 0)
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+package org.ametys.plugins.forms.workflow;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.cocoon.ProcessingException;
+import org.apache.cocoon.environment.ObjectModelHelper;
+import org.apache.cocoon.generation.ServiceableGenerator;
+import org.apache.cocoon.xml.AttributesImpl;
+import org.apache.cocoon.xml.XMLUtils;
+import org.xml.sax.SAXException;
+
+import org.ametys.plugins.forms.Field;
+import org.ametys.plugins.forms.Field.FieldType;
+import org.ametys.plugins.forms.Form;
+import org.ametys.plugins.forms.FormsException;
+import org.ametys.plugins.forms.data.FieldValue;
+import org.ametys.plugins.forms.data.UserEntry;
+import org.ametys.plugins.forms.jcr.FormPropertiesManager;
+import org.ametys.plugins.forms.table.FormTableManager;
+import org.ametys.runtime.user.CurrentUserProvider;
+import org.ametys.runtime.user.User;
+import org.ametys.runtime.user.UsersManager;
+
+/**
+ * Generator retrieving information for the further information requests
+ */
+public class FurtherInformationGenerator extends ServiceableGenerator
+{
+    /** Manager of the forms JCR representation */
+    private FormPropertiesManager _formPropertiesManager;
+    
+    /** Manager of the form entries SQL tables */
+    private FormTableManager _formTableManager;
+
+    /** Helper component allowing to retrieve information on the current user */
+    private CurrentUserProvider _currentUserProvider;
+
+    /** The users manager */
+    private UsersManager _usersManager;
+
+    @Override
+    public void service(ServiceManager serviceManager) throws ServiceException
+    {
+        _usersManager = (UsersManager) serviceManager.lookup(UsersManager.ROLE);
+        _currentUserProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
+        _formPropertiesManager = (FormPropertiesManager) serviceManager.lookup(FormPropertiesManager.ROLE);
+        _formTableManager = new FormTableManager();
+    }
+    
+    @SuppressWarnings("unchecked")
+    public void generate() throws IOException, SAXException, ProcessingException
+    {
+        contentHandler.startDocument();
+        XMLUtils.startElement(contentHandler, "further-information");
+        
+        // Retrieve the current user name and email
+        String login = _currentUserProvider.getUser();
+        User user = _usersManager.getUser(login);
+        
+        AttributesImpl atts = new AttributesImpl();
+        atts.addCDATAAttribute("email", user.getEmail());
+        atts.addCDATAAttribute("fullname", user.getFullName());
+        
+        XMLUtils.createElement(contentHandler, "current-user", atts);
+        
+        // Retrieve the email fields
+        Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
+        String formId = (String) jsParameters.get("formId");
+        String entryId = (String) jsParameters.get("entryId");
+        
+        List<UserEntry> submission = null; 
+        try
+        {
+            Form form = _formPropertiesManager.getForm(formId);
+            Map<String, FieldValue> columns = _formTableManager.getColumns(form);
+            
+            submission = _formTableManager.getSubmissions(form, columns, 0, Integer.MAX_VALUE, new ArrayList<Integer>(Arrays.asList(Integer.valueOf(entryId))));
+        }
+        catch (FormsException e)
+        {
+            throw new ProcessingException("An error occured while trying to recover the user entry '" + entryId + "' of the form of id '" + formId + "'.");
+        }
+        
+        XMLUtils.startElement(contentHandler, "emails");
+        
+        List<FieldValue> entries = submission.get(0).getValues();
+        for (FieldValue entry : entries)
+        {
+            Field field = entry.getField();
+            if (FieldType.TEXT.equals(field.getType()))
+            {
+                Map<String, String> properties = field.getProperties();
+                if ("email".equals(properties.get("regexptype")) && entry.getValue() != null)
+                {
+                    atts.clear();
+                    
+                    atts.addCDATAAttribute("displayValue", field.getLabel() + ": " + (String) entry.getValue());
+                    atts.addCDATAAttribute("value", (String) entry.getValue());
+                    
+                    XMLUtils.createElement(contentHandler, "email", atts);
+                }
+            }
+        }
+        
+        XMLUtils.endElement(contentHandler, "emails");
+        
+        XMLUtils.endElement(contentHandler, "further-information");
+        contentHandler.endDocument();
+    }
+}
Index: main/plugin-forms/src/org/ametys/plugins/forms/workflow/RequestInformationFunction.java
===================================================================
--- main/plugin-forms/src/org/ametys/plugins/forms/workflow/RequestInformationFunction.java	(revision 0)
+++ main/plugin-forms/src/org/ametys/plugins/forms/workflow/RequestInformationFunction.java	(revision 0)
@@ -0,0 +1,76 @@
+/*
+ *  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.
+ */
+package org.ametys.plugins.forms.workflow;
+
+import java.util.Map;
+
+import javax.mail.MessagingException;
+
+import org.apache.avalon.framework.component.Component;
+import org.apache.avalon.framework.logger.AbstractLogEnabled;
+
+import org.ametys.runtime.plugins.core.mail.SendMailHelper;
+
+import com.opensymphony.module.propertyset.PropertySet;
+import com.opensymphony.workflow.FunctionProvider;
+import com.opensymphony.workflow.WorkflowException;
+
+/**
+ * OS workflow function to send mail after an action is triggered.
+ * The author of a form entry is also notified if the receipt is set
+ */
+public class RequestInformationFunction extends AbstractLogEnabled implements Component, FunctionProvider
+{
+    /** Actually send the email ? */
+    public static final String SEND_MAIL = "send-request-information-mail";
+    
+    /** Body of the email */
+    public static final String SENDER = "sender";
+    
+    /** Body of the email */
+    public static final String RECIPIENT = "recipient";
+
+    /** Subject of the email */
+    public static final String SUBJECT = "subject";
+    
+    /** Body of the email */
+    public static final String BODY = "body";
+    
+    @Override
+    public void execute(Map transientVars, Map args, PropertySet ps) throws WorkflowException
+    {
+        // If "send-mail" is set to true or is not present, send the mail.
+        boolean dontSendMail = "false".equals(transientVars.get(SEND_MAIL));
+        if (dontSendMail)
+        {
+            return;
+        }
+        
+        String sender = (String) transientVars.get(SENDER);
+        String recipient = (String) transientVars.get(RECIPIENT);
+        String subject = (String) transientVars.get(SUBJECT);
+        String body = (String) transientVars.get(BODY);
+        
+        try
+        {
+            SendMailHelper.sendMail(subject, null, body, recipient, sender);
+        }
+        catch (MessagingException e)
+        {
+            getLogger().warn("Could not send a workflow notification mail to " + recipient, e);
+        }
+    }
+}
Index: main/plugin-forms/stylesheets/export/excel.xsl
===================================================================
--- main/plugin-forms/stylesheets/export/excel.xsl	(revision 40266)
+++ main/plugin-forms/stylesheets/export/excel.xsl	(working copy)
@@ -47,6 +47,7 @@
                                 </xsl:choose>
                             </th>
                         </xsl:for-each>
+                        <th><i18n:text i18n:key="PLUGINS_FORMS_EXPORT_COLUMN_WORKFLOW_STEP"/></th>
                     </tr>
                 </thead>
             </xsl:if>
@@ -94,6 +95,11 @@
                                 </xsl:choose>
                             </td>
                         </xsl:for-each>
+                        <td>
+                            <xsl:if test="@workflowStep">
+                                <xsl:value-of select="@workflowStep" />
+                            </xsl:if>
+                        </td>
                     </tr>
                 </xsl:for-each>
             </tbody>
Index: main/plugin-forms/stylesheets/ribbon/ribbon.xsl
===================================================================
--- main/plugin-forms/stylesheets/ribbon/ribbon.xsl	(revision 0)
+++ main/plugin-forms/stylesheets/ribbon/ribbon.xsl	(revision 0)
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   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.
+   -->
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:i18n="http://apache.org/cocoon/i18n/2.1"
+    exclude-result-prefixes="i18n">
+    
+    <xsl:template match="root">
+        <xsl:apply-templates select="ribbon"/>
+    </xsl:template>
+    
+    <xsl:template match="*">
+        <xsl:copy>
+            <xsl:copy-of select="@*"/>
+            <xsl:apply-templates/>
+        </xsl:copy>
+    </xsl:template>
+
+    <!-- Copy the last tab and use its content in conjunction with the ribbon groups written in the workflow-tab tag to define the new tabs -->
+    <xsl:template match="tab[position() = last()]">
+        <xsl:copy-of select="."/>
+        <xsl:variable name="groups" select="./groups"/>
+        <xsl:variable name="contextualColor" select="@contextualColor" />
+        <xsl:variable name="contextualLabel" select="@contextualLabel" />
+        <xsl:variable name="contextualGroup" select="@contextualGroup" />
+        <xsl:variable name="label" select="@label" />
+        
+        <xsl:for-each select="/root/workflow-tab/group">
+            <tab>
+                <xsl:attribute name="id"><xsl:value-of select="../@tabId"/></xsl:attribute>
+                <xsl:attribute name="label"><xsl:value-of select="$label"/></xsl:attribute>
+                <xsl:attribute name="contextualColor"><xsl:value-of select="$contextualColor"/></xsl:attribute>
+                <xsl:attribute name="contextualLabel"><xsl:value-of select="$contextualLabel"/></xsl:attribute>
+                <xsl:attribute name="contextualGroup"><xsl:value-of select="$contextualGroup"/></xsl:attribute>
+                
+                <groups>
+                    <!-- Actions group -->
+	                <xsl:copy-of select="$groups/group"/>
+	                
+	                <!-- Workflow group -->
+	                <xsl:copy-of select="." />
+                </groups>
+            </tab>
+        </xsl:for-each>
+    </xsl:template>
+    
+</xsl:stylesheet>
