Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

SharePoint et plus...

Les technologies de développement Web Microsoft en français dans le texte.
[SharePoint 2010] Migration d'une WebApplication vers Claims
Voici mon premier post depuis mon installation récente de l'autre coté de l'atlantique, dans cette belle ville de Montréal.
Dernièrement, j'ai eu l'occasion de travailler sur un projet de migration de MOSS 2007 avec authentification Windows vers SharePoint 2010 avec authentification en mode Claims avec ADFS v2.Je ne reviendrai pas sur la configuration de SharePoint 2010 et ADFS v2, cela a déjà était fait en détail ici par exemple.D'ailleurs ici la problématique n'est pas spécifiquement lié a ADFS v2, ni même a la migration mais simplement le passage de l'authentification Basic a Claims pour des utilisateurs déjà référencés sur une application existante.

Supposons donc que vous ayez configuré votre SharePoint 2010 pour fonctionner avec ADFS v2.Nous pouvons donc créer une nouvelle Application Web qui utilise les Claims et sélectionner votre Trusted Identity Provider ADFS comme mode d'authentification possible.


Nous pouvons alors tester les collections de sites nouvellement migrées avec le compte de service: aucun problème a signaler !

Ceci est normal ! Les utilisateurs sont toujours référencés dans la base de contenu en tant DOMAIN\utilisateur alors qu'ils devraient désormais être référencés avec le format Claims du type i:xx.x|ProviderName|utilisateur. (une petite vérification dans la table UserInfo permet de constater cet état).

$w = Get-SPWebApplication "http://<server>/"
$w.MigrateUsers($True) // True signifie la migration vers Claims, False depuis Claims
Et voila une gang d'utilisateurs prêts a 'Claimser' en tabarnak !!!
[SharePoint 2010] Automatiser la mise à jour des photos de profils utilisateurs avec PowerShell
Première constatation: SharePoint 2010 est la dernière version de SharePoint à intégrer la ligne de commande STSADM pour son administration. En 2014, bye bye STSADM !!! Il est donc temps de commencer à regarder vers PowerShell pour voir a quel point l'administration de SharePoint peut être simplifiée avec les CmdLet spécialisées.

Deuxième constatation: Que ce soit pour l'implémentation des MySites en environnement de production ou à des fins de démo, il pourrait vous arriver d'avoir besoin de mettre à jour en masse les photos des profils utilisateurs de SharePoint Server 2010.


Mon idole utilise SharePoint !

Voici donc un petit tutorial permettant de réaliser cette tâche de façon automatisée avec PowerShell.

La première étape
consiste a retailler les photos pour qu'elles s'intègre parfaitement à l'affichage et ce dans tous les cas (My Profile, Organization Browser, ...):

