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.


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

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 :


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 :

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

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) :

et ensuite appeler sa méthode.

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 :

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

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 :

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 :

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

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 :

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