Développer un workflow avec Visual Studio pour WSS 3/MOSS 2007

Ce tutoriel illustre comment créer un workflow avec Visual Studio et comment le déployer dans Sharepoint pour l'exploiter. Il est préférable d'avoir déjà une certaine une connaissance de Sharepoint et de connaître les rudiments de la plate-forme dotnet. Je remercie Kikof pour avoir relu ce document.

Article lu   fois.

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Un workflow est une séquence d'actions qui suivent un processus business défini par une entreprise et qui intervient à un instant T. Le concept de workflow a été introduit par Microsoft dans la "version" 3 du framework.

Avant tout développement de workflow avec Visual Studio, il faut se poser la question de savoir si ce que l'on veut faire est faisable avec Sharepoint Designer. Si tel est le cas, il faut privilégier cette piste car elle est beaucoup plus rapide et le risque d'induction de bug est réduit voire nul.

Dans ce tutoriel, nous allons créer un workflow qui crée une annonce dans la liste "Announcements" d'un site enfant lorsqu'un document est ajouté à une liste du "Top Level Site". Cet exemple est simpliste mais permet de comprendre la mise en oeuvre d'un workflow. Par ailleurs cet exercice n'est pas faisable avec un workflow développé via Sharepoint Designer car celui-ci ne permet pas de mettre à jour une liste d'un site différent de la liste sur laquelle le workflow s'applique.

II. Développement

II-A. Outils

Pour réaliser cet exercice, il est indispensable d'installer les templates Visual Studio 2005 que vous trouverez ici

II-B. Création du projet

Lorsque vous avez installé les templates, ouvrez Visual Studio et créez un nouveau projet de type "Sequential Workflow" comme illustré par l'image ci-dessous.

Nouveau Projet
Nouveau Projet

Ceci fait, dans l'explorateur de solution, vous retrouverez tous les fichiers créés automatiquement par le template.

II-C. Le fichier feature.xml

Le fichier feature.xml sert à décrire la nature de la fonctionnalité de votre workflow et permet également de spécifier une portée. Lorsque le workflow sera déployé sur Sharepoint en tant que feature, vous pourrez l'activer/la désactiver à souhait (voir section III). Lorsque vous double-cliquez sur ce fichier dans l'explorateur de solution. Vous découvrirez des instructions expliquant comment insérer le squelette XML de la feature.

Après avoir exécuté les instructions, vous devriez obtenir ceci

 
Sélectionnez

<Feature  Id="GUID"
          Title="Default Title"
          Description="This feature is a workflow that ..."
          Version="12.0.0.0"
          Scope="Site"
          xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest Location="workflow.xml" />
  </ElementManifests>
  <Properties>
    <Property Key="GloballyAvailable" Value="true" />
  </Properties>
</Feature>

Voici l'explication de chaque élément

  1. Id : vous devez insérer un GUID (identifiant unique), ceci est expliqué dans la section suivante
  2. Title : simplement le titre de votre feature
  3. Description: la description de votre feature, elle apparaîtra telle quelle aux administrateurs Sharepoint
  4. Version: une version
  5. Scope: définit la portée de la feature. Farm = ferme sharepoint, Site = Collection de site, Web=uniquement un site
  6. xmlns: espace de noms
  7. ElementManifest Location : spécifie l'emplacement du fichier workflow.xml (expliqué plus bas)
  8. GloballyAvailable: détermine la disponibilité

II-C-1. Attribution du GUID

Pour créer un GUID, rien de plus simple. Positionnez votre curseur sur le mot "GUID" de la ligne Id="GUID". Ensuite, cliquez sur "Tools->Create GUID" pour obtenir cette fenêtre

Attribution d'un GUID
Attribution d'un GUID

Cliquez sur "Copy" puis sur "Exit". Collez le contenu pour remplacer "GUID" et retirez les accolades entourant le GUID. Vous devriez obtenir une suite de ce style {D42A70AB-BAA6-4d68-BBCA-1469A3EBD8F6}, après avoir retiré les accolades : D42A70AB-BAA6-4d68-BBCA-1469A3EBD8F6