Par défaut, lorsqu'un utilisateur uploade sa photo via le formulaire sur son site personnel, SharePoint génère automatiquement 3 miniatures de taille différentes. Ces photos sont sauvegardées dans la bibliothèque "User Photos" du site hôte des MySites dans un dossier "Profile Pictures".
Ces 3 photos seront donc retaillées en gardant la proportion. Pour chaque photos, le coté le plus grand (c'est à dire la largeur en mode paysage ou la hauteur en mode portrait) auront respectivement les tailles suivantes:

  • 144px pour la photo principale du profil sur le MySite,
  • 96px pour la photo de l'utilisateur sélectionné dans le navigateur hiérarchique Silverlight (Org Browser),
  • 32px pour les photos des utilisateurs de la hierarchie dans l'Org Browser.

L'idéal pour avoir une belle photo bien intégrée dans tous les cas étant de fournir des photos carrées avec une taille de coté supérieure ou égale à 144px.
Pour réaliser le retaillage de façon automatique sur un grand nombre de fichiers, vous pouvez utiliser des outils pros tel que PhotoShop ou un des multiples outils gratuits du net tel que: FotoSizer  ou FastStone Photo Resizer


La deuxième étape consiste à uploader les fichiers dans la bibliothèque "User Photos" du site hôte des MySites. Vous pouvez biensur le faire manuellement, mais c'est tellement pus fun de le faire via PowerShell !!!

Voici un exemple de code uploadant l'ensemble des fichiers d'un dossier:

$mysiteurl = "http://sharepoint/my/"
$web = Get-SPWeb $mysiteurl
$folder = $web.GetFolder("User Photos")
$spfiles = $folder.Files

$files = ls "Profiles Pictures - 200x200"
foreach($f in $files) {
  $spfile = $spfiles.Add(“User Photos/$($f.Name)”,$f.OpenRead(),$true)
}

J'ouvre le Web avec la cmdlet Get-SPWeb puis je récupère dossier racine de la bibliothèque en lui passant le nom de celle-ci. Je peux ensuite récupérer la liste des fichiers du dossier avec la collection Files.
La cmdlet ls me permet de boucler sur l'ensemble des fichier du dossier en local. Puis j'utilise la méthode Add de SPFileCollection, qui possède une multitude de surcharges, pour ajouter/uploader le fichier. Dans ce cas, je passe à cette méthode le nom du fichier cible, la FileStream du contenu du fichier local,  et un booléen indiquant s'il faut ou non écraser le fichier existant.


La troisième étape consiste à faire le lien entre mes profils existants et mes photos tout juste uploadées:

Pour cela, j'ai décidé de nommer mes photos en respectant la nomenclature [login].jpg.
Voici un exemple de code bouclant sur l'ensemble des profils utilisateurs et modifiant la valeur du champ "PictureURL" en utilisant le login (AccountName) comme nom de fichier JPEG:

$mysiteurl = "http://sharepoint/my/"
$domain = "CONTOSO\\"
$site = Get-SPSite $mysiteurl
$context = Get-SPServiceContext $site
$ProfileManager = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context)
$profiles = $ProfileManager.GetEnumerator()
foreach ($userProfile in $profiles) {
 $login =
$userProfile["AccountName"].Value
 $login = $login -replace $domain
 $url = $mysiteurl  + "User%20Photos/" + $login + ".jpg"
 $userProfile["PictureURL"].Value = $url
 $userProfile.Commit()
}

A ce moment de la procédure, vos profils ont effectivement une photo mais celle-ci a toujours sa taille d'origine et cela peut provoquer des soucis d'affichage, notamment sur l'OrgBrowser Silverlight qui ne resize pas automatiquement la photo:


La quatrième étape consiste donc à demander à SharePoint de générer les 3 miniatures aux tailles adéquates:

Pour cela, SharePoint propose une cmdlet magique qui va générer les 3 miniatures pour l'ensemble des profils utilisateurs et mettre à jour les propriétés du profil correspondantes.

Update-SPProfilePhotoStore –MySiteHostLocation "http://sharepoint/my/"


Et voila, vous savez tout pour mettre à jour rapidement et simplement les photos des profils utilisateurs SharePoint.
Je ne veux donc plus voir trainer une seule silhouette de Bill Gates Big Smile


[SharePoint 2010] Introduction au Modèle Objet Client

SharePoint 2010 offre désormais la possibilité, grâce au Modèle Objet Client, de concevoir des applications clientes qui interagissent avec SharePoint sans pour autant exécuter du code sur l'environnement SharePoint. Cela vous permet de créer des applications .Net, Silverlight ou ECMAScript (JavaScript, JScript) qui accèdent à SharePoint sans avoir à déployer sur la ferme SharePoint et sans avoir à utiliser les Web Services de SharePoint.

Le Modèle Object Client met à disposition une API qui est assez proche de son équivalent serveur. Voici une équivalence de classes clientes et serveur:
ClientContext > SPContext
Site > SPSite
Web > SPWeb
List > SPList
ListItem > SPListItem
Field  > SPField

