SharePoint 2007 : Trouver les fichiers CheckOut dans une librairie de document

Suite à une discussion sur les forums SharePoint MSDN France (http://forums.microsoft.com/MSDN-FR/ShowPost.aspx?PostID=3521908&SiteID=12), je me suis dis qu'il serait intéressant de parler de certaines "particularités" avec les fichiers CheckOut dans SharePoint.

Prenons par exemple une bibliothèque de documents classique :

CropperCapture[3]

On remarque 5 fichiers :

  • 1 fichier crée par l'user Administrator, Sauvegardé, Checkin en Brouillon (0.1)
  • 1 fichier crée par l'user Administrator, Sauvegardé, Checkin et Publié (1.0)
  • 1 fichier crée par l'user Administrator, Sauvegardé, Jamais Checkin
  • 1 fichier crée par l'user Philippe.Sentenac, Sauvegardé, Checkin en Brouillon (0.1)
  • 1 fichier crée par l'user Philippe.Sentenac, Sauvegardé, Checkin et Publié (1.0)

Cependant, si on se logue sur un autre utilisateur (Philippe.Sentenac) qui les droits de Contribution sur ce site...

CropperCapture[1]

On remarque toujours 5 fichiers MAIS pas tout à fait les mêmes :

  • 1 fichier crée par l'user Administrator, Sauvegardé, Checkin en Brouillon (0.1)
  • 1 fichier crée par l'user Administrator, Sauvegardé, Checkin et Publié (1.0)
  • 1 fichier crée par l'user Philippe.Sentenac, Sauvegardé, Checkin en Brouillon (0.1)
  • 1 fichier crée par l'user Philippe.Sentenac, Sauvegardé, Jamais Checkin
  • 1 fichier crée par l'user Philippe.Sentenac, Sauvegardé, Checkin et Publié (1.0)

En réalité, il y a bien 6 fichiers dans cette bibliothèque !

Mais les fichiers créés et jamais checkin ne sont visibles que pour la personne qui les a crée même si vous êtes Administrateur du site/collection de site/etc...

Quand je parle de fichier crée et jamais Checkin, je parle des fichier créés via cette manipulation :

  • Dans la bibliothèque de document, je clique sur le menu New
  • Mon document Word s'ouvre, je le modifie, le sauvegarde dans la librairie SharePoint
  • Je quitte Word en précisant que je ne souhaite pas checkin le fichier.

Facilement, on comprend que cette particularité peut poser quelque problèmes lorsqu'on veut accèder à ces fichiers voire même les supprimer.

Voyons un peu comment ca se passe au niveau code et ce que l'on peut afficher :

CropperCapture[2]

Commençons par l'affichage de ShowItemsCountDIff :

/// <summary>
/// Shows the difference using docList.ItemCount and docList.Items.Count
/// </summary>
/// <param name="docList">The doc list.</param>
private static void ShowItemsCountDiff(SPList docList)
{
    Console.WriteLine("ShowItemsCountDiff, User : " + docList.ParentWeb.CurrentUser.LoginName);

    Console.WriteLine("docList.ItemCount : " + docList.ItemCount);
    Console.WriteLine("docList.Items.Count :" + docList.Items.Count);
}

image

Ce qui est intéressant ici, c'est de remarquer la différence entre la valeur docList.ItemCount et docList.items.Count.

Dans l'univers SharePoint, vous retrouverez souvent des propriétés comme docList.ItemCount, elles existent afin d'éviter de devoir manipuler la collection d'Items et ainsi d'être plus performant. Enfin, en théorie, car ici, la valeur est différente...

Si on regarde dans reflector, on s'aperçoit que docList.ItemCount fait appel à une variable en interne qui sera toujours à jour grâce à EnsurePropsFresh et qui ne semble pas prendre en compte les problématiques de permission.

public int get_ItemCount()
{
    this.EnsurePropsFresh();
    return (int) this.m_arrListProps[20, this.m_iRow];
}

Ainsi, en comparant les différentes valeurs de ces deux propriétés, on se peut se rendre compte qu'on fait face à la fameuse problématique du fichier invisible "jamais checkin".

Mais on peut aller plus loin, en prenant un raisonnement basique :

/// <summary>
/// Shows the check out files and their check out status.
/// Iterate on each item and item.File in the docLib
/// </summary>
/// <param name="docLib">The doc lib.</param>
private static void ShowFilesAndCheckOutStatus(SPDocumentLibrary docLib)
{
    Console.WriteLine("ShowFilesAndCheckOutStatus, User : " + docLib.ParentWeb.CurrentUser.LoginName);
    foreach (SPListItem item in docLib.Items)
    {
        Console.WriteLine("\t item.File.Url : " + item.File.Url);
        Console.WriteLine("\t item.File.CheckOutStatus : " + item.File.CheckOutStatus);

        if (item.File.CheckOutStatus != SPFile.SPCheckOutStatus.None)
            Console.WriteLine("\t\t item.File.CheckedOutBy.Name : " + item.File.CheckedOutBy.Name);
    }
}

image

En manipulant (itérant) sur chacun des fichiers auquel nous avons accès dans la bibliothèque, on a accès à une propriété interessante item.File.CheckOutStatus. Si ce status est différent de SPCheckOutStatus.None alors le fichier est CheckOut. On peut alors déterminer par qui.

Le problème, comme on le voit ci-dessus, est qu'on ne récupère qu'un seul fichier "jamais checkin" : celui que nous avons crée avec l'administrateur et pas celui crée avec l'account Philippe.Sentenac... Ce qui ne répond pas à notre problématique du coup, bref passons à la suite...

/// <summary>
/// Shows the check out files.
/// Iterate on each SPCheckedOutFile in docLib.CheckedOutFiles
/// </summary>
/// <param name="docLib">The doc lib.</param>
private static void ShowCheckOutFiles(SPDocumentLibrary docLib)
{
    Console.WriteLine("ShowCheckOutFiles, User : " + docLib.ParentWeb.CurrentUser.LoginName);
    try
    {
        Console.WriteLine("docLib.CheckedOutFiles.Count :" + docLib.CheckedOutFiles.Count);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Tried to execute : docLib.CheckedOutFiles.Count, {0}", ex.Message);
        return;
    }

    foreach (Microsoft.SharePoint.SPCheckedOutFile checkedOutFile in docLib.CheckedOutFiles)
    {
        Console.WriteLine("\t" + checkedOutFile.Url);
        Console.WriteLine("\t\t" + checkedOutFile.CheckedOutByName);
    }
}

image

Comme vous pouvez le voir au résultat qui s'affiche sur la console, il semblerait qu'on ait atteint notre objectif avec la propriété CheckedOutFiles.

En effet, elle nous renvoie bien le nombre exact de documents "jamais checkin" et nous permet même d'y accéder directement pour savoir notamment qui en est l'auteur et voire même d'en reprendre le contrôle avec la méthode : Microsoft.SharePoint.SPCheckedOutFile.TakeOverCheckOut();

Un seul petit bémol... il faut être siteAdmin pour y avoir accès :) d'où la gestion d'exception (grossière mais fonctionnelle)