Vous n'avez plus qu'à définir le scope (portée) qui est par défaut "Site" c'est à dire, toute la collection de sites.

Voici notre fichier feature.xml de l'exemple après transformation

 
Sélectionnez

<Feature  Id="36D935D9-2BE5-4e98-A8B9-FD72B1A730B2"
          Title="Workflow Annonce"
          Description="Ce workflow crée une annonce lorsqu'un document est ajouté dans la liste 'Shared Documents'"
          Version="12.0.0.0"
          Scope="Site"
          xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest Location="workflow.xml" />
  </ElementManifests>
  <Properties>
    <Property Key="GloballyAvailable" Value="true" />
  </Properties>
</Feature>

II-D. Le fichier workflow.xml

Le fichier workflow.xml sert à identifier votre workflow de manière unique via un GUID, et sert à associer le workflow déployé à votre assemblage. Il permet également d'associer des formulaires Infopath.

Comme pour le fichier feature.xml, suivez les instructions de Visual Studio pour insérer le squelette XML. Vous devriez obtenir ceci

 
Sélectionnez

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Workflow
       Name="My Workflow"
       Description="This workflow ..."
       Id="GUID"
       CodeBesideClass="ProjectName.Workflow1" 
       CodeBesideAssembly="ProjectName, Version=3.0.0.0, Culture=neutral, PublicKeyToken=publicKeyToken"
       TaskListContentTypeId="0x000"
       AssociationUrl="_layouts/MyAssocForm.aspx"
       InstantiationUrl="_layouts/MyInitForm.aspx"
       ModificationUrl="_layouts/MyModForm.aspx">
 
    <Categories/>
    <MetaData>
 
      <Modification_GUID_Name>Name of Modification</Modification_GUID_Name>
 
      <StatusPageUrl>_layouts/WrkStat.aspx</StatusPageUrl>
    </MetaData>
  </Workflow>
</Elements>

Voici l'explication de chaque élément

  1. Name: le nom de votre workflow
  2. Description: la description de votre workflow (apparaît lors de l'association du workflow à une liste)
  3. Id: GUID même chose que pour la feature
  4. CodeBesideClass: le nom de votre projet suivi du nom de votre classe principale.
  5. CodeBesideAssembly: la signature de votre assemblage. (voir section II-E)
  6. TaskListContentTypeId: la liste des tâches liée (nous ne l'utiliserons pas dans cet exemple)
  7. AssociationUrl, InstanciationUrl et ModificationUrl : pages aspx pour les workflows interactifs (nous ne l'utiliserons pas dans cet exemple)

Voici notre fichier workflow.xml de l'exemple après transformation

 
Sélectionnez

<?xml version="1.0" encoding="utf-8" ?>
<!-- _lcid="1033" _version="12.0.3015" _dal="1"   -->
<!-- _LocalBinding   -->
 
<!-- Insert Workflow.xml Code Snippet here.  To do this:
1) Right click on this page and select "Insert Snippet" (or press Ctrl+K, then X)
2) Select Snippets->Windows SharePoint Services Workflow->Workflow.xml Code -->
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Workflow
       Name="Workflow Annonce"
       Description="Ce workflow crée une annonce lorsqu'un document est ajouté dans la liste 'Shared Documents'"
       Id="6FEFAB53-7CF7-49e5-9815-BED1AAA2B2A6"
       CodeBesideClass="AnnouncementWorkflow.Workflow1" 
       CodeBesideAssembly="AnnouncementWorkflow, Version=3.0.0.0, Culture=neutral, PublicKeyToken=cc22401ad6f86bc6"            
       >
    <Categories/>    
  </Workflow>
</Elements>

Vous devez également attribuer un GUID à l'attribut Id comme expliqué pour le fichier feature.xml.

Note: tant pour le fichier workflow.xml que pour le fichier feature.xml, Visual Studio a automatiquement lié le schéma XSL C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\wss.xsd afin de proposer l'intellisence. Il est intéressant de connaître l'emplacement de ce fichier XSD car il est parfois nécessaire de faire le lien manuellement.

II-E. Le fichier Install.bat

Le fichier Install.bat automatiquement créé par le template Visual Studio sert à déployer la feature et le workflow automatiquement dans Sharepoint. Il faudra donc demander à Visual Studio de l'exécuter lors du build. Lorsque vous cliquez sur le fichier bat dans l'explorateur de solution, vous obtenez ceci. J'ai placé mes commentaires en remarque dans le fichier directement après le mot clé REM

 
Sélectionnez

 
:: Before running this file, sign the assembly in Project properties
::
:: To customize this file, find and replace
:: a) "MyFeature" with your own feature names
:: b) "feature.xml" with the name of your feature.xml file
:: c) "workflow.xml" with the name of your workflow.xml file
:: d) "http://localhost" with the name of the site you wish to publish to
 