Globalement, à part pour le ClientContext, il suffit juste d'enlever le préfixe SP... Big Smile

  • Pour utiliser cette API dans une application .Net, votre projet doit faire référence aux assemblies suivantes : Microsoft.SharePoint.Client.dll et Microsoft.SharePoint.Client.Runtime.dll, que vous pouvez trouver dans le dossier ISAPI de SharePoint et que vous pouvez copier sur votre machine de développement.
  • Pour un projet Silverlight, il s'agit des assemblies: Microsoft.SharePoint.Client.Silverlight.dll et Microsoft.SharePoint.Client.Runtime.Silverlight.dll, que vous pouvez trouver dans le dossier TEMPLATE\LAYOUTS\ClientBin.

Voici un petit exemple de code d'un projet Console affichant les titres des listes d'un site donné:

using (ClientContext ctx = new ClientContext("http://monsite"))
{
Web web = ctx.Web;
ListCollection lists = web.Lists;
ctx.Load(web);
ctx.Load(
lists);

ctx.ExecuteQuery();

foreach (List list in
lists)
{
Console.WriteLine("Liste: {0}", list.Title);
}
}

Les points importants:
  • ClientContext pour se connecter à un site SharePoint
  • ClientContext.Load() pour initier les objets à utiliser
  • ClientContext.ExecuteQuery() pour exécuter les requêtes et provisionner les objets
==> Ce qui est intéressant et va vous permettre de gérer finement les appels au serveur, c'est que le serveur n'est contacté qu'au moment de l'appel de la méthode ExecuteQuery() (Les commandes sont en fait batchées sur le client, envoyées au service client.svc qui retourne ensuite le résultat). Vous pouvez donc charger votre hiérarchie d'objet (Web, List, Items, ...) et contacter le serveur qu'au moment voulu.


Voilà, nous avons réussi à interagir avec SharePoint, très simplement, sans installer de code sur la ferme et sans utiliser les Web Services. Que du bonheur ! Il ne vous reste plus qu'a imaginer des cas d'utilisation...

Pour aller plus loin: http://msdn.microsoft.com/fr-fr/library/ee857094.aspx

Quest Notes Migrator for SharePoint
J’ai eu l’occasion, à plusieurs reprises ces derniers temps, de réaliser des missions de migration Notes vers SharePoint 2007 mettant en œuvre l’outil Notes Migrator for SharePoint développé par Quest, mais je n’avais pas encore exprimé mes retours.

Notes Migrator for SharePoint est un outil qui permet de migrer des documents Notes, QuickPlace (Espace de travail collaboratif) ou Domino.Doc (Gestion documentaire) vers des listes, bibliothèques de documents ou bibliothèques de formulaires SharePoint.

Ce qui frappe au premier abord, c'est la simplicité d'utilisation notamment du Designer qui permet de créer des jobs de migration en sélectionnant les champs dans la base Notes source, les colonnes dans la liste SharePoint cible et le mapping entre les deux. Basique !

Quest Notes Migrator for SharePoint Designer
Le Designer permet de sélectionner la source Notes, la cible SharePoint et le mapping entre les colonnes.

Le moteur de requetage peut extraire les champs des documents, les métadonnées gérées par Notes, les colonnes des vues, les pièces jointes, images embarquées, le texte riche, les DocLinks, etc... et les re-injecter dans SharePoint plus ou moins fidèlement en fonction du type de cible choisi parmi les possibilités suivantes:
  • Liste Standard (Discussion, etc.)
  • Liste Custom
  • Types de contenus
  • En tant que pièces jointes
  • Fichier HTML
  • Fichier MIME
  • Basic pages
  • Wiki pages
  • Web Part pages
  • Publishing Pages
  • InfoPath XML documents
  • Word 2007 (OpenXML) documents
  • En mixant Listes et Bibliothèques
