Archives pour la catégorie Développements

Données XML stockées pour un document dans SharePoint 2013


Ci dessous la vue XML des métadonnées SharePoint d’un document Word. Ces informations ne sont pas toutes simplement visibles dans l’interface mais la plupart du temps accessibles via les API.

Il est intéressant de savoir ce que stocke réellement SharePoint plutôt que recréer des champs manuellement pour stocker les mêmes informations que celles déjà en base.

ows_metainfos corresponds aux propriétés Office du document (éditables via le DIP – document information panel), les autres sont générées par SharePoint.

Ces données correspondent au type de document « Document » standard.

<xml>
ows_ContentTypeId='0x010100E281DC6F2D53C441AEDA86736AB42A8C'
ows__ModerationStatus='2'
ows_FileLeafRef='2;#MonDoc01.docx'
ows_Modified_x0020_By='i:0#.w|spalex\administrateur'
ows_Created_x0020_By='i:0#.w|spalex\administrateur'
ows_File_x0020_Type='docx'
ows_Title='Mon Doc'
ows_ID='2'
ows_ContentType='Document'
ows_Created='2014-01-21 18:25:54'
ows_Author='1;#Administrateur'
ows_Modified='2014-01-21 18:26:35'
ows_Editor='1;#Administrateur'
ows_FileRef='2;#Documents partages/MonDoc01.docx'
ows_FileDirRef='2;#Documents partages'
ows_Last_x0020_Modified='2;#2014-01-21 18:26:35'
ows_Created_x0020_Date='2;#2014-01-21 18:25:54'
ows_File_x0020_Size='2;#313439'
ows_FSObjType='2;#0'
ows_SortBehavior='2;#0'
ows_PermMask='0x7fffffffffffffff'
ows_CheckedOutUserId='2;#'
ows_IsCheckedoutToLocal='2;#0'
ows_UniqueId='2;#{12617EB8-4A60-418C-8699-8409E3EDE263}'
ows_ProgId='2;#'
ows_ScopeId='2;#{8C37151A-FAE0-44A3-844F-D8019764A4FC}'
ows_VirusStatus='2;#313439'
ows_CheckedOutTitle='2;#'
ows__CheckinComment='2;#my major version !'
ows__EditMenuTableStart='MonDoc01.docx'
ows__EditMenuTableStart2='2'
ows__EditMenuTableEnd='2'
ows_LinkFilenameNoMenu='MonDoc01.docx'
ows_LinkFilename='MonDoc01.docx'
ows_LinkFilename2='MonDoc01.docx'
ows_DocIcon='docx'
ows_ServerUrl='/Documents partages/MonDoc01.docx'
ows_EncodedAbsUrl='http://spalexandre/Documents%20partages/MonDoc01.docx'
ows_BaseName='MonDoc01'
ows_FileSizeDisplay='313439'
ows_MetaInfo='2;#Client:SW|Mon ClientCode projet:SW|CP001_ReviewingToolsShownOnce:SW|vti_thumbnailexists:BW|falsevti_parserversion:SR|15.0.0.4505_Category:EW|vti_pluggableparserversion:SR|15.0.0.4505vti_stickycachedpluggableparserprops:VX|Subject Date\\ de\\ version CustomerName Keywords _Status Client Code\\ projet _AuthorEmailDisplayName _ReviewingToolsShownOnce vti_title _Author _Category ContentType Référence Société _AdHocReviewCycleID _NewReviewCycle _EmailSubject _AuthorEmail _Comments Titre\\ Projet Version Classification ProjectTitleTitre Projet:SW|Mon ProjetVersion:SW|0.10vti_author:SR|i:0#.w|spalex\\administrateurvti_previewexists:BW|falseKeywords:SW|_Status:EW|_AuthorEmailDisplayName:SW|Alexandre DAVIDvti_foldersubfolderitemcount:IR|0vti_modifiedby:SR|i:0#.w|spalex\\administrateurvti_title:SW|Mon Doc_Author:SW|alexandre.david@orange.comContentType:EW|Référence:SW|MonDoc01_AdHocReviewCycleID:IW|-612166534_NewReviewCycle:SW|_EmailSubject:SW|Mon DocContentTypeId:SW|0x010100E281DC6F2D53C441AEDA86736AB42A8C_Comments:SW|Subject:SW|&amp;lt;Sujet&amp;gt;Date de version:SW|January 8th, 2014CustomerName:SW|Mon Clientvti_folderitemcount:IR|0Société:SW|Ma Société_AuthorEmail:SW|alexandre.david@orange.comClassification:SW|Non classifiéProjectTitle:SW|Mon Projet'
ows__Level='2'
ows__IsCurrentVersion='1'
ows_ItemChildCount='2;#0'
ows_FolderChildCount='2;#0'
ows_SelectTitle='2'
ows_SelectFilename='2'
ows_Edit='0'
ows_owshiddenversion='4'
ows__UIVersion='2'
ows__UIVersionString='0.2'
ows_Order='200.000000000000'
ows_GUID='{EBDDBF68-3B7B-4CFD-92A7-D04A9D9A6268}'
ows_WorkflowVersion='1'
ows_ParentVersionString='2;#'
ows_ParentLeafName='2;#'
Etag="{12617EB8-4A60-418C-8699-8409E3EDE263},4"
ows_Combine='0'
ows_RepairDocument='0'
ows_ServerRedirected='0'
</xml>