echo Copying the feature...
 
rd /s /q "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\MyFeature"
REM: suppression du répertoire contenant les fichiers déployés avec la feature
mkdir "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\MyFeature"
REM: Création du répertoire contenant les fichiers déployés avec la feature
 
copy /Y feature.xml  "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\MyFeature\"
REM: copie du fichier feature.xml
copy /Y workflow.xml "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\MyFeature\"
REM: copie du fichier workflow.xml
xcopy /s /Y *.aspx "%programfiles%\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS"
REM: copie des fichiers .aspx liés
echo Adding assemblies to the GAC...
::"%programfiles% (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -uf MyFeature
REM: désinstallation de l'assemblage de la GAC (global assembly cache)
::"%programfiles% (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -if bin\Debug\MyFeature.dll
REM: Installation de l'assemblage dans la GAC
echo Activating the feature...
pushd %programfiles%\common files\microsoft shared\web server extensions\12\bin
 
stsadm -o installfeature -filename MyFeature\feature.xml -force
REM: déploiement de la feature
stsadm -o activatefeature -filename MyFeature\feature.xml -url http://localhost
REM: activation de la feature
 
echo Doing an iisreset...
popd
iisreset
REM: redémarrage de IIS, vous pouvez aussi redémarrer uniquement le pool d'application

Vous devez suivre les instructions données par Visual Studio pour transformer le fichier bat selon vos besoins. Voici celui de notre exemple après transformation

 
Sélectionnez

 
:: Before running this file, sign the assembly in Project properties
::
:: To customize this file, find and replace
:: a) "MyFeature" with your own feature names
:: b) "feature.xml" with the name of your feature.xml file
:: c) "workflow.xml" with the name of your workflow.xml file
:: d) "http://localhost" with the name of the site you wish to publish to
 
echo Copying the feature...
 
rd /s /q "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\AnnouncementWorkflow"
mkdir "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\AnnouncementWorkflow"
 
copy /Y feature.xml  "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\AnnouncementWorkflow\"
copy /Y workflow.xml "%CommonProgramFiles%\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\AnnouncementWorkflow\"
xcopy /s /Y *.aspx "%programfiles%\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS"
 
echo Adding assemblies to the GAC...
 
"%programfiles%\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -uf AnnouncementWorkflow
"%programfiles%\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -if bin\Debug\AnnouncementWorkflow.dll
 
:: Note: 64-bit alternative to lines above; uncomment these to install on a 64-bit machine
::"%programfiles% (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -uf AnnouncementWorkflow
::"%programfiles% (x86)\Microsoft Visual Studio 8\SDK\v2.0\Bin\gacutil.exe" -if bin\Debug\AnnouncementWorkflow.dll
 
 
echo Activating the feature...
 
pushd %programfiles%\common files\microsoft shared\web server extensions\12\bin
 
