jeudi 7 mai 2009

Blog déplacé

Ce blog est désormais disponible à l'adresse suivante: http://blogs.developpeur.org/shareseb

mercredi 25 mars 2009

[SharePoint 2007] Feature permettant de masquer les colonnes d'une liste en fonction de critères

Voici une feature disponible en téléchargement sur CodePlex permettant de masquer les colonnes d'une liste de façon systématique ou suivant des critères (comme l'appartenance du user à un groupe par exemple) pour les 3 modes d'affichage (View, New, Edit).

Une feature qui peut être bien utile pour éviter la création de modèles de listes Custom...

http://www.codeplex.com/SPListDisplaySetting

Sinon vous pouvez faire le même genre de manipulation avec ce petit outil sous la forme d'une application WinForms: http://patrikluca.blogspot.com/2008/08/hide-list-fields-upon-creation-of.html

lundi 9 mars 2009

[SharePoint 2007] Développer un workflow d'approbation custom utilisant le formulaire InfoPath standard

Lorsque l'on a besoin de customiser une fonctionnalité dans SharePoint, il est souvent nécessaire de repartir de zéro... Heureusement, il est parfois possible de ré-utiliser en partie l'existant.

J'avais besoin de créer un workflow d'approbation dont le comportement réponde réellement aux contraintes de mon client, mais que les formulaires de validation gardent le look-n-feel des formulaires standard de SharePoint :