Au final, juste histoire de confirmer ce que je pensais, j'ai par la suite réalisé la requête CAML suivante :

/// <summary>
/// Shows the check out files and their check out status.
/// Iterate on each item and item.File in the docLib
/// </summary>
/// <param name="docList">The doc list.</param>
/// <param name="query">The query.</param>
private static void ShowCheckOutFiles(SPList docList, SPQuery query)
{
    Console.WriteLine("ShowCheckOutFiles with Query, User : " + docList.ParentWeb.CurrentUser.LoginName);
    
    SPListItemCollection result = docList.GetItems(query);
 
    Console.WriteLine("SPListItemCollection.Count :" + result.Count);
    
    foreach (SPListItem item in result)
    {
        Console.WriteLine(item["LinkFilename"].ToString() + " | " + item.ID);
        Console.WriteLine("\t" + item["CheckoutUser"].ToString());
        Console.WriteLine("\t" + item["CheckedOutUserId"].ToString());
    }
}
 
/// <summary>
/// Gets the check out files query.
/// </summary>
/// <returns>The SPQuery</returns>
private static SPQuery GetCheckOutFilesQuery()
{
    System.Text.StringBuilder sbQuery = new System.Text.StringBuilder();
 
    sbQuery.Append("<Where>");
    sbQuery.Append("     <IsNotNull>");
    sbQuery.Append("          <FieldRef Name=\"CheckoutUser\" />");
    sbQuery.Append("     </IsNotNull>");
    sbQuery.Append("</Where>");
 
    string sQuery = sbQuery.ToString();
 
    System.Text.StringBuilder sbViewFields = new System.Text.StringBuilder();
 
    sbViewFields.Append("<FieldRef Name=\"LinkFilename\" />");
    sbViewFields.Append("<FieldRef Name=\"CheckedOutUserId\" />");
    sbViewFields.Append("<FieldRef Name=\"IsCheckedoutToLocal\" />");
    sbViewFields.Append("<FieldRef Name=\"CheckoutUser\" />");
 
    string sViewFields = sbViewFields.ToString();
 
    SPQuery query = new SPQuery();
    query.Query = sQuery;
    query.ViewFields = sViewFields;
    return query;
}

