[SharePoint] Changer un dossier en Document Set

Les dossiers et les Document Sets sont deux éléments importants pour le classement des documents dans SharePoint. Si les dossiers existent depuis les premières versions, les Document Sets sont arrivés avec SharePoint 2010. Pour rappel, il s’agit de conteneurs spéciaux qui permettent, notamment, d’assigner à leur contenu les mêmes métadonnées que les leurs ou de capturer l’ensemble du contenu en version unique.

Au niveau implémentation, un Document Set est un type de contenu particulier qui hérite du type Dossier et qui fournit son ses fonctionnalités additionnelles sous forme de Custom Actions dans le ruban ou encore dans le menu contextuel.

On serait tenté de se dire que, puisque l’un hérite de l’autre, transformer un dossier simple en Document Set ne serait pas bien compliqué et qu’il faudrait juste changer son type de contenu. Voyons ce qu’il en est dans la pratique.

 

Depuis l’édition des propriétés, on change simplement le type de contenu à Document Set :

image

 

image

Après avoir validé, on note l’apparition d’une nouvelle fonction dans le menu contextuel prouvant qu’il s’agit bien maintenant d’un Document Set :

image

Néanmoins, l’icône n’a pas changé et on n’est pas redirigé sur la page d’accueil typique à un Document Set quand on navigue dans l’élément. C’est parce que le ProgId de l’élément n’a pas été défini lors du changement alors qu’il est bien positionné quand on créé un Document Set normalement.

Par PowerShell (ou C#, peu importe) on ne peut définir directement le ProgId car la propriété du SPFolder est en lecture seule.

image

Il faut passer par le property bag de l’élément et lui définir la propriété vti_progid avec la valeur “Sharepoint.DocumentSet” :

image

L’icône est alors changé et on dispose de la page d’accueil d’un document set.

image

image

La page d’accueil nous indique, par contre, que des types de contenu sont disponibles pour ce Document Set et qu’il faut le mettre à jour. C’est assez facile à faire par code :

image

Tout ceci fait, on a converti totalement un dossier en Document Set. Qui sait, peut-être que pour la vNext ces opérations seront natives…

SPierrick

[SharePoint] Gérer par programmation la publication de Types de contenu

La fédération de types de contenu est une fonctionnalité qui existe depuis SharePoint 2010 grâce au service de Metadonnées gérées (Taxonomy). Le concept est simple, une collection de sites “maître” contient des types de contenu dont certains peuvent être publiés vers les autres collections de sites des applications Web abonnées audit service. La publication vers les sites consommateurs est quant à elle réalisée par un timer job, une instance par application Web.

L’implémentation de ce mécanisme est fort bien documenté sur différents blogs, sur MSDN et les différents sites de Microsoft donc je n’y reviendrai pas.

Néanmoins, que faire si vous souhaitez contrôler en masse un certain nombre de types de contenu ? Imaginez que vous en provisionnez plusieurs dizaines pour un projet et que vous souhaitez qu’ils soient tous publiés dès le début.

Bien sûr, vous pouvez aller sur chacun et cocher Publish, mais ce qui peut être fait par UI peut aussi être fait par API. En l’occurence, les exemples ci-dessous sont réalisés en PowerShell mais le portage en .NET est très simple.

 

Les API se trouvent dans l’assembly Microsoft.SharePoint.Taxonomy.dll et plus précisemment dans la classe ContentTypePublisher du namespace Microsoft.SharePoint.Taxonomy.ContentTypeSync. Depuis un Shell avec les SnapIns SharePoint, rien à faire, c’est déjà chargé.

Voici les opérations que vous pouvez effectuer avec cette classe.

 

Si vous souhaitez déterminer si la publication est activée sur un site, il faut récupérer un SPSite et le passer à la méthode IsContentTypeSharingEnabled :

$site = Get-SPSite http://sharepoint.contoso.com/sites/cthub

[Microsoft.SharePoint.Taxonomy.ContentTypeSync.ContentTypePublisher]::IsContentTypeSharingEnabled($site)

 

La méthode retournant True ou False, on peut alors au besoin activer la fonctionnalité de Content Type Hub :

Enable-SPFeature -Identity [Microsoft.SharePoint.Taxonomy.FeatureIds]::MetadataHub -Url http://sharepoint.contoso.com/sites/cthub

 

Pour pouvoir déterminer si un Type est publié, le publier ou retirer sa publication, il faut instancier la classe ContentTypePublisher là encore en lui passant le SPSite du Content Type Hub : 

$publisher = New-Object Microsoft.SharePoint.Taxonomy.ContentTypeSync.ContentTypePublisher($site)

 

$customDocCT = $site.RootWeb.ContentTypes["My Custom Document"]

$publisher.IsPublished($customDocCT)

 

$publisher.Publish($customDocCT)

$publisher.Unpublish($customDocCT)

 

Appeler Publish sur un Type déjà publié équivaut à cocher Republish dans l’interface SharePoint.

 

Le Job se chargeant des synchronisations étant exécuté toutes les heures, il faudra le relancer manuellement pour profiter des changements immédiatement :

Get-SPTimerJob | ? { $_.Name -eq "MetadataSubscriberTimerJob" } | % { $_.RunNow() }

 

Bonne publication !

[SharePoint] Ouvrir les WSP nativement dans Windows

Il peut fréquemment arriver de vouloir ouvrir un WSP SharePoint afin de contrôler les ressources qui y sont incluses, pour raisons de débug, par exemple.

Pour ce faire, les options ne manquent pas. On peut renommer le WSP en .CAB, installer 7-Zip, WSPCompare

Je propose ici une solution simple et native permettant d'utiliser l'Explorateur Windows afin de gérer les WSP comme des CAB mais sans avoir à les renommer. Il s'agit d'un simple fichier de Base de Registres qui associe l'extension WSP avec le gestionnaire de CAB dans l'Explorateur Windows :

Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.wsp]
"PerceivedType"="compressed"
@="WSPPackage"

[HKEY_CLASSES_ROOT\.wsp\OpenWithProgids]
"WSPPackage"=""

[HKEY_CLASSES_ROOT\.wsp\PersistentHandler]
@="{098f2470-bae0-11cd-b579-08002b30bfeb}"

[HKEY_CLASSES_ROOT\WSPPackage]
@="SharePoint Package File"
"FriendlyTypeName"="SharePoint Package File"
"InfoTip"=hex(2):40,00,25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,\
  6f,00,74,00,25,00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,\
  00,63,00,61,00,62,00,76,00,69,00,65,00,77,00,2e,00,64,00,6c,00,6c,00,2c,00,\
  2d,00,32,00,31,00,00,00

[HKEY_CLASSES_ROOT\WSPPackage\CLSID]
@="{0CD7A5C0-9F37-11CE-AE65-08002B2E1262}"

[HKEY_CLASSES_ROOT\WSPPackage\DefaultIcon]
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
  00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,63,00,61,00,\
  62,00,76,00,69,00,65,00,77,00,2e,00,64,00,6c,00,6c,00,2c,00,30,00,00,00

[HKEY_CLASSES_ROOT\WSPPackage\shell]

[HKEY_CLASSES_ROOT\WSPPackage\shell\find]
"LegacyDisable"=""
"SuppressionPolicy"=dword:00000080

[HKEY_CLASSES_ROOT\WSPPackage\shell\find\command]
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
  00,5c,00,45,00,78,00,70,00,6c,00,6f,00,72,00,65,00,72,00,2e,00,65,00,78,00,\
  65,00,00,00
"DelegateExecute"="{a015411a-f97d-4ef3-8425-8a38d022aebc}"

[HKEY_CLASSES_ROOT\WSPPackage\shell\Open]
"MultiSelectModel"="Document"

[HKEY_CLASSES_ROOT\WSPPackage\shell\Open\Command]
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
  00,5c,00,45,00,78,00,70,00,6c,00,6f,00,72,00,65,00,72,00,2e,00,65,00,78,00,\
  65,00,20,00,2f,00,69,00,64,00,6c,00,69,00,73,00,74,00,2c,00,25,00,49,00,2c,\
  00,25,00,4c,00,00,00
"DelegateExecute"="{11dbb47c-a525-400b-9e80-a54615a090c0}"

