Monday, July 30, 2007

SharePoint 2007 : Workflow et Association Form avec InfoPath 2007

Aujourd'hui je vais m'intéresser à la création d'un formulaire d'association pour un Workflow.
Un formulaire d'association est une page qui apparaît lorsqu'on crée une instance de Workflow : on choisit tout d'abord le modèle de Workflow, puis la Task list qui sera associée, puis l'History List, et enfin le mode d'activation du Workflow (Automatique, manuel...)
En cliquant sur le bouton Next, j'obtiens mon propre formulaire qui me permet de récupérer des informations complémentaires dés l'instanciation du Workflow. En l'occurrence, un flag qui m'indique si un mail doit être envoyé au "Supplier", puis un commentaire à insérer dans la description de tâche. Ainsi ce texte se retrouvera dans chaque tâche. Pour créer un tel formulaire, et l'associer au workflow, il est préférable d'utiliser InfoPath 2007 qui offre beaucoup d'avantages par rapport aux pages ASPX. Voici en quelques étapes comment faire pour construire l'écran précédent et l'intégrer dans le workflow. Principe : Un formulaire InfoPath est est un fichier XML associé à un Schéma XSD. Il y a une partie mise en forme et une partie données. L'astuce principale est de transformer le shéma en une classe C# (.cs) pour disposer facilement des propriétés (Data Source) au sein du Workflow. Les propriétés seront, entre autre, des champs de saisie dans l'écran InfoPath. Préliminaires Il faut avoir créer un projet de type Workflow sous Visual Studio, puis avoir créé une arborescence prête à accueillir les fichier d'installation
Etape 1 : créer l'écran
  1. Ouvrir InfoPath et créer un nouveau Template.
  2. Il faut ensuite créer une "Data Source" (Design Tasks / Data Source), ensemble de variables qui mémoriserons les données entrées. C'est grâce à ces Data Source que l'on va pouvoir dialoguer avec le formulaire. Une bonne pratique pour créer la Data Source est de lui donner un nom explicite. Par ailleurs, ce nom sera plus tard le nom de la classe qui sera générée pour être utilisée dans le Workflow. Dans mon exemple, j'ai pris comme nom de "Data Source" AssocData et j'ai créé deux champs, l'un booleen, l'autre de type texte mutillignes
  3. Mise en forme du formulaire : une fois les champs créés, il faut, dans le menu Design Tasks / Layout faire la mise en forme. Sélectionnez un tableau à deux colonnes, puis retournez dans Design Tasks / Data Source pour faire un Drag & Drop des champs aux bons emplacements. Ajoutez ensuite un bouton (Design Tasks / Controls)
  4. Il faut ensuite paramétrer le bouton pour que le formulaire soit envoyé à SharePoint (à l'instance de workflow en cours), puis que le formulaire soit fermé. Pour cela, il faut faire un click doit sur le bouton et aller dans "Button Properties"... ... et mettre le label (ici "Done"), puis cliquer sur Rules...
  5. A partir de là, il va falloir créer une Data Connexion pour se connecter au Host (SharePoint) et renvoyer les données. Cliquez sur Add Actions puis Add... pour ajouter une Data Connexion Utilisez les mêmes paramètres (on veut faire une soumission de formulaire) et cliquez sur Next... et selectionnez le "Hosting Environment". Puis donnez un nom explicite à cette Connexion.
  6. Il faut ensuite ajouter une action (Add Action) pour fermer le formulaire (choisissez cette option dans la liste)
Etape 2 : Publication
  1. Voilà, le formulaire est prêt. Maintenant il faut (1) le publier, (2) récupérer le schéma. (1) Publication : le mécanisme de publication, dans notre cas, consiste à publier le formulaire dans notre répertoire d'installation de la feature Workflow. Tout d'abord, il faut enregistrer le formulaire quelque part où vous le retrouverez (My Document/Forms par exemple). Puis cliquez sur "Publish Form Template..." et choisir une "Network Location" puis Next Enregistrer le formulaire dans votre répertoire de projet Visual Studio dans le sous-répertoire concerné par la feature Workflow (dans mon cas, GYPOWorkflow) et donnez lui un nom explicite (celui de votre sauvegarde pourra faire l'affaire) Ne mettez rien dans cette form et ignorez l'avertissement. Publiez, c'est fait.
  2. Un fois que c'est publié, il faut encore se faciliter le travail en créant une classe C# à partir du schéma du formulaire. Cette classe aura comme propriété les champs du formulaire. Pour se faire, il faut enregistrer le code source du formulaire dans un répertoire InfoPath va générer un lot de fichier dont seul myschema.xsd nous intéresse Nous allons utiliser le programme XSD.EXE fournit avec VSStudio pour générer une classe C#. Le nom du fichier cs généré portera le nom du fichier xsd (myschema actuellement) mais la classe elle-même portera le nom de notre Data Source (AssocData pour l'exemple). Par conséquent je vous conseille de renommer myschema.xsd en AssocData.xsd pour être cohérent. Ensuite, ouvrez une fenêtre DOS, allez dans le répertoire où est situé votre shéma (My Document\XSD_Sources dans mon cas) puis tapez l'instruction suivante : xsd myschema.xsd /c Vous aurez ensuite un fichier portant le nom myschema.cs (ou AssocData.cs si vous avez renommé le fichier) qui contient une structure particulière et les accesseurs nécessaires pour retrouver les données du formulaire.
  3. Ceci étant fait, retournez sous VS et ajoutez cette classe à votre projet.
  4. Dans l'événement OnWorkflowActivated1_Invoked, ajoutez les lignes de code suivantes: //Get the info about sending email to supplier from the association form //Part 1 XmlSerializer serializer = new XmlSerializer(typeof(AssocData)); XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(workflowProperties.AssociationData)); //Part 2 AssocData AssocForm = (AssocData)serializer.Deserialize(reader); //Internal variables eMailToSupplier = (bool)AssocForm.eMailToSupplier; reviewComments = AssocForm.reviewComment; La première partie du code récupère le fichier xml InfoPath sérialisé qui est stocké dans une propriété (AssociationData) du Workflow. La deuxième partie désérialise le XML en faisant un Cast sur l'objet AssocData. Ceci permet ensuite d'utiliser les propriétés de l'objet pour récupérer les données et les stocker dans des variables locales au Workflow pour un usage ultérieur.
Etape 3 : Paramétrage de la feature
  1. une fois que le code est écrit et compilé, jeté dans le GAC, il faut créer une feature qui va se charger d'assembler tout ceci en quelque chose de cohérent. Dans un répertoire, il vous faudra avoir un fichier feature.xml, un fichier workflow.xml et votre formulaire, GYWFPOAssocForm.xsn dans mon cas. (ne vous occupez pas pour l'instant du quatrième fichier, qui fera l'objet d'un autre article) Le fichier feature.xml se présente de la façon suivante : <?xml version="1.0" encoding="utf-8"?> <Feature Id="B72AA389-51B8-4d7b-8730-F5A2885F9E93" Title="Purchase Order Workflow" Description="Purchase Order workflow, automated review process" Version="12.0.0.0" Scope="Site" ReceiverAssembly="Microsoft.Office.Workflow.Feature, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" ReceiverClass="Microsoft.Office.Workflow.Feature.WorkflowFeatureReceiver" xmlns="http://schemas.microsoft.com/sharepoint/"> <ElementManifests> <ElementManifest Location="workflow.xml" /> </ElementManifests> <Properties> <Property Key="GloballyAvailable" Value="true" /> <Property Key="RegisterForms" Value="*.xsn" /> </Properties> </Feature> Puis le fichier workflow.xml : <?xml version="1.0" encoding="utf-8" ?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Workflow Name="Purchase Order Workflow" Description="Puchase Order Workflow, automated review process" Id="869774CD-6710-4583-938B-5B691903CCBF" CodeBesideClass="MyWorkflow.WFPO" CodeBesideAssembly="MyWorkflow, Version=1.0.0.0, Culture=neutral, PublicKeyToken=53941405af02689c" TaskListContentTypeId="0x01080100C9C9515DE4E24001905074F980F93160" AssociationUrl="_layouts/CstWrkflIP.aspx" ModificationUrl="_layouts/ModWrkflIP.aspx" StatusUrl="_layouts/WrkStat.aspx"> <Categories/> <!-- Tags to specify InfoPath forms for the workflow; delete tags for forms that you do not have --> <MetaData> <Association_FormURN>urn:schemas-microsoft-com:office:infopath:GYWFPOAssocForm:-myXSD-2007-05-14T07-58-42</Association_FormURN> <AssociateOnActivation>false</AssociateOnActivation> </MetaData> </Workflow> </Elements> Pour obtenir l'URN du formulaire InfoPath, il faut charger le formulaire dans InfoPath (design Template), puis aller dans File / Properties et copier/coller l'urn entre les tags CodeBesideClass et CodeBesideAssembly correspondent à la DLL et la classe de votre workflow.
  2. Ceci fait, il faut copier le répertoire d'installation (GYPOWorkflow) dans le répertoire "12\Template\Feature" et il vous faut installer (stsadm -o install feature GYPOWorkflow\feature.xml) et activer (stsadm -o activatefeature -filename "GYPOWorkflow\feature.xml" -url http://dcmoss:50010/) la feature.