Toute la complexité réside dans le bon choix des composants SharePoint cible et le compromis entre reproduction fidèle de l'ergonomie et du comportement fonctionnel de vos applications Notes (ce qui sous-entends des développement spécifiques) ou adaptation à la philosophie bien différente de SharePoint.

NMSP inclut aussi le support de la sécurité au niveau des documents, le versionning, la création de dossiers...

L'outil NMSP Console permet lui de faire un état des lieux des bases Notes, de gérer l'avancée du projet de migration, d'assigner dynamiquement des sites et listes/bibliothèques cibles ou d'associer dynamiquement les jobs de migration.

Quest Notes for SharePoint console
La Console permet d'associer dynamiquement les cibles SharePoint et les jobs de migration aux bases Notes.

NMSP permet de migrer aussi la sécurité sur les documents, et en ce qui concerne le mapping des utilisateurs et des groupes Notes vers AD, NMSP permet d'intégrer un fichier de mapping au format XML ou fichier plat.

NMSP intègre aussi des services qui peuvent être installés sur la ferme SharePoint:
  • NMSP Import Service est un web service WCF qui permet d'intégrer les documents exportés depuis Notes et ainsi éviter de travailler directement sur la ferme SharePoint.
  • NMSP Link Tracking Service est un service couplé a une base de données SQL Server qui permet de tracer les documents migrés et et permet aux liens inter-documents de pointer sur la bonne cible quel que soit le stade actuel de la migration (pointe sur SP si le document cible est déjà migré ou sinon sur le client Notes).

Bien sur tout ceci fonctionne dans un monde idéal dans lequel très peu de customisation ont été réalisées. NMSP est un outil puissant mais n'est pas magique, il peut très bien migrer les données "brutes", et ceci de manière assez automatisée si toutes vos bases de documents respectent un nombre restreint de modèles. Mais en ce qui concerne "l'intelligence" que vos bases de documents embarquent (Boutons, form events, agents ...) il faudra bien évidement retranscrire (recoder) ceci dans SharePoint en tant que workflows, event handlers, etc...

Il est donc nécessaire de fonctionner de façon itérative: créer des versions basiques des jobs, effectuer des premiers tests de migration, identifier les customisation SharePoint nécessaire, les intégrer dans les jobs pour la création des éléments et rejouer la migration encore et encore...

Voila maintenant que vous avez mis un pied dans le monde de la migration Notes/SharePoint, il ne vous manque plus que cette petite adresse qui recense un bon nombre de petits trucs pour utiliser NMSP à 100%, écrit par le Product Manager lui même: http://notes2sharepoint.org

A noter que NMSP supporte la migration vers SharePoint 2010.


N'hésitez pas à poser vos questions/commentaires.

Seb
[SharePoint 2010] Bug sur la configuration d'une connexion pour le User Profile Synchronization Service
Même depuis la sortie de la version finale de SharePoint Server 2010, le chemin vers la configuration de la synchronisation des profils utilisateurs est un parcours semé d'embuches.

Cet article TechNet permet de partir sur de bonnes bases: http://technet.microsoft.com/fr-fr/library/ee721049.aspx

Néanmoins, je suis tombé sur un petit bug sympa, visiblement connu de Microsoft, mais qui n'a pas encore de correctif a ma connaissance:

Une fois que l'on a saisi les paramètres de connexion au contrôleur de domaine, et que l'on souhaite sélectionner les OU a synchroniser, si une OU fille à le malheur d'avoir le même nom que son OU mère, il est impossible de la sélectionner, ni même de l'étendre en cliquant sur le petit +. Il semblerait que ce soit un bug de l'interface.


Bug dans la page de création d'une connexion

Dans ce cas, il y a deux solutions:
  • Renommer l'OU ou même juste changer sa casse, cette dernière option semble suffire.
  • Utiliser le Synchronisation Service Manager (miisclient.exe) qui se trouve dans C:\Program Files\Microsoft Office Servers\14.0\Synchronization Service\UIShell pour corriger la connexion, mais je conseillerai plutôt d'utiliser cet outil à des fins de localisation de problème et pas comme solution pérenne.