::Note: Uncomment these lines if you've modified your deployment xml files or IP forms 
::stsadm -o deactivatefeature -filename AnnouncementWorkflow\feature.xml -url http://momix
::stsadm -o uninstallfeature -filename AnnouncementWorkflow\feature.xml
 
stsadm -o installfeature -filename AnnouncementWorkflow\feature.xml -force
stsadm -o activatefeature -filename AnnouncementWorkflow\feature.xml -url http://momix
 
 
echo Doing an iisreset...
popd
iisreset
 

II-E-1. Signature de l'assemblage

La première chose à faire pour pouvoir déployer son assemblage dans la GAC (global assembly cache) est de le signer. Pour cela, sélectionnez votre projet dans l'explorateur de solution de Visual Studio et cliquez sur "Properties", ensuite sur l'onglet "Signing". Vous devriez obtenir ceci

Signature de l'assemblage
Signature de l'assemblage

II-E-2. Automatiser l'exécution du fichier bat

Allez dans les propriétés de votre projet et remplissez le post-build event comme illustré ci-dessous.

Liaison avec le fichier bat
Liaison avec le fichier bat

Après cette opération, votre workflow sera automatiquement déployé vers votre serveur Sharepoint dès que vous compilerez votre projet.

II-F. Chargement des contrôles workflow de Sharepoint dans la barre d'outils

Afin de pouvoir développer des workflow spécifiques à Sharepoint, vous devez ajouter les contrôles prévus à cet effet dans la barre d'outils.

Cliquez sur la barre d'outils avec le bouton droit, choisissez "Add Tab" pour ajouter un nouvel onglet, nommez-le "WSS", ensuite, cliquez sur "Choose Items", vous devez obtenir la fenêtre suivante

Barre d'outils
Barre d'outils

Triez la liste sur la colonne "NameSpace" et localisez l'espace de nom "Microsoft.Sharepoint.WorkflowActions". Ensuite, sélectionnez toutes les entrées correspondantes. Votre barre d'outils devrait maintenant contenir tous les contrôles spécifiques

Barre d'outils
Barre d'outils

Vous êtes désormais prêts à développer un workflow pour Sharepoint.

II-G. Description sommaire des principaux types d'activité liés aux workflows

La liste ci-dessous n'est pas exhaustive mais reprend les principales activités que l'on peut utiliser pour réaliser un workflow.

Code Activity
Cette activité permet d'associer une procédure évènementielle. On l'utilisera pour toute tâche non spécifique et non prévue par un autre type d'activité
IfElseActivity
Utilisée lorsque l'on veut conditionnellement exécuter certaines étapes d'un workflow. Les expressions sont évaluées de gauche à droite. Si l'une des expressions est évaluée à Vrai, elle s'exécutera et le processus passera à l'activité suivante. Si aucune condition n'est satisfaite et que la dernière branche n'a pas de condition, elle sera considérée comme la partie "Else" et sera dès lors exécutée
WhileActivity
C'est simplement une bouche While "modélisée"
SequenceActivity
C'est une séquence d'activités. Très pratique pour pallier au pseudo problème ci-dessus.
SuspendActivity, TerminateActivity, InvokeWorkflowActivity
Permettent respectivement de suspendre, arrêter un workflow ou invoquer l'exécution d'un autre workflow.
ThrowActivity
Similaire à un Throw en dotnet ou java
ParallelActivity
Permet l'exécution parallèle simultanée de plusieurs activités. Tant que toutes les activités contenues dans une ParallelActivity ne sont pas terminées, le workflow reste dans la ParallelActivity. Ce serait le cas aussi avec une SequenceActivity sauf que la SequenceActivity impose un ordre séquentiel alors que la ParallelActivity n'en impose pas. Une ParallelActivity est intéressante à utiliser lorsque l'on a besoin de l'intervention de plusieurs acteurs pour pouvoir passer à l'activité suivante sans toutefois imposer quel tel acteur intervienne avant tel autre. Imaginons une approbation de document où 2 personnes doivent avoir lu et approuvé le document et que l'on ne souhaite pas imposer un ordre d'intervention.
DelayActivity
Similaire à un Thread.Sleep(). Dans le contexte d'un workflow, une DelayActivity sera surtout utilisée pour configurer un TimeOut
ListenActivity
Ressemble à la ParallelActivity sauf que dès que l'une des sous-activités a été réalisée, le workflow sort de la ListenActivity alors que dans une ParallelActivity, toutes les activités filles doivent être complètées
ReplicatorActivity
Boucle jusqu'à ce qu'une condition soit remplie et peut contenir plusieurs activités filles. Peut exécuter celles-ci séquentiellement ou parallèlement
EventDrivenActivity
S'exécute uniquement lorsqu'un évènement précis se produit