Enregistrez ce snippet dans un .reg, double-cliquez dessus, redémarrez la machine (ou lancez un taskkill /im explorer.exe /f suivi d'un explorer.exe) et les WSP pourront être ouvert par un double-clique.

 

J'espère que ça pourra vous servir.

SPPierrick

[Système] Connaissez-vous fsutil ?

Ce n’est pas plus une trouvaille qu’une nouveauté mais c’est un outil natif dans Windows (au moins depuis Vista, si je ne m’abuse) et ça peut être TRES pratique.

Avant que vous n’ayez tous fuis, point de crainte, les développeurs et les testeurs peuvent aussi en avoir l’utilité !

fsutil, en substance, est un couteau suisse pour gérer les arcanes des systèmes de fichiers Windows. On y retrouve plein d’options, allant de la gestion des noms de fichiers courts aux transactions ou encore aux liens symboliques.

image

Il y a aussi une option pour créer des fichiers d’une taille arbitraire. Et ça peut s’avérer assez utile quand on veut tester ses développements sur différentes tailles de fichiers (car, c’est bien connu, il y a rarement de plantages avec un helloWorld.txt de 300 octets).

Bien entendu, pour gérer des fichiers, vous pouvez aussi prendre votre meilleure générateur de Lorem Ipsum et générer 3 volumes de 1024 pages mais ça risque d’être assez long.

Ainsi, la commande fsutil file createnew prend 2 paramètres : le chemin et le nom du fichier cible et la taille à créer.

image

L’exécution de cette commande est instantanée car elle se passe dans des couches basses de l’OS.

L’intérieur du fichier n’a pas de spécificité, c’est plein de vide Sourire.

image

Et il y a mieux. Je suis un fervent défenseur de PowerShell et voici un argument de plus. Imaginez que vous souhaitez créer un fichier de 175Mb. Fort simple :

image

PowerShell sait interpréter les notations d’octets et retourner un nombre entier correspondant.

image

Pour tester un développement de traitement de fichiers et évaluer des cas aux limites, je me suis ainsi généré un jeu de fichiers de différentes tailles qui m’ont servi de cas de tests.

Bien sur, ça ne remplace pas les cas où il faut du vrai contenu (vidéo, texte, …) mais ça reste un moyen simple, natif et rapide.

SPPierrick

[SharePoint] Gestion documentaire avec les Document ID, les arcanes

En analysant une solution de GED custom, j’ai été amené à valider le fonctionnement des Document ID SharePoint pour déterminer si c’était une solution technique adaptée à notre cas. Pour rappel ou, si vous ne connaissez pas cette fonctionnalité, il s’agit d’un moyen d’identifier tous les documents d’une collection de sites grâce à un identifiant unique à la fois dans SharePoint (avec un champ caché) et dans Office (avec une insertion de QuickPart).

Cet identifiant est composé d’un préfixe personnalisable et généré aléatoirement par défaut, du numéro de liste auquel le document appartient et du numéro du document. Cette fonctionnalité a déjà été assez commentée et documentée de part et d’autres, tant sur le point de vue technique que fonctionnel mais j’aimerai revenir plus en détail sur l’implémentation technique.

La WebPart de recherche de document par Document ID

Nativement, SharePoint fourni une WebPart permettant de retourner un document associé à un ID.

image

image

Concrètement, rien de spécial si ce n’est une redirection vers la page DocIdRedir.aspx.

image

La page de redirection de Document ID

C’est surtout à cette page que vous aurez affaire ne serait-ce que nativement. Elle ne contient rien de plus que le code retournant une URL à partir d’un identifiant :

image

image

L’appel est simple, on fournit un SPSite ainsi qu’une string contenant l’ID à la méthode statique DocumentId.FindUrlsById et un tableau de string (le plus souvent à un élément) est retourné avec les urls du fichier.

Cette page retourne en réponse HTTP le fichier derrière l’URL en question mais elle ne fournit surtout les bases pour exploiter les ID de manière custom.

Mettre les mains dans le code !

Toujours avec nous ? Alors attention, il y a des dragons à partir de ce point !

Continuons à partir de ce que la page de redirection nous a appris. Les API du DocumentId se trouvent dans l’assembly “Microsoft.Office.DocumentManagement”. Pour ce post, j’utiliserai le Management Shell de SharePoint mais il vous faudra rajouter une référence à cette DLL pour développer en .NET.

La classe DocumentId est celle que vous utiliserez le plus car elle permet de gérer les providers, trouver des éléments, … Les

Si nous voulons obtenir une URL à partir d’un ID, plusieurs méthodes s’offrent à nous.

- En utilisant le provider par défaut :

Faisons comme la page DocIdRedir nous indique :

image

Il y une autre méthode assez similaire qui ne retourne qu’une seule URL mais qui attend un numéro de version :

image

Elle permet, en effet, de retourner une URL en utilisant le chemin interne absolue des versions de documents.

- Ou en utilisant un provider spécifique :

On peut utiliser la méthode GetDocumentUrlsById que tout provider doit implémenter pour retourner des URL. C’est un tout petit peu plus complexe car on doit d’abord récupérer ledit provider (OobProvider, par défaut) :

image

et ensuite appeler sa méthode.

image

Outre la ligne de code en plus, cette méthode présente un désavantage majeur à savoir qu’elle court circuite le mécanisme générique des Document ID pour appeler directement la routine du provider avec sa logique spécifique (une requête CAML, dans le cas de l’OobProvider). Or, les Document ID repose aussi sur le moteur de recherche et les deux méthodes de la classe DocumentId gèrent intelligemment quel mode de requête doit être utilisé en premier.

Encore une histoire de fournisseurs

Vous l’aurez compris, rien ne nous empêche d’implémenter nos propres règles d’identification et de recherche de documents et ce grâce à un système de providers.

Tout provider doit hériter de la classe DocumentIdProvider qui est assez bien documenté (si si) sur MSDN avec un exemple en prime :

http://msdn.microsoft.com/en-us/library/microsoft.office.documentmanagement.documentidprovider.aspx

Je ne vais pas revenir sur chaque méthode mais attardons nous sur la récupération d’URL.

GetDocumentUrlsById implémente la mécanique du provider pour trouver un document. Nous avons vu plus haut que la recherche d’URL est basée sur cette méthode mais aussi sur la recherche. C’est précisemment à ça que sert la propriété DoCustomSearchBeforeDefaultSearch. Elle indique à la classe DocumentId qui prime lors d’une requête, le moteur de recherche ou la méthode GetDocumentUrlsById.

Et les 2 méthodes de DocumentId ne se valent pas forcément :

  • FindUrlsById: Celle ci appelle d’abord GetDocumentUrlsById  puis le moteur de recherche en fonction de la valeur de DoCustomSearchBeforeDefaultSearch. Dans OobProvider, cette propriété retourne toujours false.
  • FindUrlById: Celle là effectue toujours une recherche en premier puis, si aucun résultat n’est retourné, elle appelle la méthode GetDocumentUrlsById du provider défini dans la collection de sites.

Bien entendu “mais sur quels critères est utilisée la recherche ?!” me direz-vous ! Et vous aurez raison. Dans les paramètres de collection de sites, on peut spécifier un Scope pour les Document ID :

image

Pour associer un nouveau provider ou réassocier celui par défaut, rien de plus simple :

image

Toujours dans la classe DocumentId, SetDefaultProvider associe l’OobProvider et SetProvider associe celui de votre choix.

Je ne recommande pas de les modifier manuellement mais, pour information, tous ces paramètres sont stockés dans le Property Bag du site racine de la collection de sites :

image

Pôle emploi

Pour assurer la cohérence, notamment si on souhaite renommer le préfixe des ID, 2 jobs tournent chaque jour (à 22H par défaut).

Event Receiver

L’attribution d’IDs aux documents est assez simple. Un Event Receiver est associé (à l’initialisation ou par job) à chaque bibliothèque avec 4 événements :

image

Chaque événement appelle la méthode ItemChangedInternal de la classe DocumentId. Celle ci appelle ensuite la méthode AssignDocId ci dessous :

image

On note qu’elle cherche le Provider par défaut du site puis appelle sa méthode GenerateDocumentId et mets le document à jour en assignant son ID suivi d’un SystemUpdate.

Et la question à 100.000€ : est-ce que le Document ID ne fonctionne que sur des documents ? Eh bien, à vous de juger :

image

On pourrait parler longtemps de l’implémentation de cette fonction mais ce post couvre déjà un bon périmètre. J’espère que ce “deep dive” vous aura permis de mieux comprendre comment ça fonctionne.

SPBrouillet

[IIS/SharePoint] Comment effectuer un WarmUp automatique de vos applications

Inutile de présenter ce qu’est le warmup, chaque SharePointeur en aura fait les frais après avoir lancé un IISReset ou simplement en étant le premier à appeler une page SharePoint le matin. Il y a nombre de différentes techniques qui vont de bon vieux scripts VBS à des solutions plus complexes comme des addons IIS ou des scripts PowerShell.

Mais c’est en diagnostiquant une ferme SharePoint chez un client que j’ai découvert une solution qui m’a le plus plus : lancer le WarmUp en connectant une tâche planifiée aux événements Windows.

Pour ce faire, c’est très simple ! Pour commencer, on va créer une tâche qui, ici, lancera du PowerShell (car PowerShell is good!) :

image

Ensuite, il faut créer un Trigger. Habituellement, c’est ici qu’on spécifie une planification telle que “tous les jours, à 6h du matin”. Dans notre cas, on spécifie que le Trigger sera un événement.

image

Pour trouver lequel, lançons la commande “iisreset” et voyons ce qui est tracé :

image

Le journal System a tracé deux événements de la source “IIS-IISReset” avec des ID différents. Ils font référence à l’arrêt puis au redémarrage d’IIS. C’est la dernière entrée qui nous intéresse.

Nous pouvons donc créer notre Trigger avec les paramètres suivants pour initier un warmup juste après le redémarrage d’IIS :

image

Quid des recyclage manuels ou automatiques ?

Si nous souhaitons lancer un WarmUp dès qu’un Application Pool est recyclée, nous devons modifier les propriétés IIS de l’Application Pool.

image

On a à notre disposition plusieurs évenements pour contrôler finement quels types de recyclages seront interceptés. Dans notre cas, activons “Manual Recycle”.

Ainsi, dès qu’on va demander le recyclage d’un App Pool, on aura l’évenement suivant que nous pourons rajouter comme trigger à notre tâche planifiée :

image

Et avec ça, vous prendrez peut-être un code de retour ?

Toutes les tâches planifiées Windows gêrent un code de retour (Last Run Result). Comme les scripts peuvent parfois devenir complexes, on pourra aller encore plus loin et retourner un code pour tracer si tout s’est bien passé :

[Environment]::Exit(0)

ou bien pour remonter une erreur de traitement avec un retour –1, par exemple :

image

 

Bon réveil à vos fermes !

SPierrick

[Office 365] Utiliser le modèle objet client SharePoint en PowerShell vers SharePoint Online

Cette semaine, j’ai eu à oeuvrer sur une migration de données vers SharePoint Online. Nous devions créer en masse des bibliothèques de documents, définir des champs, y télécharger des fichiers et définir des métadonnées sur ceux-ci.

Dans un environnement on premises, c’est trivial, grâce à PowerShell et/ou aux API SharePoint. Initialement, nous avons utilisé les Web Services SharePoint mais ça reposait sur une application C# console, ce qui posait quelques complications :

  • quand on a gouté aux API SharePoint, les Web Services sont assez pauvres
  • les consultants qui devront utiliser l’outil et procéder aux migrations sont spécialisés en infrastructure et une application console est pour eux une “boite noire” qui impose d’avoir les sources, Visual Studio et de recompiler au besoin quand on a des changements à apporter
  • les sources de données à migrer seront décrites dans des CSV. Pour les traiter en C#, point de gestion native ! Il faut soit coder un parser à la main soit trouver une bibliothèque existante qui s’en chargera.

Avec ces contraintes, mon choix s’est immédiatement porté sur le recodage de l’outil en PowerShell.

Outre ne plus avoir la “barrière de la langue” avec les non développeurs, le parsing de CSV en PowerShell est d’une grande simplicité :

$data = Import-Csv $csvPath -Encoding Default

Les API du Modèle Object Client SharePoint permettent de faire tout ce que nous avions à faire donc, nous les utiliserons.

Normalement, on se connecte sur SharePoint avec un object ClientContext. Dans Office 365, nous avons besoin de nous authentifier différemment. Le blog MSDN suivant non seulement nous indique la marche à suivre mais nous fournit une bibliothèque .NET pour gérer le tout :

http://blogs.msdn.com/b/sharepointdev/archive/2011/05/12/connecting-to-sharepoint-online-web-services.aspx

Les pré requis sont les suivant :

Le chargement des DLL se fait par réflexion :

$clientDlls = "Microsoft.SharePoint.Client.dll", "Microsoft.SharePoint.Client.Runtime.dll", "ClaimsAuth.dll"
$clientDlls | % { [void][Reflection.Assembly]::LoadFrom("$_") }

Une fois les API chargées, on ouvre un contexte SharePoint en appelant la méthode GetAuthenticatedContext :

$authContext = [MSDN.Samples.ClaimsAuth.ClaimClientContext]::GetAuthenticatedContext(“https://mytenant.sharepoint.com”) 

Cette méthode ouvre une fenêtre de navigation (bloquant l’exécution du script tant qu’elle n'elle n’est pas fermée) qui permet à l’utilisateur de s’authentifier. Dès que l’utilisateur s’est connecté, la fenêtre se ferme et l’exécution du script se poursuit.

A noter : si on doit exécuter le script sur différents tenants depuis une même session Windows, il faut proprement se déconnecter du précédant tenant avant de vouloir s’authentifier sur le prochain sinon, quand on appelera GetAuthenticatedContext, la popup d’authentification ne se fermera pas et le context client sera mal initialisé faisant planter la suite du script !

L’utilisation du contexte client en PowerShell fonctionne presque de la même manière qu’en .NET classique. On prépare ses objets et collections d’objets puis on exécute ses requêtes.

$web = $authContext.Web
$authContext.Load($web)
$authContext.Load($web.Webs)
$authContext.Load($web.Lists)
$authContext.ExecuteQuery()

PowerShell n’étant pas ami avec les expressions lambdas et LINQ, on ne peut notamment pas utiliser LoadQuery.

Le reste du script fonctionne comme en .NET. Vous trouverez ci-dessous quelques exemples que j’ai utilisés.

Récupérer une liste par son nom

$list = $web.Lists.GetByTitle($entry.DocLibName)
$authContext.Load($list)
$authContext.ExecuteQuery()

Créer une liste

$listInfo = New-Object Microsoft.SharePoint.Client.ListCreationInformation
$listInfo.Title = $name
$listInfo.TemplateType = 101
$list = $web.Lists.Add($listInfo)
$authContext.ExecuteQuery()

Obtenir un object User

$user = $web.EnsureUser("i:0#.f|membership|toto@mytenant.onmicrosoft.com")
$authContext.Load($user)
$authContext.ExecuteQuery()

N.B.: Pour qu’un utilisateur soit accessible dans SharePoint, il faut qu’il ait une licence SharePoint Online associée.

Télécharger un fichier vers SharePoint

$fci = New-Object Microsoft.SharePoint.Client.FileCreationInformation

$fci.Url = $documentUrl $fci.Overwrite = $true $fci.Content = [IO.File]::ReadAllBytes($localFilePath)

 

$documentFiles = $docSetFolder.Files $authContext.Load($documentFiles) $authContext.ExecuteQuery()   $newFile = $documentFiles.Add($fci) $authContext.Load($newFile) $authContext.ExecuteQuery()

J’espère que ce petit post vous sera utile !

SPierrick (PS et SP ne font qu’un !)

[PowerShell 3] Télécharger et installer la documentation en ligne

Les fans de PowerShell et d’avance de phase auront surement eu le même problème que moi : la beta de PowerShell v3 n’a pas de documentation en local. Certes, on peut appeler Get-Help (je préfère “man”) sur une commandlet pour avoir la liste de ses paramètres ainsi que leurs types mais ça manque cruellement d’exemples ainsi que de descriptions.

image

Normalement, taper “Update-Help” permet de télécharger les derniers fichiers d’aide pour PowerShell mais ils n’ont pas encore été traduits. Donc si vous êtes sur un Windows dont les paramètres régionaux ne sont pas paramétrés en Anglais US, mais que vous voulez quand même avoir de l’aide dans votre PowerShell, 2 options s’offrent à vous :

- temporairement changer les paramètres régionaux de Windows

- spécifier un paramètre de culture à la commande Update-Help.

image

Dans ce cas précis, “Update-Help –UICulture en-US” permettra de récupérer les fichiers d’aide en anglais pour chaque module chargé dans la session courante de votre Shell.

Donc, tant qu’à faire, autant tout charger d’un coup en tapant “ImportSystemModules” pour charger tous les modules et snap-ins de votre système avant de lancer Update-Help.

 

SPierrick

[SharePoint 2010] – SharePoint 2010, Windows (Server) 8 et des erreurs IIS sont dans une VM…

Je me suis récemment fait une VM de test avec Windows Server 8 Beta, Visual Studio 11, SQL Server 2012 et SharePoint 2010. Outre les difficultés d’installation de SharePoint (j’utilise essentiellement PowerShell avec le très bon http://autospinstaller.codeplex.com donc je n’ais pas été impacté), on peut être confronté au message d’erreur suivant lors du provisionning de la Central Admin ou de la création d’une Application SharePoint.

image

Qu’est-ce donc ?! Simplement les Pools IIS 8 qui sont configurés par défaut en .NET 4, ce qui ne convient pas à SharePoint. Le contournement est donc de reconfigurer les pools en .NET 2… à chaque fois ?? Voilà un processus qui semble bien pénible.

On peut rendre automatique cette configuration en définissant le Framework .NET dans les valeurs par défaut des Pools d’Application :

image

Ceci fait, SharePoint peut alors créer ses applications Web sans problèmes.

 

SPierrick

[SharePoint] Arrêter l’activation d’une feature depuis un Event Receiver

Les Features sont la pierre angulaire de pratiquement tous nos développements SharePoint pour en contrôler le déploiement et le retrait. Parfois, on veut pouvoir interdire leur activation si des contraintes ne sont pas satisfaites et on peut y arriver nativement avec les dépendances de Features.

Mais que peut-on faire si on souhaite gérer plus finement l’activation de nos Features pour interdire l’activation si certaines conditions ne sont pas respectées ?

 

Il m’est arrivé de vouloir interdire l’activation de 2 Features pour les rendre mutuellement exclusives en affichant un message d’erreur indiquant à l’utilisateur explicitement ce qui ne va pas. Un petit tour sur le net m’a permis de trouver différentes techniques pour gérer l’activation grâce, notamment, à un Feature Receiver et sa méthode FeatureActivated dans lequel on utilise un SPLongOperation qui navigue vers une page d’erreur, le cas échéant.

Ce genre de technique pose, néanmoins, 2 problèmes :

  • Dès le début de l’appel de cette méthode, la Feature est déjà considérée comme activée et donc, il faut gérer son retrait (en quelque sorte un retour arrière) si on choisit d’afficher une erreur.
  • Ca ne marche pas avec PowerShell/Visual Studio ! En effet, il n’y a pas toujours de contexte Web…

Si on retourne vers une approche plus .NET, on peut essayer de lancer une exception avec un message en paramètre :

image

La Feature n’a bel et bien pas été activée mais le message retourné n’est pas ce qu’on a demandé.

Voyons ce qui se passe si on lance une SPException à la place :

image

On obtient le bon message d’erreur qui est affiché tant dans une console que dans le navigateur !

 

On est encore loin du must qui serait avoir des activations variabilisées au runtime de nos Features mais on peut au moins les empêcher de s’activer n’importe comment avec quelques SPException bien placées.

 

Pierrick

[Office 365] Sauvegarder et restaurer un site SharePoint grâce aux Web Services

On a beau dire que le cloud est fiable et sécurisé, si je dois y confier mes données, j’aime avoir la certitude que mes données peuvent être sauvegardées et restaurées à volonté. Il en va de même avec SharePoint.

Mon client actuel souhaite migrer son SharePoint 2010 on premises vers un Office 365. Rien de spécial, jusque là, sauf que ce client est aussi un gros utilisateur de bases Access Services avec des données critiques. Les corruptions de données arrivent rarement mais elles font assez mal quand elles arrivent et j’en ais justement eu une sur une de ces bases Access Services. Sans difficulté, le SharePoint étant sauvegardé par DPM 2010, la restauration de la base récalcitrante a été immédiate. Je me suis alors mis à rechercher un moyen de sauvegarder (notamment) ces sites dans le cloud.

SharePoint Designer 2007 proposait de sauvegarder et restaurer des sites SharePoint ce qui correspond presque à ce que je souhaite à ça de près que :

  • SharePoint Designer 2010 ne le propose plus
  • Il faudrait que ce processus soit automatisable

Comprendre comment ça marche

Pour faire des tests et, ayant la flemme de provisionner une VM SharePoint 2007 sur mon portable, j’ai profité de l’opportunité pour essayer de le faire dans un VM Role Azure. Mission réussie, mon général ! Non seulement ça fonctionne à merveille mais on bénéficie de performances de folies.

J’ai alors utilisé notre bon ami Fiddler pour monitorer ce qui se passe quand on sauvegarde un site avec SharePoint Designer :

image

Un premier appel à sites.asmx et à la méthode ExportWeb crée un fichier d’export (.cmp) contenant toutes les données du site exporté et un fichier de rapport/contrôle (.snt). Ces fichiers sont stockés dans SharePoint puis, comme l’export est asynchrone, plusieurs appels vont “pinger” le fichier de rapport pour déterminer si la création est terminée et, si c’est le cas, récupérer le fichier pour le stocker localement.

Mettre les mains de le cambouie

L’API existant toujours dans SharePoint 2010 (et donc Office 365) on peut continuer l’utiliser.

Pour accéder aux Web Services comme au Client Object Model, il faut déjà passer la barrière de l’authentification. Je me baserai sur l’authentification  “headless” (silentieuse) en utilisant le code du post suivant :

http://blogs.msdn.com/b/cjohnson/archive/2011/05/14/part-2-headless-authentication-with-sharepoint-online-and-the-client-side-object-model.aspx

Une fois le sample ouvert, la première chose à faire est de rajouter une référence aux Web Service sites.asmx.

image

L’initialisation de l’authentification se fait grâce au code suivant :

string cmdSpoSite = args[0];
string cmdUserName = args[1];
string cmdPassword = args[2];

helper = new MsOnlineClaimsHelper(
            cmdUserName,
            cmdPassword, 
            cmdSpoSite);

Ici, l’adresse du site, le compte et le mot de passe sont passés en paramètres de l’application.

Ce helper nous permettra d’obtenir les cookies nécessaires à passer au proxy Web Service. J’ai modifié très légerement le sample MSDN pour retourner les cookies par une propriété. On instancie ensuite le proxy Web Service et on lui assigne les cookies d’Office 365 :

SitesProxy.Sites client = new SitesProxy.Sites();
client.CookieContainer = helper.CookieContainer;

Tous les appels de méthodes seront authentifiés avec le compte spécifié plus haut.

Maintenant, sauvegardons une base Access Services nommé “Contacts” dans la bibliothèque “Documents partagés” du site parent grâce au code suivant :

string exportJobName = "export_" + Guid.NewGuid().ToString();

int exportResult = client.ExportWeb(exportJobName,
    "https://toto.sharepoint.com/Contacts" /* Web Url */,
    "https://toto.sharepoint.com/Documents partages" /* Data Path*/,
    true /* Include Subwebs */,
    true /* Include Security */,
    true /* Overwrite */,
    0);
Console.WriteLine("Backup result: " + exportResult);

Les paramètres sont assez simples :

  • Le premier paramètre est le “nom du travail”. Il s’agit du nom de base des fichiers .cmp et .snt générés.
  • La premier URL indique ce que l’ont souhaite backuper
  • La seconde où on veut le backuper
  • Si on veut backuper les sous-sites
  • Inclure la sécurité
  • Ecraser les fichiers s’ils existent déjà.

Le paramètre de retour n’aide, par contre, pas beaucoup : il s’agit d’un magic number. En décompilant le Web Services avec Reflector ILSpy on trouvera un enum qui indique les différents statut retournés. Par exemple, si tout se passe bien, la méthode retournera 1 (Pending). 5 signifiera NoFileAccess : soit le compte n’a pas les droits, soit l’endroit spécifié n’existe pas.

 

Pour restaurer notre site, rien de plus simple :

string importJobName = "import_" + Guid.NewGuid().ToString();

int importResult = client.ImportWeb(importJobName,
    "https://toto.sharepoint.com/Restauration" /* Web Url*/,
    new[] { "https://toto.sharepoint.com/Documents partages/" + exportJobName + ".cmp" } /* Data Files */,
    "https://toto.sharepoint.com/Documents partages" /* Log Path */,
    true /* Include Security */,
    true /* Override */);
Console.WriteLine("Import result: " + importResult);

Les seules différences que l’on va noter par rapport à la sauvegarde sont :

  • On peut restaurer plusieurs fichiers dans le même site.
  • On doit spécifier un endroit où stocker les “logs” de restauration. Il s’agit simplement d’un fichier .snt qui indiquera le résultat de l’opération.
  • Le site de destination peut ne pas exister (un site sera créé au besoin) mais s’il existe, il doit être de même template que celui du backup.

Voici des exemples de logs :

  • Si on essaie de restaurer un package issu d’un autre template que le site de destination :

12,Error occurred while importing the web https://toto.sharepoint.com/Restauration.
Cannot import site. The exported site is based on the template ACCSRV#0 but the destination site is based on the template STS#1. You can import sites only into sites that are based on same template as the exported site.

  • Si on essaie de restaurer un package invalide :

12,Error occurred while importing the web https://toto.sharepoint.com/Restauration.
Failed to read package file.

image

Et pour aller plus loin ?
On peut bien imaginer une tâche dans un Worker Role Azure ou bien sur un serveur On Premise qui ira sauvegarder les données critiques directement dans SharePoint, dans des blobs Azure…

A l’heure où Microsoft parle de réversibilité des données, ce genre de techniques est un moyen de calmer les craintes lors des migrations.

 

Pierrick

[Hyper-V 3] Présentation des commandlets PowerShell

Avec la sortie prochaine de la Beta Consumer Preview de Windows 8, j’avais envie de revenir sur une des fonctionnalités que j’attends le plus et que, en bon geek que je suis, j’utilise déjà : Hyper-V 3 ainsi son module PowerShell.

Il y a déjà pléthore de posts de parts et d’autres qui référençent les différentes nouvelles fonctionnalités d’Hyper-V 3 (les réplicat, VHDX, disponibilité sur Windows 8, SAN virtuels…) mais je n’en ait pas vu beaucoup sur les avancées niveau scripting. Bien entendu, ces informations ne valent que pour la Developer Preview et sont sujêtes à changement.

On pourrait croire qu’avec WinRT et Metro, tout est axé sur le tactile et au diable les shells ! Il n’en est rien. Pour donner un ordre d’idée, Windows 8 Developer Preview contient dans les 45 modules ( (Get-Module -ListAvailable).Count ) pour pouvoir tout piloter ou presque avec une facilité déconcertante. Bien que ce soit toujours possible, utiliser WMI n’est plus une obligation pour arriver à ses fins.

Hyper-V étant un citoyen de première classe, il dispose lui aussi de son module qu’on référence comme suit depuis un PowerShell en mode Administrateur (vous n’avez pas désactivé l’UAC, n’est-ce pas ??) :

Import-Module Hyper-V

Microsoft n’a pas été avare sur les commandes, il n’y en a pas moins de 148 !

image

Avec, on peut pratiquement tout faire depuis la création d’une VM à la configuration d’un SAN virtuel.

Je n’ai pas testé l’intégralité des commandes mais la majorité que j’aie vue implémente les “paramètres communs” :

image

Ces paramètres que, dans l’idéal, toute commande PowerShell devrait implémenter permettent d’afficher ou non des messages de confirmation avec Confirm (à désactiver avec prudence !) et de contrôler un tir à blanc avec WhatIf.

Par exemple, la commande ci dessous lance un tir à blanc de création de VM :

image

Qu’est-ce que ça pourrait donner si on voulait pouvoir rendre l’intégralité d’un script inerte simplement en changeant un commutateur ? Prenons le script suivant :

ComplexHyperVSuperScript.ps1:
param ([switch]$WhatIf = $false)

Import-Module Hyper-V -ErrorAction:SilentlyContinue

New-VM -Name Toto -NoVHD -WhatIf:$WhatIf
New-VHD -Path ./test.vhd -SizeBytes 3145728 -WhatIf

D’une complexité folle, il créé une VM Toto et un VHD test.vhd. Grâce au commutateur global au script $WhatIf que l’on passe en paramètre à tous nos appels de fonctions, on peut arriver à un résultat assez prédictible et testable. Ainsi, le script avec WhatIf et sans donnera les résultats ci-dessous :

image

Le premier appel n’affiche que des messages (qu’on pourrait qualifier d’un peu trop synthétiques) indiquant ce qui sera fait tandis que le second effectue les actions et retourne des résultats détaillés.

Et ces résultats sont très utiles car il ne s’agit pas de simples lignes de textes mais d’objets. Les commandlets Hyper-V ont toutes des signatures similaires à celle-ci :

image

Elles retournent un Object .NET correspondant à ce qui a été manipulé par la commande. Dans notre script ci-dessus, il s’agissait respectivement d’un objet VM et d’un objet VHD, eux-mêmes avec leurs méthodes et leurs propriétés.

Et c’est très intéressant ! Car avec la notion de sous-shells, on a la possibilité de chaîner plusieurs appels de commandes redirigeant ainsi la sortie d’une à l’entrée de la suivante. Ainsi, la ligne ci-dessous me permettra de réduire la taille occupée sur disque de tous les VHD de toutes les VM présentes sur mon Hyper-V :

Get-VM | Get-VHD | Optimize-VHD

On peut imaginer aussi la création de machines de tests (délibérément sans VHD pour la simplicité de l’exemple) ainsi que leur suppression :

foreach ($i in 1..10) { New-VM -Name "TestVM_$i" -MemoryStartupBytes 1073741824 -NoVHD | Set-VMMemory -DynamicMemoryEnabled:$true -StartupBytes 1073741824 -MaximumBytes 2147483648 -MinimumBytes 826277888 }

Get-VM Test* | Remove-VM -Confirm:$false -Force

Les développeurs qui n’ont que 2 ou 3 VM de développement n’en auront pas forcément l’utilité mais, personnellement, il m’est arrivé de jongler entre un client où je devais émuler une infra à 3 VM (UAG, ADFS, SP) et d’autres clients avec encore d’autres configurations. La flemme aidant, j’ai fini par écrire quelques fonctions dans mon profile PowerShell afin de lancer et éteindre mes grappes de VM en un seul appel de commande.

Typiquement, une fonction qui appelle Start-VM “Toto”, “Tutu”, “Tata” en arrivant le matin et une autre qui appelle Shutdown-VM/Save-VM “Toto”, “Tutu”, “Tata” le soir.

Pour paraphraser l’Agent Smith, "on ne devrait jamais confier à un humain le travail d’un programme"; Windows (Server) 8 sera résolument dans le même esprit.

J’espère que cet avant-gout vous aura donné l’eau à la bouche !

[PowerShell] Générer des logs depuis vos scripts avec 2 lignes de code

Si vous faites beaucoup de scripting en PowerShell (et vous avez bien raison d’en faire ! Smile), vous aurez surement eu à générer des logs pour tracer l’exécution de vos scripts. Et, bien souvent, on implémente nos propres fonctions d’écriture de fichiers.
Quand on veut tracer des scripts dans des fichiers texte, on a beaucoup plus simple, toutefois. Les commandes Start/Stop-Transcript sont faites exactement pour ça.

Elles servent à loguer chaque commande tapée par l’utilisateur et tout ce qui apparait dans un flux de sortie (standard, erreur, verbose…) entre le Start et le Stop.

Ainsi, le script suivant :

Start-Transcript test.rtf
Write-Host "test host" -ForegroundColor Green
Write-Output "test output"
Write-Error "Test error"
Write-Verbose "This is verbose text"

Stop-Transcript

Générera un log ci-dessous :

image

Simple, non ?

Il y a quelques précautions à prendre, toutefois.

Premièrement, il faut penser à appeler Stop-Transcript pour fermer le fichier de log. N’oubliez pas de le faire également dans vos blocs de gestion d’erreurs. Omettre la fermeture causera une erreur si on essaye d’accéder à nouveau au fichier.

Ensuite, que préférer, Write-Host ou Write-Output ? Les 2 sont tracées par Start-Transcript et les 2 permettent d’afficher du texte. Write-Host permet en plus de contrôler l’affichage en incluant ou pas un retour chariot et en changeant les couleurs de texte et d’arrière-plan. On serait donc tenté d’utiliser Write-Host mais,dans certains cas, il peut poser problème.

Imaginons que l’on essaye d’invoquer un Write-Host depuis une application console C# avec le code suivant (tordu, j’en conviens, mais nombre de produits serveur font des invocations PowerShell) :

using System.Management.Automation;
using System.Management.Automation.Runspaces;

namespace PSAutomation
{
    class Program
    {
        static void Main(string[] args)
        {
            Runspace runspace = RunspaceFactory.CreateRunspace();

            runspace.Open();

            using (PowerShell shell = PowerShell.Create())
            {
                shell.Runspace = runspace;
                Command cmd = new Command("Write-Host");
                cmd.Parameters.Add("Object", "toto");

                shell.Commands.AddCommand(cmd);
                shell.Invoke();
            }
            runspace.Close();
        }
    }
}

Voilà ce qui va se passer :

image

Ce qui s’explique car “l’hôte” courant n’est pas un shell mais un runspace sans gestion de l’affichage. Tandis que la sortie standard utilisée par Write-Output, elle, existe toujours.

Pour tous mes scripts où j’ai besoin d’avoir des logs, je n’utilise maintenant plus que les Transcript et des Write-Output/Error (enrobés dans des fonctions qui ajoutent un TimeCode en début de ligne). Vous pourrez obtenir plus de détails avec la commande “man Start-Transcript –full”.

Bons logs !

[SharePoint 2010] N’oubliez pas la taxonomie avant les profils utilisateurs

Lors d’une intervention chez un client, une migration SharePoint 2010, j’ai eu une petite surprise lors de la mise en place du service de Profils utilisateurs. Contrairement aux habitudes, il ne s’agissait pas cette fois d’un blocage au démarrage du service, celui-ci a parfaitement fonctionné et la synchronisation également.

La surprise est venue en consultant les profils importés :

image

On obtient un message d’erreur uniquement sur certaines propriétés : “There was a problem retrieving data for this field. Updating values in this field is disabled temporarily. You can still update values in other fields.

En regardant les différentes propriétés impactées, on constate qu’elles requièrent un TermSet associé.

image

Ceci peut provenir si une application de service de métadonnées gérées n’est pas disponible ou est défectueuse. En effet, le client ne souhaitant pas utiliser la taxonomie, je n’avais pas créé le service.

La première chose à faire est donc d’activer l’instance du service :

image

Une fois ceci fait, on peut créer une Application de service de Métadonnées gérées.

N.B.: C’est important d’activer d’abord l’instance de service avant de créer une Application de service, faute de quoi le gestionnaire de termes ne fonctionnera pas (en disant qu’aucun "Endpoint” n’est disponible).

Enfin, il faut vérifier que l’Application de service est bien celle par défaut. Pour ce faire, sélectionner le proxy de l’application et cliquer sur Propriétés.

image

Les 2 premières cases doivent être cochées :

image

 

Vous pourrez directement constater que le problème est reglé dans les Profils.

 

Pierrick Catro-Brouillet

[Virtualisation] – Comment réutiliser un disque virtuel Hyper-V avec Virtual Box (Partie 3/3)

Dans les posts précédents, nous avons vu comment utiliser un VHD Hyper-V/VPC dans des solutions concurrentes et, plus spécifiquement, dans VMware Player.

Nous allons voir ici comment utiliser sans conversion le VHD de Windows Server Core 2008 R2 Trial dans Virtual Box.

Virtual Box est un produit moins mature qu’Hyper-V, VPC ou VMware mais qui commence à être assez stable tout en offrant un jeu de fonctionnalités assez large pour un produit gratuit (support des snapshots, notamment).

Initialement développé par Sun, il appartient maintenant à Oracle.

La dernière version est la 4.0.8 et est disponible ici http://www.virtualbox.org/wiki/Downloads.

Une fois installé, on créé une VM avec un wizard assez similaire à celui de VMware.

On commence par choisir un nom ainsi que l’OS de la VM. Virtual Box ne supportant pas actuellement 2008 R2, on spécifie Windows 7 x64, ce qui revient au même.

clip_image002

clip_image004

Contrairement à VMware, on peut attacher le disque virtuel immédiatement. Virtual Box est différent d’Hyper-V, VPC ou VMware dans le sens où il fédère tous les disques virtuels et ISO dans sa bibliothèque.

On doit donc d’abord cliquer sur l’icône de dossier, à droite pour faire apparaitre la bibliothèque à y référencer le disque. Ensuite, il apparaitra en “disque dur existant”.

clip_image006

La wizard se termine avec une page récapitulative des caractéristiques de bases de la VM.

clip_image008

Tout comme VMware, on pourrait la démarrer pour constater un magnifique écran bleu. Même raisons que VMware à une différence prête : le disque est monté par défaut en SATA dans Virtual Box.

Contrairement à VMware, par contre, on n’aura pas besoin de faire de la plomberie à coup de Notepad : tout se fait dans Virtual Box.

Untitled

On ouvre la partie “Stockage”.

clip_image010

Pas possible de changer le type d’attachement directement donc on retire le disque du contrôleur SATA :

clip_image012

Puis on le rattache au contrôleur IDE :

clip_image014

clip_image016

On peut alors booter correctement.

Même punition que pour VMware, on pourra obtenir d’avantage de confort en installant les “Additions invité” dans la VM. Elles seront à désinstaller si jamais on veut transférer cette VM sur un autre virtualiseur/hyperviseur par la suite.

clip_image018

Virtual Box est en train de devenir un très bon produit, très ouvert puisqu’il supporte nativement les VHD, VMDK et VDI (son propre format). Il supporte également les snapshots mais il souffre des mêmes problèmes que VMware Workstation si on souhaite déplacer le disque parent, le chemin en dur est enregistré en absolu :

Untitled

Outre ce point, il s’agit d’une alternative viable à Hyper-V si nécessaire pour des consultants voulant/devant rester sous Windows 7.

[Virtualisation] – Comment réutiliser un disque virtuel Hyper-V avec VMware (Partie 2/3)

Dans le post précédent, nous avons présenté les différents moyens de réutiliser un disque virtuel Hyper-V/VPC avec VMware et Virtual Box.

Nous allons voir ici comment utiliser un VHD avec VMWare Player.

Pou rappel, nous utilisons le VHD trial de Windows Server Core 2008 R2.

VMware est très réputé pour ses solutions de virtualisation côté client. En effet, VMware Workstation est la solution présentant le plus de fonctionnalité actuellement et est plus que largement arrivée à maturité, la dernière version étant la 7.1.4.

Il existe également une version gratuite utilisant le coeur de Workstation mais avec un jeu de fonctionnalité plus réduit, VMware Player. Si dans les versions précédentes elle ne pouvait servir qu’à exécuter des VM déjà créées, elle permet maintenant d’en créer de nouvelles.

La dernière version est téléchargeable à l’adresse suivante :

http://downloads.vmware.com/d/info/desktop_downloads/vmware_player/3_0

Une fois installée, voici comment créer une VM pour notre VHD 2008 R2 :

clip_image002

Cliquer sur “Create a New Virtual Machine” affiche un wizard donc la première étape est le choix d’un support pour installer l’OS. Vu que Windows est déjà installé sur notre VHD, nous cliquons sur “I will install the operating system later.” puis sur Next.

clip_image004

La page suivante permet de choisir l’OS invité, ici 2008 R2.

clip_image006

En suite, nous choisissons où va résider le fichier de machine virtuel. Pensez à choisir un endroit avec suffisamment de place car le fichier de mémoire virtuel (égal à la taille de la RAM de la VM à l’exécution) se trouvera à cet endroit. Pour la version Workstation, c’est également ici que seront stockés les Snapshots (indisponibles dans la version Player).

clip_image008

L’étape suivante est la création d’un disque virtuel pour la VM. Dans la version Workstation, nous avons la possibilité de ne pas en créer. C’est obligatoire pour la version Player donc on laisse les options par défaut et nous supprimerons ce VMDK plus tard.

clip_image010

Le wizard se conclue avec un résumé de la configuration choisie.

clip_image012

Nous allons maintenant cliquer sur “Edit virtual machine settings” afin de connecter le VHD.

clip_image014

Premièrement, nous détachons le VMDK de la VM et le supprimons physiquement dans le dossier de la VM puis nous choisissons d’en attacher un nouveau.

Untitled

clip_image016

clip_image018

L’astuce se trouve là : même si VMware ne le propose pas, il supporte bel et bien les VHD. On force donc l’affichage des VHD dans la boite de dialogue et on sélectionne notre disque.

clip_image020

On constate que la taille maximale est bien reconnue (127GB étant la taille par défaut allouée par Hyper-V aux disques dynamiques).

clip_image022

Les choses se compliquent au démarrage de la VM :

clip_image024

Impossible de passer outre cet écran bleu. Comme expliqué dans le post précédent, c’est parce qu’on essaie de booter une VM configurée sur un disque IDE en SCSI.

Heureusement, même si VMWare ne nous le propose pas, il supporte les disques IDE. Nous devons, toutefois, éditer le fichier de VM au Notepad. Ouvrons donc le .vmx stocké à l’endroit spécifié plus haut.

Untitled

Il suffit tout simplement de remplacer “scsi” par “ide” puis sauvegarder. Si on essaie de booter à nouveau, l’OS démarrera correctement :

clip_image028

Dernière étape, afin de bénéficier de plus de confort comme le redimensionnement d’écran ou le copier-coller entre hôte et invité, on installera les VMware Tools :

clip_image030

Si jamais on doit remettre le VHD sur un autre virtualiseur/hyperviseur, il suffira de retirer les VMware Tools avant de le transmettre.

N.B.: Les tools sont inclus dans la distribution VMware Workstation mais pas dans VMware Player. Ce dernier essaie de les télécharger à la première utilisation puis les garde localement ce qui pourra poser problème dans les environnement sans accès internet.

Petite précision pour ceux qui utiliseraient VMware Workstation avec un VHD, les snapshots fonctionnent bien, toutefois la manière dont sont gérés les disques différentiels est différente de celle d’Hyper-V. La relation parent/enfant est maintenant en stockant en dur dans les VHD le chemin absolu du parent. Donc le seul moyen que j’ai trouvé pour s’affranchir de cette limite quand on souhaite déplacer/transférer un disque est de faire un merge à partir du snapshot souhaité.

Dans le prochain post, nous verrons comment utiliser notre VHD sous Virtual Box.

[Virtualisation] – Comment réutiliser un disque virtuel Hyper-V – Intro (Partie 1/3)

Les outils de virtualisation sont de plus en plus présents dans notre quotidien, tant au niveau des datacenters de l’entreprise ou au niveaupour nos centres de données ou pour nos portables.

Différentes solutions existent pour faire tourner des machines virtuelles, hélas, chacune avec ses différences. A commencer par leurs standards respectifs et leurs formats de fichiers.

Nous avons différents cas de figure à Winwise qui nous imposent des choix sur l’outil le mieux adapté pour nous.

Pour la plupart, nous utilisons Hyper-V (côté datacenter mais aussi pour les VM des consultants SharePoint) et Virtual PC pour les autres besoins “simples” (comprendre ne nécessitant pas de VM x64, de VLAN custom…) ou pour ceux ne disposant pas de 2008 R2 sur leur machine. Donc nos formats de disques virtuels sont majoritairement des VHD (sauf rares et ponctuelles exceptions).

Un gros cluster Hyper-V à 3 noeuds héberge nos VMs de production ainsi que celles de notre centre de formation. C’est ainsi beaucoup plus facile de réutiliser les mêmes VM pour des projets ou pour des formations si on respecte le même format.

Les problèmes commencent à arriver quand nous devons échanger des VM avec des partenaires/clients ne disposant pas d’Hyper-V ou que l’on souhaite faire tourner la VM sur un Windows 7 (portable d’un consultant par exemple).

Nous devons alors nous rabattre sur une solution concurrente, telle que VMWare Player ou Virtual Box. Le premier réflexe est alors systématiquement : “bon, maintenant il va falloir convertir nos VHD”. En effet, chaque virtualiseur a son propre format de disque virtuel.

Il existe des outils, certains gratuits qui peuvent effectuer cette conversion comme :

image

Convertir un disque est non seulement affreusement long mais en plus consomme au moins 2 fois la taille du disque original.

Donc, une fois 1 ou 2h de perdue, on a un vmdk qu’on teste sur un VMware (ou Virtual Box puisqu’il le supporte), on tente de démarrer et on a … un écran bleu ! Mais ?! Mon disque n’a-t-il pas, été converti ?? Le disque oui, mais pas l’OS qui était déjà installé. Or, Hyper-V et Virtual PC ne savent booter qu’en IDE donc toutes leurs VM ont été installées avec des drivers IDE et, manque de chance, VMWare ne démarre par défaut que sur du SCSI et Virtual Box sur du SATA.

Il existe certains outils faisant plus ou moins le travail de convertir plus en profondeur (en reparamétrant l’OS) les VM comme SCVMM de Microsoft, VMware Converter ou encore les outils de Paragon.

Je ne détaillerai pas ici ces outils car, pour la plupart du temps, ils ne sont même pas nécessaires ! En effet, VMware et Virtual Box, savent booter sur IDE pour peu qu’on sache ou aller. De plus, ils supportent nativement les VHD ! Le temps de “conversion” s’en retrouve donc drastiquement réduit puisqu’il ne reste plus qu’à créer une machine virtuelle à la configuration adéquate et pointant sur le VHD.

Dans les 2 prochains posts, je vous présenterai :

En guise d’exemple, j’utiliserai le VHD d’évaluation de Windows Server Core 2008 R2 qu’on peut trouver ici : http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=16572

Il s’agit donc d’une VM x64, préparée pour Hyper-V.

J’espère que vous serez alors convaincus que l’utilisation d’un VHD ne sera plus un obstacle à la compatibilité.

[SharePoint 2010] Personnalisation de formulaires (partie 3) : InfoPath

Dans les précédents posts, nous avons vu comment modifier des formulaires avec du JavaScript, avec SharePoint Designer et avec des modèles ASP.NET. Dans ce post, je vais vous présenter les deux modes de formulaires que le logiciel InfoPath 2010 permet de créer pour SharePoint.

InfoPath est apparu en 2003 dans la suite Office. A l'époque, il ne permettait qu'une intégration très limitée avec SPS 2003. Dans sa version 2007, l'intégration était plus poussée avec, notamment, la possibilité de remplir des formulaires en mode Web, directement dans SharePoint (édition entreprise uniquement). La version 2010 propose à son tour son lot de fonctions supplémentaires et devient maintenant partie intégrante de SharePoint (elle est notamment utilisée par SharePoint Workspace pour fournir l'interface de saisie d'une liste synchronisée hors ligne).

Personnalisation de liste

Une des fonctions les plus intéressantes de cette version 2010 est la possibilité de personnaliser des formulaires de liste. Une icône est présente dans le ruban pour ouvrir directement InfoPath en modification sur le formulaire de la liste courante (on peut également le faire depuis SharePoint Designer).

Cliquer sur ce bouton ouvre InfoPath et génére automatiquement une source de données liée aux champs de la liste ainsi qu'une vue avec tous les champs en tableau.

On peut ensuite modifier le formulaire grâce au designer, rajouter des règles de validation sur des champs, du formatage, des images…

Pour appliquer ces modifications, on clique sur Quick Publish ce qui télécharge le formulaire dans SharePoint et l'associe à la liste.

Quand on édite un élément, on a maintenant notre nouvelle interface.

Les avantages de cette méthode sont le côté user-friendly et à la portée des utilisateurs à l'aise avec Office. Les règles de validation sont souples et peuvent prendre en compte des regex.

Les inconvénients viennent du fait qu'on est limité aux outils du designer InfoPath (pas de code client ni serveur), et que le modèle de données est limité aux colonnes de la liste De plus, ce mode de personnalisation repose sur la liste et pas le type de contenu.

Bibliothèque de formulaires

Les formulaires InfoPath basés sur une bibliothèque de formulaires proviennent d'InfoPath 2003.

Pour les créer, on ouvre InfoPath et on choisit le modèle SharePoint Form Library.

On note qu'InfoPath nous propose plus d'options que lors d'une personnalisation de liste.

On dispose d'un onglet « Developer » ainsi que de contrôles supplémentaires. La principale différence de ces derniers est que ce modèle de formulaire supporte une source de données hiérarchique et non tabulaire comme les listes classiques. Ainsi, on peut créer des listes à puce, des choix multiples…

Ces contrôles particuliers définissent des nœuds « Groupe » dans la source de données :

Je me sers également de cette possibilité quand je veux organiser ma source de données pour grouper des champs (imaginez un formulaire avec un groupe de champs « Général », « RH » et « DSI »).

Code serveur

Le plus gros avantage lorsque l'on travaille sur des formulaires InfoPath pour SharePoint, c'est la possibilité d'y insérer du code .NET. L'onglet Developer nous permet de rajouter du code à notre formulaire (disponible uniquement si la « programmabilité .NET » a été cochée à l'installation d'InfoPath).

Code Editor ouvre une sorte de de Visual Studio 2005 light appelé VSTA. On aura la possibilité de modifier certains aspects du formulaire lors de son chargement, d'intéragir sur certains événements, et effectuer une validation personnalisée. Ce sujet méritant plus qu'un simple paragraphe, il sera couvert en détails lors du prochain post.

Vues

Contrairement à la personnalisation de liste, ce modèle nous permet de créer autant de vues que l'on souhaite :

Ceci, combiné à un événement lors du chargement du formulaire, peut nous permettre de n'afficher à l'utilisateur que ce qu'il a le droit de voir.

Promotion des champs

Dans ce modèle, nous sommes entièrement libres dans notre source de données, on peut choisir quels champs seront « promus » comme colonnes de liste.

Dans les options du formulaire, on sélectionne les champs à promouvoir.

On donne le nom qui sera utilisé pour la colonne de liste.

Les principaux avantages de ce modèle :

  • On peut presque tout faire. Les power users peuvent utiliser le logiciel et créer leur propre formulaire, les concevoir puis les donner aux développeurs qui rajouteront la logique métier.
  • Ce modèle supportant les types de contenu, il est réutilisable facilement.
  • Le déploiement supporte les Sandbox.
  • Modèle de données extensible (XML)

Les inconvénients :

  • On est limité au framework InfoPath. La couche graphique est très peu (ou très difficilement) personnalisable.
  • Code limité au Framework .NET 2.0
  • Développement dans VSTA et non dans Visual Studio 2010
  • L'utilisation de code impose une approbation de l'administrateur de la ferme, ou un déploiement en mode sandbox si disponible.

Récapitulatif des principales différences

Formulaires basés sur des documents

Formulaires de listes

Source de données gérée dans le document (promotion facultative de champs)

Source de données gérée par SharePoint

Modèle de données hiérarchique (XML)

Modèle de données tabulaire

Possibilité de code-behind

Pas de code-behind

Problématique de génération des noms des documents

Limitations liées au schéma de données

Identifiant unique basé sur un nom de fichier (.xml)

Identifiant unique incrémental

Promotion de champ sélective

Tous les champs sont des colonnes de liste

 

Ces solutions nécessitent InfoPath 2010 (Office Pro Plus ou séparément) et une licence SharePoint Server Entreprise. Malgré son coût, elle permet de gagner beaucoup de temps lorsque l'on doit créer des formulaires complexes, réutilisables et faciles à maintenir.

J'espère que ce tour d'horizon des différentes méthodes de personnalisation des formulaires SharePoint pourra vous être utile !

[SharePoint 2010] Personnalisation de formulaires (partie 2) : modèle de formulaire

Dans le post précédent, on a vu comment modifier des formulaires en JavaScript et en XSL.

Dans ce post, nous allons voir la méthode que je préfère (hors InfoPath) quand il s'agit de créer un formulaire custom réutilisables.

La technique a été abordée dans un article MSDN de 2008 (http://msdn.microsoft.com/en-us/library/aa544142.aspx) mais reste valable pour MSS 2010.

Une très grande partie du rendu de SharePoint dépend de « modèles ». Ces modèles sont définis dans le fichier DefaultTemplates.ascx situé dans 14\TEMPLATE\CONTROLTEMPLATES.

En l'inspectant avec Visual Studio (plus pratique que Notepad grâce à la coloration syntaxique), on voit qu'il n'y a que des SharePoint :RenderingTemplate.

Pour rendre un formulaire unifié pour chaque liste, ListFormWebPart se base sur le modèle de formulaire défini dans le type de contenu qu'elle doit rendre.

Si on prend la définition du type de contenu racine, « Elément », on voit qu'il référence le modèle de formulaire « ListForm ».

Pour avoir un modèle personnalisé, une bonne pratique consiste à créer un type de contenu et référencer de la même manière un modèle de notre conception. Ci-dessous, on voit un type de contenu personnalisé, qui hérite de « Elément », référence 3 colonnes et surtout, défini un document XML indiquant à SharePoint qu'il devra utiliser un modèle nommé « DemandesListForm » pour rendre les formulaires de création, modification et affichage.

Pour créer un nouveau modèle, on doit créer un fichier ASCX dans le dossier CONTROLTEMPLATES reprenant les mêmes déclarations que DefaultTemplates, copier-coller le template « ListForm » et le renommer en DemandesListForm.

Maintenant qu'on a notre modèle, on peut à notre gré changer le DOM, utiliser les contrôles SharePoint, ASP.NET ou même nos propres contrôles.

Dans la capture ci-dessous, j'ai remis le JavaScript utilisé dans le post précédent et j'ai aussi mis le champ « Demandeur » en face du champ « Titre ».

Attardons-nous sur certains de ces contrôles :

  • ListFieldIterator est le champ qui va générer tous les contrôles HTML dans une table. Il a lui-même un template qui utilise pour chaque ligne de la table un contrôle CompositeField. Ce contrôle a également un template qui va utiliser FormField, FieldDescription et AppendOnlyHistory.

    Un des avantages de ListFieldIterator est qu'il ne va générer que les champs qui n'ont pas déjà été générés dans le formulaire. Ainsi, je place toujours ce contrôle à la fin de mes formulaires personnalisés pour permettre à mes utilisateurs de rajouter des champs manuellement une fois que la solution aura été déployée.

  • FormField est le contrôle qui va rendre le contrôle HTML lié à un champ. On passe simplement en paramètre le nom interne du champ dans l'attribut FieldName (c'est ce que fait CompositeField et ListFieldIterator quand on les laisse faire).
  • FieldDescription fonctionne exactement comme FormField et affiche le libellé du champ.
  • AppendOnlyHistory est utilisé par les champs de type Texte enrichi. Il s'agit d'une option permettant de n'enregistrer que le différentiel quand on modifie la valeur d'un champ qui aurait été paramétré pour.

Point important à noter : quand on référence manuellement des champs, on aura un plantage (Internal Server Error 500) si on se trompe de nom interne ou si le champ n'existe pas ! Il faut donc être prudent si les utilisateurs finaux ont la possibilité de modifier les listes.

Enfin, nous allons déployer tous ces éléments avec un WSP. Le package comportera un module pour provisionner le JavaScript, des définitions de Type de contenu et de colonnes et un dossier mappé pour déployer notre ASCX.

Maintenant, quand un utilisateur voudra créer, modifier ou afficher un élément utilisant notre type de contenu, il aura l'interface suivante :

Les principaux avantages de ce type de personnalisation sont qu'il s'agit uniquement d'ASP.NET, on peut donc développer comme on le souhaite, et que c'est lié aux types de contenu, ce qui permet une très grande flexibilité et réutilisabilité.

Le principal inconvénient est que c'est du développement donc, ce n'est pas à la portée d'un utilisateur final et il faut déployer un WSP en mode Farm.

Vous retrouverez les sources de cet exemple ici:

Dans le dernier post, nous verrons comment utiliser InfoPath 2010.

[SharePoint 2010] Personnalisation de formulaires (partie 1) : injection JavaScript et SharePoint Designer

Un sujet qui me tient particulièrement à cœur dans SharePoint est la personnalisation des formulaires de saisie. SharePoint propose, pour les listes et bibliothèques, des formulaires ayant toujours le même gabarit : affichage de chaque champ en ligne sur 2 colonnes (libellé à gauche et le champ en lui-même à droite).

Ce modèle donne le plus souvent entière satisfaction. Il arrive cependant que l'on souhaite avoir un « formulaire custom », le développeur a alors plusieurs options. Le premier réflexe semble être toujours de créer une WebPart avec des contrôles ASP.NET et de réimplémenter la plomberie sous-jacente (sauvegarde, sécurité, validation, navigation…). Cela entraine des coûts de développement pouvant être importants, peut être difficile à maintenir et à réutiliser mais surtout, il est dommage de ne pas utiliser les possibilités offertes par SharePoint en standard.

Je vais vous présenter les différents moyens que j'ai pu expérimenter jusqu'ici à travers cette série de posts :

Le rendu unifié des formulaires de listes provient d'une seule WebPart, la ListFormWebPart. Cette WebPart est instanciée pour chaque liste dans 3 pages et a un comportement différent pour chacune d'elles : visualisation d'élément dans DispForm.aspx (chaque champ est affiché dans des labels), création d'éléments dans NewForm.aspx (chaque champ affiche un contrôle vide) et édition d'élément dans EditForm.aspx (chaque champ est affiché avec sa donnée dans des contrôles).

Dans ce billet, nous allons voir 2 méthodes pour modifier ces formulaires : l'injection de javascript, et la personnalisation avec SharePoint Designer.

Injection de Javascript

Le comportement prédictible de ces affichages permet de les altérer avec des injections de JavaScript. C'est une des méthodes les plus anciennes (elle date de SPS 2003 même si c'était bien moins simple qu'avec MSS 2010) et les plus utilisées par les power users (on trouve plein d'exemples sur le site www.endusersharepoint.com). Des bibliothèques existent pour faciliter les manipulations, notamment http://spservices.codeplex.com (fournissant des fonctions pour implémenter des dropdown cascadées, par exemple).

Dans cet exemple, on va définir l'arrière-plan du champ titre en vert quand il a le focus et le remettre en blanc quand il le perd. Pour ce faire, on va utiliser JQuery.

La première étape consiste à choisir le formulaire à modifier. Après avoir navigué sur la liste en question, dans la section Liste du ruban, on clique sur le bouton déroulant Modifier les composants WebPart Formulaire puis sur le formulaire à modifier.

On voit qu'il s'agit d'une page de composants WebPart toute simple et qu'on a la possibilité d'en rajouter.

Pour injecter du JS, on va simplement rajouter une WebPart Editeur de contenu.

Pour altérer le DOM du formulaire généré par la ListFormWebPart, mon meilleur ami est la touche F12 (dans Internet Explorer, ou Firefox avec Firebug). En sélectionnant le contrôle du champ Titre, on note qu'il a un attribut title avec pour valeur, le nom du champ.

Pour écrire du script, il faut être en mode d'édition HTML. On doit cliquer dans la WebPart d'édition de contenu, cliquer sur HTML puis Modifier la source HTML dans le ruban.

Dans notre exemple, le code est simple. On référence JQuery, on rajoute un événement qui ne s'exécutera que quand le DOM sera entièrement chargé. Cet événement va capturer le contrôle du champ Titre (un tag input avec l'attribut Title défini à la valeur Titre) et lui ajouter les événements focusin et focusout.

Quand on retourne dans notre liste et que l'on créé un élément, on verra notre champ Titre changer de couleur de fond selon s'il a le focus ou non.

Les principaux avantages de cette méthode sont que les possibilités en JQuery sont énormes et que l'on peut déployer nos modifications sans l'aide d'un administrateur de ferme. Ces modifications peuvent d'ailleurs être packagées dans une solution sandbox.

Les principaux inconvénients, sont que ce n'est que du JS (il ne faut donc pas s'en servir pour manipuler de la donnée sensible) et que ce n'est pas efficace si on a beaucoup de formulaires car il faudra réappliquer la même chose n fois.

Utilisation de SharePoint Designer

Une autre méthode prisée des power users est d'utiliser SharePoint Designer.

Si on ouvre une des pages aspx de liste dans SharePoint Designer, on voit que la WebPart présente dans la page est la ListFormWebPart avec son rendu par défaut.

Dans l'onglet Insertion du ruban, on a l'option de Personnaliser le formulaire de liste.

Ensuite, on précise qu'on souhaite, ici, un formulaire de modification pour la liste qui va bien.

Ca aura pour effet de remplacer la ListFormWebPart par une DataFormWebPart. Il s'agit d'une WebPart destinée à saisir de la donnée dont le rendu est généré au travers d'un flux XSL.

On peut donc entièrement le personnaliser pour rajouter ou modifier des tags HTML, du JavaScript…

Les avantages sont les mêmes que pour l'injection de JavaScripts sauf qu'en plus, on modifie ici directement le rendu généré par le serveur. C'est donc plus performant et plus fiable.

Les inconvénients sont qu'il faut aussi effectuer ces modifications à chaque endroit où on veut les voir (peut vite devenir fastidieux), qu'il s'agisse de XSL (difficile à maitriser pour des power users) et surtout, c'est une modification SharePoint Designer ce qui déghostera la page (sauvegarde de tout le contenu en base de données).

Voyons maintenant comment personnaliser le modèle du formulaire de manière réutilisable avec la ListFormWebPart.

Plus de Messages Page suivante »

Les 10 derniers blogs postés

- RDV à Genève le 12 décembre pour l’évènement “SharePoint–Office 365 : des pratiques pour une meilleure productivité !” par Le blog de Patrick [MVP Office 365] le 11-19-2014, 10:40

- [IIS] Erreurs web personnalisées par Blog de Jérémy Jeanson le 11-19-2014, 00:00

- BDD/TDD + Javascript par Fathi Bellahcene le 11-16-2014, 16:57

- Sécuriser sans stocker de mots de passe par Blog de Jérémy Jeanson le 11-15-2014, 08:58

- Où télécharger la preview de Visual Studio 2015 ? par Blog de Jérémy Jeanson le 11-13-2014, 21:33

- Les cartes sont partout ! par Le blog de Patrick [MVP Office 365] le 11-13-2014, 17:26

- [ #Office365 ] Courrier basse priorité ! par Le blog de Patrick [MVP Office 365] le 11-12-2014, 08:56

- [Oracle] Fichier oranfsodm12.dll absent du package client par Blog de Jérémy Jeanson le 11-10-2014, 20:44

- [ #Office365 ] Le chapitre 1 des Groupes est écrit, et alors ? par Le blog de Patrick [MVP Office 365] le 11-10-2014, 20:23

- SharePoint 2007: Script PowerShell pour ajouter un admin à plusieurs collections de sites par Blog Technique de Romelard Fabrice le 11-07-2014, 11:24