Voilà, c'est tout, normalement cela doit marcher. C'est un peu laborieux, mais une fois que le formulaire est enregistré, il est très simple de le modifier. Et c'est très joli !

Wednesday, July 18, 2007

Champs type Lookup et type People (update) et type Link

Je reviens rapidemment sur les deux champs à saisie simple et multiple, les champs Lookup et People Comme je le précise dans mon article précédent, ils renvoient une chaîne de caractères bizarres composés d'un couple ID/Libellé ( "#1;#data1#;#2#data2#;") Pour pouvoir accéder facilement aux données, il faut réussir à utiliser au final un type SPFieldLookupValue qui a deux propriétés utiles: - LookupId : pour récupérer l'Id de l'item de la liste liée dans le lookup - LookupValue : pour récupérer le libellé (ce qu'on voit dans la liste) Dans le cas d'une sélection multiple, on doit utiliser le constructeur de SPFieldLookupValueCollection avec en paramètre la chaîne de caractère à traiter : SPFieldLookupValueCollection spcol = new SPFieldLookupValueCollection(string_from_field) Ce qui retournera une collection de SPFieldLookupValue Par contre, si c'est une sélection unique, deux cas se présentent : Cas 1 : c'est un champ de type Lookup, alors on peut utiliser le contructeur de SPFieldLookupValue : SPFieldLookupValue spfield = new SPFieldLookupValue(string_from_field) Cas 2 : c'est un champ de type People, alors ce champs renvoit un Integer qui est l'Id du SPUser : SPUser user = web.AllUsers.GetByID(userId); Le champs de type Link Le champ de type Link a besoin de deux informations : - l'URL - La description Pour adresser un tel champs, il faut utiliser l'objet SPFieldUrlValue et renseigner les deux propriétés Description et Url : SPFieldUrlValue linkURL = new SPFieldUrlValue(); linkURL.Description = description; linkURL.Url = link; La description représente le libellé qui apparaîtra dans la colonne. Ce libellé sera un lien vers Url Voilà trois champs que l'on rencontre très souvent et qui sont un petit peu pénible à utiliser.