Je ne vais pas revenir sur l'ensemble du processus de création d'un Workflow utilisant des formulaires InfoPath (cette page le fait très bien: http://stephaneey.developpez.com/tutoriel/sharepoint/workflowforms/) mais juste détailler ce qu'il faut savoir pour utiliser le formulaire de tâche d'approbation "out-of-the-box" avec un workflow custom.

Pour cela, il faut déjà configurer la feature de mon workflow de la manière suivante:

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Workflow
Name="MonWorkflowAppro"
Description="Description de mon workflow d'approbation"
Id="e8c83205-5791-4c6d-8798-e35e5cad77ca"
CodeBesideClass="MonProjet.MonWorkflowAppro"
CodeBesideAssembly="MonProjet, Version=1.0.0.0, Culture=neutral, PublicKeyToken=99ad5b5da4846ba1"
TaskListContentTypeId="0x01080100C9C9515DE4E24001905074F980F93160"
AssociationUrl="_layouts/CstWrkflIP.aspx"
InstantiationUrl="_layouts/IniWrkflIP.aspx"
ModificationUrl="_layouts/ModWrkflIP.aspx"
StatusUrl="_layouts/WrkStat.aspx">

<Categories/>
<MetaData>
<InitiationType>OnNewItem</InitiationType>
<Task1_FormURN>urn:schemas-microsoft-com:office:infopath:workflow:ReviewRouting-Review:$Subst:LCID;</Task1_FormURN>
<AssociateOnActivation>true</AssociateOnActivation>
</MetaData>
</Workflow>
</Elements>

Je garde les url des pages aspx standard pour afficher les formulaires (Association, Instanciation, etc) et je fournis l'urn du formulaire InfoPath standard dans Task0_FormURN.
Un workflow peut créer différents types de tâches qui sont identifiées par un entier (0, 1, 2, ...), ici j'ai associé l'urn de mon formulaire InfoPath au type 0, il faut donc, lorsque que je créé ma tache dans mon workflow, lui assigner le type 0.
Pour cela, dans la méthode Invoking de mon activité CreateTask, je vais assigner 0 à la propriété TaskType de mes TaskProperties.

Autre point: le formulaire standard contient 2 liens en bas de page "Réaffecter la tâche" et "Demande de modification". Vous ne souhaitez peut être pas implémenter ces fonctionnalités dans votre workflow, il peut donc vous être utile de désactiver ces liens.
Pour cela, toujours dans le méthode Invoking de votre activité CreateTask, il faut mettre la valeur False aux entrées ows_AllowDelegation et ows_AllowChangeRequests des ExtendedProperties de vos TaskProperties:

MyTaskProps.ExtendedProperties["ows_AllowDelegation"] = "False"; MyTaskProps.ExtendedProperties["ows_AllowChangeRequests"] = "False";

Une fois votre tâche correctement créée par votre workflow vous voulez à juste titre récupérer la réponse saisie par l'utilisateur "Approuvé" ou "Rejeté".

Pour cela, dans votre méthode qui sera déclenché à la modification de la tâche, vous allez pouvoir utiliser la collection AfterProperties qui contient les valeurs du formulaire.
Pour récupérer le statut, il faut tester la valeur de MyTaskAfterProps.ExtendedProperties["TaskStatus"] qui prend "#" si approuvé et "@" si rejeté.

Si vous n'avez pas désactivé les boutons de délégation ou demande de modification, vous pouvez récupérer les infos suivantes:

Délégation:
MyTaskProps.ExtendedProperties["Decline"] prend les valeurs 0, 1 ou 2
0 = pas de délégation
1 = délégué à l'initiateur du workflow
2 = délégué à la personne définie par MyTaskProps.ExtendedProperties["DelegateTo"]

Demande de modification
MyTaskProps.ExtendedProperties["dcr"] prend les valeurs 0, 1 ou 2
0 = pas de demande
1 = demande à l'initiateur du workflow
2 = demande à la personne définie par MyTaskProps.ExtendedProperties["DelegateTo"]

Voila vous avez maintenant toutes les billes pour vous interfacer avec ce formulaire de tâche d'approbation.

vendredi 20 février 2009

[SharePoint 2007] Récuperer un SPUser à partir de la valeur d'un colonne de type Personne

Pour le développement d'un workflow SharePoint, j'avais besoin de créer dynamiquement une tâche et de l'assigner à un utilisateur sélectionné, via une colonne de type Personne, lors de la création de l'élément qui déclenche mon workflow.

Et là problème: la valeur que je récupère de ma colonne est une chaîne de caractères du type "18;#NOM, Prénom" (l'id du SPUser et le display name).

Or pour assigner ma tâche, j'ai besoin de lui spécifier le login name de l'utilisateur, c'est à dire "DOMAIN\username".

C'est un problème que j'avais déjà rencontré lors du développement d'event handlers.

Voici un petit bout de code qui permet de récupérer l'objet SPUser à partir de la valeur stockée dans une colonne de type Personne:

string columnName = "Nom de ma colonne";
string columnValue = workflowProperties.Item[columnName].ToString();
SPFieldUser userField = (SPFieldUser)workflowProperties.List.Fields.GetField(columnName);
SPFieldUserValue fieldValue = (SPFieldUserValue)userField.GetFieldValue(columnValue);
SPUser user = fieldValue.User;


Ici, je récupère l'objet SPList concernée et la valeur de ma colonne via mes WorkflowProperties, mais dans le cas d'un Event Handler, je les aurai récupérés via les SPItemEventProperties.

Ensuite, avec mon SPUser, je peux récupérer toutes les infos dont j'ai besoin, notamment le LoginName.

mardi 17 février 2009

[SharePoint 2007] Requêtes XPath sur les noeud XML retournés par les WebServices SharePoint

Il peut être très utile dans certains cas de communiquer avec SharePoint en utilisant ses WebServices, depuis une machine en dehors de la ferme par exemple.

Ceux-ci sont relativement simples d'utilisation lorsque vous êtes habitués à utiliser des WebServices et l'essentiel du modèle objet est exposé:
  • Administration
  • Alerts
  • Authentication
  • Copy
  • Document Workspace
  • Forms
  • Imaging
  • List Data Retrieval
  • Lists
  • Meetings
  • People
  • Permissions
  • SharePoint Directory Management
  • Site Data
  • Sites
  • Search
  • Users and Groups
  • Versions
  • Views
  • Web Part Pages
  • Webs
La référence est disponible sur MSDN: http://msdn.microsoft.com/en-us/library/cc752745.aspx

Voici un exemple d'instanciation du WebService Users and Groups:

UserGroup wsUserGroup = new UserGroup();
wsUserGroup.Credentials = System.Net.CredentialCache.DefaultCredentials;
wsUserGroup.Url = mySiteUrl + "/_vti_bin/UserGroup.asmx";


Mais attention ! Les méthodes retournent toutes des objets XmlNode qui sont dépendants de namespace URI.
Donc pour éviter de galérer avec des SelectSingleNode qui retourne null, il est nécessaire de rajouter des préfixes à vos requêtes XPath:

Exemple avec la méthode GetGroupCollectionFromSite() de mon WebService précédemment instancié qui me retourne un XmlNode du type:

<GetGroupCollectionFromSite xmlns=
"http://schemas.microsoft.com/sharepoint/soap/directory/">
<Groups>
<Group ID="3" Name="Group1" Description="Description" OwnerID="1"
OwnerIsUser="False" />
<Group ID="15" Name="Group2" Description="Description"
OwnerID="12" OwnerIsUser="True" />
<Group ID="16" Name="Group3" Description="Description"
OwnerID="7" OwnerIsUser="False" />
</Groups>
</GetGroupCollectionFromSite>


Voici donc une méthode qui déclare toutes les URI utilisées par SharePoint avec des préfixes choisis arbitrairement qui seront utilisés par nos requêtes XPath:

private XmlNodeList RunXPathQuery(XmlNode XmlNodeToQuery, string XPathQuery)
{
// load the complete XML node and all its child nodes into a XML document
XmlDocument Document = new XmlDocument();
Document.LoadXml(XmlNodeToQuery.OuterXml);

// all the possible namespaces used by SharePoint and a randomly choosen prefix
const string SharePointNamespacePrefix = "sp";
const string SharePointNamespaceURI = "http://schemas.microsoft.com/sharepoint/soap/";
const string ListItemsNamespacePrefix = "z";
const string ListItemsNamespaceURI = "#RowsetSchema";
const string PictureLibrariesNamespacePrefix = "y";
const string PictureLibrariesNamespaceURI = "http://schemas.microsoft.com/sharepoint/soap/ois/";
const string WebPartsNamespacePrefix = "w";
const string WebPartsNamespaceURI = "http://schemas.microsoft.com/WebPart/v2";
const string DirectoryNamespacePrefix = "d";
const string DirectoryNamespaceURI = "http://schemas.microsoft.com/sharepoint/soap/directory/";

// now associate with the xmlns namespaces (part of all XML nodes returned
// from SharePoint) a namespace prefix which we can then use in the queries
XmlNamespaceManager NamespaceMngr = new XmlNamespaceManager(Document.NameTable);
NamespaceMngr.AddNamespace(SharePointNamespacePrefix, SharePointNamespaceURI);
NamespaceMngr.AddNamespace(ListItemsNamespacePrefix, ListItemsNamespaceURI);
NamespaceMngr.AddNamespace(PictureLibrariesNamespacePrefix, PictureLibrariesNamespaceURI);
NamespaceMngr.AddNamespace(WebPartsNamespacePrefix, WebPartsNamespaceURI);
NamespaceMngr.AddNamespace(DirectoryNamespacePrefix, DirectoryNamespaceURI);

// run the XPath query and return the result nodes
return Document.SelectNodes(XPathQuery, NamespaceMngr);
}


Je serai donc capable d'executer des requêtes XPath sur mon noeud de la maniere suivante:

XmlNode myNode = wsUserGroup.GetGroupCollectionFromSite();
XmlNodeList myGroupList = RunXPathQuery(myNode, "/d:Groups/d:Group[@ID='16']");
Cet exemple de requête XPath me retourne les noeuds dont l'attribut ID est égale à 16.

Références:
Talk to SharePoint Through its Web Services

vendredi 13 février 2009

[SharePoint 2007] WebPart de sondage




Voici une page sur laquelle vous pouvez télécharger une WebPart permettant d'afficher le formulaire et les résultats d'un sondage.

Cette WebPart se branche sur une liste de type "Enquête" et affiche dans une seule et même zone le questionnaire ou les résultats si l'utilisateur courant a déjà répondu.

Les résultats peuvent être affichés en barre ou en camembert, et sont générés en PNG avec la librairie GDI+ de System.Drawing.

Tout cela est disponible en Setup ou avec les sources C# donc facilement "customizable".

http://darrenjohnstone.net/2008/02/17/sharepoint-quick-surveys/

vendredi 23 janvier 2009

[SharePoint 2007] Le point sur les diverses limitations

Au cours de mes expériences d’intégration de SharePoint en entreprise, j’ai entendu pas mal de sons de cloche différents sur les diverses limitations réelles ou recommandées : nombres de documents par liste, taille de la base de contenu, etc.

Voici quelques éléments pour faire le point:

Tout d’abord, la taille des bases de contenu :

Il n’y a aucune limitation liée à SharePoint, cela dépend juste de la version de SQL Server utilisée :

  • Avec SQL Server 2005 Express, vous avez une limitation dans la taille des bases de 4Go, et 2Go avec la version précédente : MSDE.
  • Avec SQL Server 2005, il n’y a aucune limite à part l’espace disponible sur le disque et les éventuelles contraintes imposées par les DBAs.
Dans la plupart des cas, pour conserver des performances correctes, Microsoft recommande de ne pas dépasser les 100Go pour une base de contenu, comme l'indique ce document publié par l'équipe SharePoint: Performance recommendations for storage planning and monitoring

Pour ce qui concerne les composants SharePoint, voici un article sur technet qui fixe les limites à ne pas dépasser pour garder des performances acceptables, il n’y a aucune limite technique qui soit gravée dans le marbre:

http://technet.microsoft.com/en-us/library/cc262787.aspx