Espérons que ce problème soit corrigé prochainement...


[EDIT]
Voici la KB expliquant la solution pour ce problème: http://support.microsoft.com/kb/2300488
[SharePoint 2007] Event Receiver, BeforeProperties, AfterProperties et les autres...
Voici un petit post illustré par un cas spécifique concernant le SPItemEventReceiver ...

Voici le besoin:
Sur un élément de liste, je souhaite forcer la valeur d'un champ si et seulement si la valeur d'un autre champ de l'élément a été changé.
Ex: si "champ1" change de valeur > un champ "statut" prends comme valeur "In Progress"

Il y a donc deux points clés pour réaliser cette fonctionnalité:
  • Tester si "champ1" a changé de valeur (pas seulement le déclenchement d'un update mais bien la valeur qui change)
  • Modifier la valeur de "statut"
J'ai donc pensé a créer un Event Receiver du type SPItemEventReceiver.

Petit rappel:
Pour tous les types d'event receiver, il y a deux types d'évènements, les évènements synchrones (qui se déclenche avant l'action) et les évènements asynchrones (qui se déclenchent après l'action). Les méthodes correspondantes auront respectivement les suffixes "ing" (ItemAdding par ex) et "ed" (ItemAdded par ex).

Test du changement de valeur
Pour réaliser la première partie de cette fonctionnalité, j'ai d'abord penser à utiliser l'évènement ItemUpdated et de comparer le contenu de BeforeProperties et AfterProperties.
Comme ceci par exemple:

if (properties.BeforeProperties["Champ1"] != properties.AfterProperties["Champ1"])

Seulement voila, pour un élément de liste, la collection BeforeProperties n'est pas renseignée, que ce soit en synchrone ou asynchrone. Avec une bibliothèque de document, cela n'aurait pas été le cas.

Voici donc deux tableaux (Liste vs DocLib) résumant le contenu des différentes collections en fonction des évènements:
List BeforeProperties AfterProperties properties.ListItem
ItemAdding Vide Nouvelles valeurs
Null
ItemAdded Vide Nouvelles valeurs Nouvelles valeurs
ItemUpdating Vide Valeurs modifiées
Valeurs d'origine
ItemUpdated Vide Valeurs modifiées
Valeurs modifiées
ItemDeleting Vide Vide Valeurs d'origine
ItemDeleted Vide Vide Null

Library BeforeProperties AfterProperties properties.ListItem
ItemAdding Vide Vide Null
ItemAdded Vide Vide Nouvelles valeurs
ItemUpdating Valeurs d'origine Valeurs modifiées Valeurs d'origine
ItemUpdated Valeurs d'origine Valeurs modifiées Valeurs modifiées
ItemDeleting Vide Vide Valeurs d'origine
ItemDeleted Vide Vide Null

La solution a donc été d'utiliser ListItem au sein méthode ItemUpdating de cette manière:

if(properties.ListItem["Champ1"] != properties.AfterProperties["Champ1"])

Modification de valeur d'un champ
Ensuite pour modifier le champ Statut, j'ai d'abord pensé utiliser l'objet ListItem et la méthode SystemUpdate, mais contrairement à la ItemUpdated, cela ne fonctionne pas au sein de ItemUpdating...

La méthode qui fonctionne est en fait bien plus simple. Elle consiste a directement modifier le champ dans la collection AfterProperties  sans même lancer d'Update sur l'item !

Voici donc le code dans son intégralité:

public override void ItemUpdating(SPItemEventProperties properties)
{
  try
  {
    if(properties.ListItem["Champ1"] != properties.AfterProperties["Champ1"])
    {
      properties.AfterProperties["Statut"] = "In progress";
    }
  }
  catch (Exception ex)
  {
    //log exception
  } 
}


A noter, que je n'utilises pas les méthodes DisableEventFiring et EnableEventFiring puisqu'il n'y a pas d'update...
[SharePoint 2007] Déploiement de paramétrage(s) dans le(s) web.config
Il est question dans ce billet de discuter des méthodes de déploiement de paramétrages dans le fichier web.config d'une application web SharePoint, aussi bien à la création d'une nouvelle application, que dans le cas d'une modification incrémentale sur des applications existantes.

La méthode la plus "naturelle" consiste à intégrer ces paramétrages dans votre solution SharePoint (WSP). Pour ceux qui ne connaissent pas le principe, voici comment cela fonctionne:

Lors de la création d'une application web, WSS copie le web.config depuis C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\CONFIG vers le dossier racine de l'application web. Avant de copier ce fichier, WSS vérifie l'existence de fichiers du type webconfig.*.xml dans le dossier CONFIG et fusionne le contenu de ces fichiers avec le web.config.

Exemple de fichier webconfig.*.xml:
<actions>
   <add path="configuration/SharePoint/SafeControls">
      <SafeControl
         Assembly="System.Web, Version=1.0.5000.0, Culture=neutral,
            PublicKeyToken=b03f5f7f11d50a3a"
         Namespace="System.Web.UI.WebControls"
         TypeName="*"
         Safe="True"/>
   </add>
   <remove path="configuration/SharePoint/RuntimeFilter"/>
   <add path="configuration/SharePoint">
      <RuntimeFilter
         Assembly="Company.Product, Version=1.0.1000.0,
            Culture=neutral, PublickKeyToken=1111111111"
         Class="MyRuntTimeFilter",
         BuilderUrl="MyBuilderUrl"/>
   </add>
</actions>


Vous pouvez ensuite packager ce fichier webconfig.*.xml dans votre wsp et modifier le manifest.xml en conséquence:
<Solution xmlns=”http://schemas.microsoft.com/sharepoint/” SolutionId=”GUID”>
<RootFiles>
   <RootFile Location=”CONFIG
\webconfig.myname.xml”/>
</RootFiles>
...
</Solution>


Mais malheureusement, ce système n'est valable que pour la création d'une nouvelle application web...

Imaginons maintenant, que vous ayez une solution WSP déjà déployée sur plusieurs applications web de votre ferme et que vous ayez besoin de modifier "en masse" des fichiers web.config existants.
Par exemple, une nouvelle webpart à été ajouté à votre solution et vous souhaitez la mettre à disposition sur les sites existants. Vous avez donc peut être besoin de rajouter un tag SafeControl sur plusieurs web.config d'applications existantes.

Quelles sont les solutions qui s'offrent à nous ?
  • à la main: Nan je déconnes...

  • La commande stsadm.exe -o copyappbincontent permet de copier les fichiers spécifiques aux application du dossier 12\CONFIG vers les dossiers appropriés des applications. Mais attention: toutes les applications (sauf l'administration centrale) sont impactées et il est nécessaire d'effectuer cette opération sur CHACUN des frontaux. De plus, si vous aviez déjà déployé des paramètres avec un fichier webconfig.*.xml, vous risquez de vous retrouver avec des déclarations en double !

  • La classe SPWebConfigModification de Microsoft.SharePoint.Administration.dll permet d'écrire des nœuds et des attributs dans le web.config. Vous pouvez donc utiliser cette classe dans un FeatureReceiver pour ajouter le noeud à l'activation et le supprimer à la désactivation.
    Exemple d'utilisation de SPWebConfigModification :
    SPWebService service = SPWebService.ContentService;
    SPWebConfigModification myModification = new SPWebConfigModification();
    myModification.Path = “configuration/SharePoint/SafeControls”;
    myModification.Name = “SafeControl[@Assembly='MyCustomAssembly'][@Namespace='MyCustomNamespace'][@TypeName='*'][@Safe='True']“;
    myModification.Sequence = 0;
    myModification.Owner = “User Name“;
    myModification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
    myModification.Value = “<SafeControl Assembly=’MyCustomAssembly’ Namespace=’MyCustomNamespace’ TypeName=’*’ Safe=’True’ />”;
    service.WebConfigModifications.Add(myModification);
    /*Call Update and ApplyWebConfigModifications to save changes*/
    service.Update();
    service.ApplyWebConfigModifications();


  • Utiliser le web.config Modification Manager téléchargeable à cette adresse:
    http://blog.thekid.me.uk/archive/2007/03/24/web-config-modification-manager-for-sharepoint.aspx

En conclusion, la mise à jour des fichiers de config est une opération délicate qui doit être automatisé mais avec beaucoup de prudence. La solution du SPWebConfigModification me semble la plus sûre, à condition de bien tester vos modifications, mais doit être utilisée en complément des fichiers webconfig.*.xml qui serviront toujours en cas de remontée après un crash par exemple.

La discussion est ouverte !
[SharePoint 2007] Exemption d'expiration

Lorsque l'expiration est activée sur une liste ou une bibliothèque, il est possible d'exclure (exempter) un élément de la stratégie d'expiration de façon manuelle:








En activant l'expiration sur une liste SharePoint contenant déjà des éléments, je me suis rendu compte que le workflow d'expiration (ou la suppression) ne se déclenchait pas sur ces éléments existants.
En fait, il semble que, par défaut, les éléments créés avant l'activation de l'expiration soient marqués comme exempts de l'expiration.

Ma liste contenant des centaines d'éléments, j'ai donc eu besoin de créer un petit batch pour supprimer l'exemption de façon automatique.
Voici la méthode qui permet de supprimer l'exemption sur un élément:

Microsoft.Office.RecordsManagement.InformationPolicy.Policy.RemoveExemption(SPListItem item)

Toutefois, attention si la date d'expiration est dépassée, votre workflow d'expiration (ou la suppression) va être déclenché !

[SharePoint 2007] Problème de mise à jour de formulaire de workflow dans SharePoint Designer

Dans mon quotidien SharePointesque, je suis loin d'être un fervent adepte de SharePoint Designer. Il peut être pratique pour tester des modifications rapidement sur une page, en environnement de développement uniquement, mais il ne permet pas de créer/customiser dans SharePoint de façon pérenne. De plus, je ne le trouves pas très user-friendly.

Voici un exemple de ce qui me fait dire cela:

Dernièrement, j'ai été forcé d'utiliser SharePoint Designer pour apporter des modifications sur un workflow réalisé par un de mes prédécesseurs. Ce workflow utilise l'action "Collect Data Task" qui permet de d'assigner une tâche à un utilisateur, d'y associer des meta-données et créé automatiquement le formulaire pour récupérer des informations saisies par l'utilisateur.

Bizarrement, lorsque je tentais de modifier les propriétés de ce formulaire, mes modifications n'étaient pas prises en compte. En prenant un peu de recul j'ai en effet constaté que le fichier aspx de ce formulaire n'était pas mis à jour...
C'est seulement lorsque j'ai supprimé ce fichier, et sauvegardé à nouveau le workflow que le fichier aspx à été recréer avec mes modifications... Un problème tout bête mais qui m'a bien fait perdre du temps...

[SharePoint 2007] Compatibilité Office 2003/2007
J'écris ce petit post juste pour mettre de coté ce lien utile qui détaille notamment ce qu'il est possible ou non de faire sur SharePoint 2007 avec un client Office 2003:
http://www.sharepointusecases.com/index.php/2008/08/office-2003-and-sharepoint-2007-comparision/
[SharePoint 2007] Un workflow SharePoint Designer ne démarre pas automatiquement
Voici une des petites surprises dont SharePoint a le secret que je viens de rencontrer:

Je souhaitais modifier un workflow créé avec SharePoint Designer pour qu'il se déclenche automatiquement lors de la création d'un élément dans ma liste. Bizarrement, même après avoir effectué cette modification, le workflow pouvait toujours être démarré manuellement mais ne se déclenchait pas automatiquement.

Il s'agit en fait d'un correctif apporté par le SP1 qui empêche les workflows déclaratifs de se déclencher automatiquement avec le compte systeme.

Après installation du SP1, les workflows déclaratifs ne se déclencheront pas automatiquement si les conditions suivantes sont réunies:

  • L'application Web WSS s'exécute en utilisant un compte utilisateur de domaine
  • L'utilisateur courant est connecté avec ce compte
  • Le site affiche "Compte Système" comme nom d'utilisateur
Voici le lien vers la solution complète: http://support.microsoft.com/kb/947284

[SharePoint 2007] Les blocs de code ne sont pas autorisés dans ce fichier

Si vous désirez ajouter du code C# ou VB.Net dans une page maître ou un modèle de page, vous risquez de tomber sur l'erreur suivante:

Les blocs de code ne sont pas autorisés dans ce fichier.

Afin d'autoriser les blocs de codes dans vos pages, il est nécessaire de modifier le web.config de votre application Web en ajoutant le nœud suivant:

<PageParserPaths>
<PageParserPath VirtualPath="/_catalogs/masterpage/*" CompilationMode="Always" AllowServerSideScript="True" IncludeSubfolders="True" />
</PageParserPaths>

L'attribut VirtualPath contient le chemin vers les pages que vous voulez autoriser, dans cet exemple toutes les pages de la galerie des pages maîtres et modèles de pages et dans les sous-dossier sont autorisées à exécuter du script coté serveur.

[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
[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.
[SharePoint 2007] Récuperer un SPUser à partir de la valeur d'une 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).

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

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;


Ensuite, avec mon SPUser, je peux récupérer toutes les infos dont j'ai besoin, notamment le LoginName.
[SharePoint 2007] Requêtes XPath sur les noeuds XML retournés par les WebServices
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
[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/
[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

[SharePoint 2007] Utiliser l'API pour lister les Web App et les collections de sites d'une ferme
Voici un petit bout de code permettant de lister les Web App d'une ferme, puis pour chaque WebApp, les collections de sites. N'oubliez pas de référencer le namespace Microsoft.SharePoint.Administration.


SPFarm farm = SPFarm.Local;
SPWebService service = farm.Services.GetValue<SPWebService>("");

foreach (SPWebApplication webApp in service.WebApplications)
{
foreach (SPSite site in webApp.Sites)
{
/***
ici traitement pour
chaque collection de site
**/
}
}


Les 10 derniers blogs postés

- Simuler facilement l’envoi de mail par Blog de Jérémy Jeanson le 05-22-2013, 12:52

- ProcDump 6.0 : support du filtrage sur messages d'exceptions .NET, des filtres multiples et du ciblage par nom de service par CoqBlog le 05-20-2013, 14:50

- Votez pour le TOP 10 des influenceurs SharePoint francophones ! par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 12:59

- [Conf’SharePoint] Dernier rappel ! :-) par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 09:09

- [ #SharePoint 2013 ] les modèles de sites standards… par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 09:03

- 10 erreurs de compréhension concernant SharePoint… par Le blog de Patrick [MVP SharePoint] le 05-20-2013, 08:27

- Conf’SharePoint : 10 bonnes raisons pour ne pas la rater par Le petit blog de Pierre / Pierre's little blog le 05-14-2013, 02:24

- [Event] Soirée de lancement Agile .NET France à Lyon par Blog Agile/ALM de Vincent THAVONEKHAM le 05-13-2013, 01:29

- .NET / Debug : inspection de la mémoire d'applications .NET (dump ou processus live) : première livraison d'une librairie .NET par Microsoft par CoqBlog le 05-11-2013, 22:21

- SharePoint : Incompatibilité avec Internet Explorer 10 (IE10) par Blog Technique de Romelard Fabrice le 05-08-2013, 16:29