image

On obtient bien le résultat escompté à savoir le seul fichier actuellement checkOut de la bibliothèque auquel on a accès avec ce compte.

Si maintenant, on décide de refaire le même parcours en s'authentifiant comme Philippe.Sentenac (qui a les permissions de Contribution sur le site), on obtient ceci :

image

  • ShowItemsCountDiff : Même fonctionnement
  • ShowFilesAndCheckOutStatus : Même fonctionnement, on ne voit que les fichiers auquels on a accès.
  • ShowCheckOutFiles : Nous n'avons pas le droit d'y accèder car Philippe.Sentenac n'est pas SiteAdmin
  • ShowCheckOutFiles with Query : Même comportement, on ne voit que les fichiers auxquels on a accès.

Enfin, voilà le code complet de cette petite application console, j'avoue humblement que le code est loin d'être parfait mais j'espère que cela suffira pour vous permettre d'apprécier ce post.

A noter que j'utilise la dll SharePointOfView pour les logs et les extension de method bien pratique, vous pourrez la retrouver sur www.codeplex.com/SharePointOfView

using System;
using Microsoft.SharePoint;
using SharePointOfView.Diagnostics;
using SharePointOfView.ExtensionMethods;
 
 
namespace Phil.CheckOutFiles
{
    class Program
    {
        private static SPUser _AnotherUser;
        private static SPList _DocList;
        private static string _DocListName;
        private static string _ErrorCategory;
 
        static void Main(string[] args)
        {
            using (SPSite site = new SPSite("http://localhost"))
            {
                using (SPWeb web = site.OpenWeb("Docs"))
                {
                    _DocList = null;
                    _DocListName = "Documents";
                    _ErrorCategory = "CheckoutFiles Application";
 
                    if (!web.Lists.SovTryGet(_DocListName, out _DocList))
                    {
                        ULS.WriteError(String.Format("The list {0} should exist !, exiting...", _DocListName), _ErrorCategory);
                        return;
                    }
 
                    ShowItemsCountDiff(_DocList);
 
                    if (_DocList.BaseType != SPBaseType.DocumentLibrary)
                    {
                        ULS.WriteError(String.Format("The list {0} BaseType should be DocumentLibrary, exiting...", _DocListName), _ErrorCategory);
                        return;
                    }
 
                    SPDocumentLibrary docLib = _DocList as SPDocumentLibrary;
 
                    ShowFilesAndCheckOutStatus(docLib);
 
                    ShowCheckOutFiles(docLib);
 
                    SPQuery query = GetCheckOutFilesQuery();
 
                    ShowCheckOutFiles(_DocList, query);
 
                    _AnotherUser = web.AllUsers[@"DOMAIN\Philippe.Sentenac"];
                }
            }
 
            using (SPSite site = new SPSite("http://localhost", _AnotherUser.UserToken))
            {
                using (SPWeb web = site.OpenWeb("Docs"))
                {
                    if (!web.Lists.SovTryGet(_DocListName, out _DocList))
                    {
                        ULS.WriteError(String.Format("The list {0} should exist !, exiting...", _DocListName), _ErrorCategory);
                        return;
                    }
 
                    ShowItemsCountDiff(_DocList);
 
                    if (_DocList.BaseType != SPBaseType.DocumentLibrary)
                    {
                        ULS.WriteError(String.Format("The list {0} BaseType should be DocumentLibrary, exiting...", _DocListName), _ErrorCategory);
                        return;
                    }
 
                    SPDocumentLibrary docLib = _DocList as SPDocumentLibrary;
 
                    ShowFilesAndCheckOutStatus(docLib);
 
                    ShowCheckOutFiles(docLib);
 
                    SPQuery query = GetCheckOutFilesQuery();
 
                    ShowCheckOutFiles(_DocList, query);
                }
            }
            Console.ReadLine();
        }
 