II-H. Description des principales activités liées à Sharepoint

Voici une liste non exhaustive des principales activités directement liées aux workflows destinés à Sharepoint

OnWorkflowActivated
Cette activité est automatiquement créée lorsque l'on crée un workflow Sharepoint. Elle ne peut d'ailleurs pas être enlevée. Comme son nom l'indique, elle s'exécute dès que le workflow a été démarré.
OnWorkflowItemChanged
Cette activité est s'exécute lorsqu'un élément d'une liste est modifié
OnWorkflowItemDeleted
Cette activité est s'exécute lorsqu'un élément d'une liste est supprimé.
SetStateActivity
Permet de cibler la prochaine activité. C'est similaire à un GoTo. Par contre, elle ne permet que de pointer vers une activité de type System.Int32 (SateActivity par ex)
OnTaskCreated, OnTaskChanged et OnTaskDeleted
S'exécutent respectivement lorsqu'une tâche associée au workflow est créée, modifiée ou supprimée.
CreateTask, UpdateTask, DeleteTask et CompleteTask
Les workflows dans Sharepoint assignent souvent des tâches aux différents acteurs. CreateTask, UpdateTask et DeleteTask permettent comme leur nom l'indique de créer/modifier/supprimer une tâche. CompleteTask permet de marquer une tâche comme étant complétée, c'est à dire "terminée".
RollbackTask
Permet de réinitialiser une tâche qui était préalablement complètée.
OnWorkflowModified
S'exécute lorsqu'un utilisateur soumet un formulaire (infopath par ex) intervenant dans le workflow
SendEmail
Durant le processus d'un workflow, les différents acteurs sont régulièrement notifiés par e-mail de l'avancement de celui-ci. Cette tâche facilite l'envoi d'email.
LogToHistoryListActivity
Permet d'afficher un message dans l'historique d'exécution d'un workflow visible directement dans Sharepoint

II-I. Contrôles de workflow intervenant dans notre exemple

Notre workflow est simplissime et tiens plus d'un "proof of concept" qu'autre chose mais voici néanmoins les différentes choses auxquelles il est judicieux de prêter attention.

Notre workflow
Notre workflow
  • La tâche "OnWorkflowActivated" est automatiquement créée par le designer et est obligatoire. On peut lui associer du code mais ceci est facultatif
  • La tâche "LogToHistoryListActivity", ici symbolisée par "History" va nous servir à créer un message qui apparaîtra dans l'historique du workflow
  • La tâche "CreateAnnoncement" est une CodeActivity qui contient le code nécessaire pour créer l'annonce dans la liste Announcements
  • Le Carré rouge contenant un cercle blanc symbolise la fin du workflow

Note: notre workflow est destiné à être utilisé lors de l'ajout d'un élément dans une liste documentaire, c'est pourquoi il n'écoute pas un évènement particulier comme OnWorkflowItemChanged ou OnWorkflowItemDeleted.

II-J. Le code

Je ne reprends ici que le code important, vous n'aurez qu'à télécharger l'exemple pour disposer du code complet.

Afin de ne pas coder en dur la liste cible ni le serveur Sharepoint, j'ai défini ces variables dans les settings du workflow comme illustré ci-dessous.