Comment afficher les tâches affectées à un utilisateur SharePoint et à ses groupes


Cet article fait partie d’une série concernant la mise en oeuvre de liste externes et de workflows avec SharePoint. Les étapes suivantes seront passées en revue :

_______________________________________________________________________________________________

Il n’est pas possible, nativement, d’afficher toutes les tâches affectées directement et indirectement à un utilisateur. J’entends par « indirectement’, le fait qu’une tâche puisse être affectée à un groupe auquel l’utilisateur appartient, ce qui est plutôt une bonne pratique.

En effet nativement j’ai uniquement la possibilité de créer un affichage pour les tâches affectées à l’utilisateur courant mais pas ses groupes grâce au filtre dynamique [Utilisateur actif].

tasks assigned to user sharepoint

Si je veux créer un affichage des tâches qui me sont affectées de manière directe et indirecte, j’ai les possibilités suivantes :

  • SharePoint Designer pour créer l’affichage avec un requête personnalisée
  • Visual Studio pour créer l’affichage avec la même requête
  • WebPart dédiée développée via Visual Studio

L’ensemble de ces solutions reposent sur la même requête CAML , à savoir :

<Query>
<Where>
  <Or>
    <Membership Type='CurrentUserGroups'>
       <FieldRef Name='AssignedTo'/>
    </Membership>
    <Eq>
       <FieldRef Name='AssignedTo'></FieldRef>
       <Value Type='Integer'>
         <UserID/>
       </Value>
    </Eq>
 </Or>
</Where>
</Query>

Avec SharePoint Designer 2013

Pour filtrer une liste de tâches SharePoint 2013 afin d’afficher les tâches assignées à l’utilisateur courant ainsi qu’aux groupes dont il fait partie, il faut suivre la procédure suivante avec SharePoint Designer 2013 :

1 – Ouvrir le site cible et sa liste de tâches avec SharePoint Designer. Sélectionner l’affichage à modifier, clic droit -> modifier le fichier en mode avancé.

sharepoint designer assigned tasks

2 – Dans le code source, rechercher la « <Query> » associée à cette vue.

sharepoint designer query mytasks

3 – Remplacer cette requête CAML par celle indiquée ci dessus. L’élément clé étant le « CurrentUserGroups »

Avec une Visual Studio 2012 et une Visual WebPart

1 – Dans Visual Studio 2012, ajouter un nouvel élément de type Visual WebPart.

visual studio 2012 visual webpart

visual webpart visual studio 2012

2 – Remplacer le code behind par le code suivant :

using Microsoft.SharePoint;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web.UI.WebControls.WebParts;
using System.Linq;

namespace SharePointProject1.VisualWebPart1
{

        [ToolboxItemAttribute(false)]
        public partial class VisualWebPart1 : WebPart
        {

            /// <summary>
            /// List Name property allows Web Part user to specify which tasks list to use to retrieve data.
            /// If nothing is specified, it takes Tasks as List Name.
            /// </summary>
            [Personalizable(),
            WebBrowsable,
            DefaultValue("MyTitleList"),
            DisplayName("Nom de la liste de tâche"),
            Category("Paramètres Custom")]
            public string ListName { get; set; }

            public VisualWebPart1()
            {
            }

            protected override void OnInit(EventArgs e)
            {
                base.OnInit(e);
                InitializeControl();
            }