        /// <summary>
        /// Shows the difference using docList.ItemCount and docList.Items.Count
        /// </summary>
        /// <param name="docList">The doc list.</param>
        private static void ShowItemsCountDiff(SPList docList)
        {
            Console.WriteLine("ShowItemsCountDiff, User : " + docList.ParentWeb.CurrentUser.LoginName);
 
            Console.WriteLine("docList.ItemCount : " + docList.ItemCount);
            Console.WriteLine("docList.Items.Count :" + docList.Items.Count);
            Console.WriteLine();
        }
 
 
        /// <summary>
        /// Shows the check out files.
        /// Iterate on each SPCheckedOutFile in docLib.CheckedOutFiles
        /// </summary>
        /// <param name="docLib">The doc lib.</param>
        private static void ShowCheckOutFiles(SPDocumentLibrary docLib)
        {
            Console.WriteLine("ShowCheckOutFiles, User : " + docLib.ParentWeb.CurrentUser.LoginName);
            try
            {
                Console.WriteLine("docLib.CheckedOutFiles.Count :" + docLib.CheckedOutFiles.Count);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Tried to execute : docLib.CheckedOutFiles.Count, {0}", ex.Message);
                Console.WriteLine();
                return;
            }
 
            foreach (Microsoft.SharePoint.SPCheckedOutFile checkedOutFile in docLib.CheckedOutFiles)
            {
                Console.WriteLine("\t" + checkedOutFile.Url);
                Console.WriteLine("\t\t" + checkedOutFile.CheckedOutByName);
            }
            Console.WriteLine();
        }
        /// <summary>
        /// Shows the check out files and their check out status.
        /// Iterate on each item and item.File in the docLib
        /// </summary>
        /// <param name="docLib">The doc lib.</param>
        private static void ShowFilesAndCheckOutStatus(SPDocumentLibrary docLib)
        {
            Console.WriteLine("ShowFilesAndCheckOutStatus, User : " + docLib.ParentWeb.CurrentUser.LoginName);
            foreach (SPListItem item in docLib.Items)
            {
                Console.WriteLine("\t item.File.Url : " + item.File.Url);
                Console.WriteLine("\t item.File.CheckOutStatus : " + item.File.CheckOutStatus);
 
                if (item.File.CheckOutStatus != SPFile.SPCheckOutStatus.None)
                    Console.WriteLine("\t\t item.File.CheckedOutBy.Name : " + item.File.CheckedOutBy.Name);
            }
            Console.WriteLine();
        }
 
 
        /// <summary>
        /// Shows the check out files and their check out status.
        /// Iterate on each item and item.File in the docLib
        /// </summary>
        /// <param name="docList">The doc list.</param>
        /// <param name="query">The query.</param>
        private static void ShowCheckOutFiles(SPList docList, SPQuery query)
        {
            Console.WriteLine("ShowCheckOutFiles with Query, User : " + docList.ParentWeb.CurrentUser.LoginName);
            
            SPListItemCollection result = docList.GetItems(query);
 
            Console.WriteLine("SPListItemCollection.Count :" + result.Count);
            
            foreach (SPListItem item in result)
            {
                Console.WriteLine(item["LinkFilename"].ToString() + " | " + item.ID);
                Console.WriteLine("\t" + item["CheckoutUser"].ToString());
                Console.WriteLine("\t" + item["CheckedOutUserId"].ToString());
            }
            Console.WriteLine();
        }
 
        /// <summary>
        /// Gets the check out files query.
        /// </summary>
        /// <returns>The SPQuery</returns>
        private static SPQuery GetCheckOutFilesQuery()
        {
            System.Text.StringBuilder sbQuery = new System.Text.StringBuilder();
 
            sbQuery.Append("<Where>");
            sbQuery.Append("     <IsNotNull>");
            sbQuery.Append("          <FieldRef Name=\"CheckoutUser\" />");
            sbQuery.Append("     </IsNotNull>");
            sbQuery.Append("</Where>");
 
            string sQuery = sbQuery.ToString();
 
            System.Text.StringBuilder sbViewFields = new System.Text.StringBuilder();
 
            sbViewFields.Append("<FieldRef Name=\"LinkFilename\" />");
            sbViewFields.Append("<FieldRef Name=\"CheckedOutUserId\" />");
            sbViewFields.Append("<FieldRef Name=\"IsCheckedoutToLocal\" />");
            sbViewFields.Append("<FieldRef Name=\"CheckoutUser\" />");
 
            string sViewFields = sbViewFields.ToString();
 
            SPQuery query = new SPQuery();
            query.Query = sQuery;
            query.ViewFields = sViewFields;
            return query;
        }
    }
}

<Philippe/>

Publié mardi 8 juillet 2008 08:30 par phil
Classé sous , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

About phil

Philippe Sentenac est Consultant SharePoint à Wygwam en région Parisienne. Il intervient essentiellement sur des missions liées à SharePoint (2007 et 2010 ) mais aussi autour du Web 2.0. Plus généralement, il s'intéresse à l'ASP.Net (MVC) , à Silverlight, et à tout ce qui est orienté Web en rapport avec les nouvelles technologies, qu'il pratique depuis 2006. Féru de développement, il est passionné par les problématiques de méthodologies et d'industrialisation du développement.

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01