Configuration
Configuration

Propriété importante:

 
Sélectionnez

public Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties workflowProperties = new Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties();

Cette déclaration et instanciation automatiquement créées par le template de workflow représentent les propriétés de notre workflow. C'est par elles qu'il sera notamment possible de récupérer les valeurs de l'élément de liste ajouté/modifié/supprimé. wokflowProperties contient notamment les propriétés "BeforeProperties" et "AfterProperties" permettant de récupérer les anciennes et les nouvelles valeurs lors des mises à jour.

Voici le code lié à notre CodeActivity destiné à créer l'annonce dans la liste Announcements.

 
Sélectionnez

private void CreateAnnoncement_ExecuteCode(object sender, EventArgs e)
        {
            SPSite Site = null;
            SPWeb Web = null;
            SPList AnnouncementList = null;
            SPListItem NewItem = null;
 
            try
            {
                Site = new SPSite(AnnouncementWorkflow.Properties.Settings.Default.TargetSiteUrl);
                //Ouverture d'un SPWeb, càd un site
                Web = Site.OpenWeb();
                //0n pointe sur la liste d'annonces
                AnnouncementList = Web.Lists[AnnouncementWorkflow.Properties.Settings.Default.AnnouncementList];                
                //On instancie un nouvel élément (nouvelle ligne pour la liste)
                NewItem = AnnouncementList.Items.Add();
                //On crée un petit message
                NewItem["Title"] = "Le document " + workflowProperties.Item["Title"] + " a été mis en ligne";
                //On crée un corps de message contenant l'URL du document
                NewItem["Body"] = "Vous pouvez consulter le document " + workflowProperties.Item["Title"] +
                    " <a href='" + workflowProperties.Item["EncodedAbsUrl"] + "'>ici</a>";
                //On met à jour l'élément de liste
                NewItem.Update();
                //On récupère l'ID
 
                AnnouncementList.Update();
                Web.Update();               
 
                //On met un message dans l'historique.
                History.HistoryDescription = "Annonce " + NewItem.ID.ToString() + " créée avec succès";
 
            }
            catch(SPException Ex)
            {
                History.HistoryDescription = Ex.Message;
            }          
			finally
			{
 
				if (Web != null) Web.Close();
                if (Site != null) Site.Close();
                NewItem = null;
                AnnouncementList = null;
                Web = null;
                Site = null;
			} 
        }

Si vous disposez de plusieurs étapes dans votre workflow, il est indispensable de bien remettre à null les objets liés à la manipulation des éléments de listes sous peine de dysfonctionnements sérieux.

Note: Le système de workflow met à notre disposition des FaultHandlers pour gérer les exceptions, je ne les aborde pas dans ce tutoriel car ces composants mériteraient sans doute un tutoriel dédié. L'exemple fourni n'est donc pas optimal en matière de gestion d'erreur.

III. Activer/Désactiver le workflow déployé en tant que Feature

Si vous avez bien exécuté toutes les étapes décrites dans les sections précédentes, vous devriez retrouver votre workflow dans la partie Site Collection Features de votre collection de sites. Vous pouvez accéder à celle-ci en allant dans Site Actions -> Site Settings -> Site Collection Features

Un des avantages de la Feature est justement la capacité d'activer/désactiver la fonctionnalité qu'elle contient.

Activer/Désactiver le workflow
Activer/Désactiver le workflow

IV. Associer le workflow à une liste

Lorsque le workflow a été activé, vous devriez pouvoir l'associer à la liste. Ce workflow n'a d'ailleurs pas été conçu pour fonctionner avec une liste en particulier. Il peut en théorie fonctionner avec n'importe quelle liste documentaire car il ne référence aucun champ spécifique non standard.

Le chemin complet pour arriver à l'écran d'association est souligné en rouge sur l'image.

Associer le workflow à la liste
Associer le workflow à la liste

V. Téléchargement

Vous pouvez télécharger l'exemple ici

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.