            protected void Page_Load(object sender, EventArgs e)
            {
                try
                {
                    using (SPSite site = new SPSite(SPContext.Current.Site.Url))
                    {
                        using (SPWeb web = site.OpenWeb())
                        {
                            if (this.ListName != null && this.ListName.Length > 0)
                            {
                                this.Title = "Mes tâches de " + this.ListName;

                                SPList list = web.Lists.TryGetList(this.ListName);

                                grvTasks.DataSource = this.GetTasksEntity(list);
                                grvTasks.DataBind();

                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw (ex);
                }
            }

            private List<TaskEntity> GetTasksEntity(SPList list)
            {
                // Variables
                List<TaskEntity> tasks = new List<TaskEntity>();
                SPQuery myQuery = new SPQuery();
                myQuery.Query = @"<Where>
                   <Or>
                         <Membership Type='CurrentUserGroups'>
                            <FieldRef Name='AssignedTo'/>
                         </Membership>
                   <Eq>
                         <FieldRef Name='AssignedTo'></FieldRef>
                         <Value Type='Integer'>
                         <UserID/>
                         </Value>
                   </Eq>
                   </Or>
             </Where>";

                // récupération des tâches de l'utilisateur courant et des groupes auxquels il appartient
                SPListItemCollection tasksUserAndGroups = list.GetItems(myQuery);

                //assignation des valeur à une entité de type TaskEntity // création de la DataSource
                if (tasksUserAndGroups != null && tasksUserAndGroups.Count > 0)
                {
                    foreach (SPListItem item in tasksUserAndGroups)
                    {
                        TaskEntity task = new TaskEntity();

                        task.Title = this.GetFieldValue(item, "Title");
                        task.NavigateUrl = string.Concat(
                            list.DefaultEditFormUrl,
                            string.Format("?ID={0}", item.ID));
                        task.Description = this.GetFieldValue(item, "Description");
                        task.AssignedTo = this.GetFieldValue(item, "AssignedTo");
                        task.Status = this.GetFieldValue(item, "Status");

                        if (!string.IsNullOrEmpty(GetFieldValue(item, "StartDate")))
                        {
                            task.StartDate = DateTime.Parse(item["StartDate"].ToString());
                        }
                        else
                        {
                            task.StartDate = DateTime.Now;
                        }

                        if (!string.IsNullOrEmpty(GetFieldValue(item, "DueDate")))
                        {
                            task.DueDate = DateTime.Parse(item["DueDate"].ToString());
                        }
                        tasks.Add(task);
                    }

                }
                return tasks;
            }

            private string GetFieldValue(SPListItem item, string fieldName)
            {
                string strFieldValue = string.Empty;

                if (item != null && item.Fields.ContainsField(fieldName) && item[fieldName] != null)
                {
                    strFieldValue = item[fieldName].ToString();
                }

                return strFieldValue;
            }
        }

        /// <summary>
        /// Task entity for the Tasks list.
        /// </summary>
        public class TaskEntity
        {
            private string assignedTo;

            public string Title { get; set; }
            public string AssignedTo
            {
                get
                {
                    if (assignedTo.Length > 0)
                    {
                        string[] user =
                            assignedTo.Split(
                            SPFieldMultiChoiceValue.Delimiter.ToCharArray());
                        return user[user.Length - 1];
                    }

                    return string.Empty;
                }
                set { assignedTo = value; }
            }
            public string Description { get; set; }
            public string Status { get; set; }
            public DateTime StartDate { get; set; }
            public DateTime DueDate { get; set; }
            public string NavigateUrl { get; set; }

        }
    }

4 – Remplacer le code de votre fichier ASCX par le code suivant :

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="VisualWebPart1.ascx.cs" Inherits="SharePointProject1.VisualWebPart1.VisualWebPart1" %>

<asp:GridView ID="grvTasks" runat="server" AutoGenerateColumns="False"
    BackColor="#DEBA84" BorderColor="#DEBA84" BorderStyle="None" BorderWidth="1px"
    CellPadding="3" CellSpacing="2" EnableModelValidation="True">
    <Columns>
        <asp:HyperLinkField DataTextField="Title" HeaderText="Titre"
            DataNavigateUrlFields="NavigateUrl" />
        <asp:BoundField DataField="AssignedTo" HeaderText="Assignée à" />
        <asp:BoundField DataField="Description" HeaderText="Description" />
        <asp:BoundField DataField="Status" HeaderText="Statut" />
        <asp:BoundField DataField="StartDate" DataFormatString="{0:MMMM d, yyyy}"
            HeaderText="Date de début" />
        <asp:BoundField DataField="DueDate" HeaderText="Date de fin"
            DataFormatString="{0:MMMM d, yyyy}" />
    </Columns>
    <FooterStyle BackColor="#F7DFB5" ForeColor="#8C4510" />
    <HeaderStyle BackColor="#A55129" Font-Bold="True" ForeColor="White" />
    <PagerStyle ForeColor="#8C4510" HorizontalAlign="Center" />
    <RowStyle BackColor="#FFF7E7" ForeColor="#8C4510" />
    <SelectedRowStyle BackColor="#738A9C" Font-Bold="True" ForeColor="White" />
</asp:GridView>

5 – déployer sur votre site et ajouter la webpart à une page. Paramétrer cette webpart afin qu’elle pointe vers une liste de tâche de votre site.

visual webpart task

paramètres visual webpart

6 – Tester l’affichage. Bon OK en terme d’IHM ça casse pas des briques 🙂

mes taches sharepoint 2013

 

 

Voilà pour cette série.

Pour récapituler vous êtes capable de manipuler des données provenant d’une source de donnée externe dans un workflow de type machine à état SharePoint 2013 et d’afficher toutes ses tâches à un utilisateur donné.

 

Sources :

Event Handler de modification des autorisations sur les tâches d’un workflow SharePoint


Cet article fait partie d’une série concernant la mise en oeuvre de liste externes et de workflows avec SharePoint. Les étapes suivantes seront passées en revue :

_______________________________________________________________________________________________

Lorsqu’une tâche est créée automatiquement par une activité de workflow, elle l’est dans la liste de tâche associée au Workflow en cours. Cette liste de tâche hérite bien souvent des autorisations de son site, qui lui-même pourrait hérité des autorisation de son site parent.

Dû à cette gestion des autorisations, il s’avère, que si je me rend directement dans la liste de tâches du workflow, je peux voir les tâches affectées aux autres acteurs du Workflow, ce qui n’est pas toujours souhaité. Ceci peut ne poser aucun problème puisqu’une des nouveautés de SharePoint 2013 permet d’agréger l’ensemble des tâches affectées à un utilisateur dans son site personnel, et ceci de manière transverse (cross collection de site) grâce à l’application de Service de « Gestion du travail » (ou « Work Management »).

L’utilisateur ne verra donc que ses propres tâches triées dynamiquement en fonction de leur sources. il pourra également gérer sa « to-do list ». Il a également la possibilité de filtrer ses tâches en fonction de leur statut et de leur date d’échéance. Plutôt sympathique.

Une très bonne explication de tout cela sur le post Work Management Application de Mickey75019.

mes tâches sharepoint 2013

Ici on ne parle que d’affichage mais pas de gestion de la sécurité liée à ces tâches. Si on veut empêcher un utilisateur de consulter/modifier toutes les tâches car il a des droits de lecture/ édition sur la liste de tâche, ou qu’il est lui même acteur du Workflow mais qu’il ne doit pas voir les tâches de tous les autres acteurs, alors il faut systématiquement réécrire les autorisations des tâches créées par le Workflow via un Event Handler (=récepteur d’événement = trigger).

Ci dessous le code C# permettant de répondre à ce besoin. J’y vais surement un peu fort sur le allowUnsafeUpdate et suis preneur de retours pour des optims si vous en avez … mais ça fonctionne 🙂

 public class TaskEventReceiver : SPItemEventReceiver
    {
        /// <summary>
        /// Un élément a été ajouté.
        /// </summary>
        public override void ItemAdded(SPItemEventProperties properties)
        {
            base.ItemAdded(properties);

            try
            {

                //changement d'autorisation sur chaque tâche afin que les utilisateurs ne voient que les tâches qui leur sont affectées

                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    using (SPSite site = new SPSite(properties.WebUrl))
                    {
                        site.AllowUnsafeUpdates = true;

                        using (SPWeb web = site.OpenWeb())
                        {
                            web.AllowUnsafeUpdates = true;

                            SPListItem myItem = web.Lists[properties.ListId].GetItemById(properties.ListItem.ID); //  GET LIST OBJECT FROM SPWEB, NOT EVENT PROPERTIES

                            myItem.BreakRoleInheritance(false);

                            string spgroup = myItem["AssignedTo"].ToString();
                            spgroup = spgroup.Split('#')[1];

                            SPMember member = web.SiteGroups[spgroup];
                            SPPrincipal principal = (SPPrincipal)member;
                            SPRoleDefinition roledefinition = web.RoleDefinitions.GetByType(SPRoleType.Contributor);
                            SPRoleAssignment myRoleAssignment = new SPRoleAssignment(principal);
                            myRoleAssignment.RoleDefinitionBindings.Add(roledefinition);
                            myItem.RoleAssignments.Add(myRoleAssignment);
                            myItem.Update();
                            web.AllowUnsafeUpdates = false;
                            site.AllowUnsafeUpdates = false;
                        }
                    }
                });
            }
            catch (Exception ex)
            {
                ULSLogging.LogError("{0} De la mise à jour des autorisations, message : " + ex.Message  + " StackStrace : " + ex.StackTrace, "Ereur :");
            }
        }
}
}

Ce code serveur, déployé sous forme de feature/WSP, est exécuté à chaque fois qu’une tâche est ajoutée dans la liste que je cible (association de l’event handler avec la liste dans la feature). Il récupère le groupe auquel est assigné la tâche pour lui donner les droits de contribution sur celle-ci. Il supprime toutes les autres autorisations grâce à cette méthode : myItem.BreakRoleInheritance(false);

Un nouvel outil disponible sur codeplex permet également de répondre à cette problématique. il s’appelle SharePoint Rules Permissions

Pas encore eu l’occasion de le tester mais ce cas d’usage est cité de la description. Je suis preneur de vos retours 🙂