Partager via


Créer un récepteur d'événements de complément dans SharePoint

Il serait préférable que vous connaissiez les compléments SharePoint hébergés par un fournisseur et que vous ayez déjà développé des compléments au-delà du niveau « Hello World ». Consultez l’article Commencer à créer des compléments hébergés par un fournisseur pour SharePoint.

Nous vous recommandons également de lire l’article Gestion des événements dans les compléments pour SharePoint.

Obtenir d’autres exemples de code

Si vous continuez à travailler sur l'exemple de cet article, vous disposerez d'un exemple de code terminé. Voici d'autres exemples. Ils ne suivent pas tous l'architecture décrite dans cet article. Il existe plusieurs méthodes valables pour concevoir un récepteur d'événements de complément ; gardez à l'esprit que les conseils de Microsoft peuvent également évoluer au fil du temps.

Ajouter un récepteur d’événements installé de complément

  1. Dans Visual Studio, ouvrez le projet du Complément SharePoint hébergé par un fournisseur. (Si vous ajoutez un gestionnaire d'événement de complément à un complément hébergé par SharePoint, les Outils de développement Office pour Visual Studio le convertissent en application hébergée par un fournisseur.)

  2. Dans l’Explorateur de solutions, sélectionnez le nœud du complément SharePoint.

  3. Dans la fenêtre Propriétés, définissez la valeur de Gérer l'installation du complément sur True.

    Événements d’application dans la fenêtre Propriétés

    Les outils de développement Office Visual Studio :

    • ajoutent un fichier nommé AppEventReceiver.svc. qui contient du code squelette C# (ou VB.NET). Il s’agit du service qui gère l’événement de complément.

    • ajoutent l’entrée suivante à la section Propriétés du fichier AppManifest.xml : <InstalledEventEndpoint>~remoteAppUrl/AppEventReceiver.svc</InstalledEventEndpoint>. Cette entrée inscrit le récepteur d’événements de complément dans SharePoint.

      Remarque

      Le jeton ~remoteAppUrl est également utilisé pour l’application web distante dans le complément SharePoint hébergé par un fournisseur. Les outils de développement Office Visual Studio partent du principe que le domaine de l’application web et du gestionnaire d’événements est le même. Si ce n’est pas le cas, vous devez remplacer manuellement le jeton ~remoteAppUrl par le domaine réel de votre service.

    • créent un projet web si le projet de complément SharePoint n’en a pas. Ils garantissent également qu’un manifeste de complément est configuré pour un complément hébergé par un fournisseur. Ils ajoutent aussi des pages, des scripts, des fichiers CSS et d’autres artefacts. Si le seul composant distant requis par votre complément est le service web de gestion des événements, vous pouvez les supprimer du projet. Vérifiez aussi que l’élément StartPage du manifeste de complément ne pointe pas vers une page supprimée.

  4. Si votre batterie de test SharePoint ne se trouve pas sur l’ordinateur qui exécute Visual Studio, configurez le projet de débogage à l’aide de Microsoft Azure Service Bus. Pour en savoir plus, consultez l’article Déboguer et dépanner un récepteur d’événement distant dans un complément pour SharePoint.

  5. S’il existe une méthode ProcessOneWayEvent dans le fichier AppEventReceiver.svc, son implémentation doit simplement se faire avec la ligne throw new NotImplementedException();, car cette méthode ne peut pas être utilisée dans un gestionnaire d’événements de complément.

    Les gestionnaires d’événements de complément doivent renvoyer un objet qui indique à SharePoint si l’événement doit être achevé ou restauré, tandis que la méthode ProcessOneWayEvent ne renvoie rien.

  6. Le fichier inclut une méthode ProcessEvent qui ressemble à ce qui suit. (Il peut également y avoir un bloc de code qui illustre comment obtenir un contexte client. Supprimez-le ou commentez-le.)

    public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
    {
        SPRemoteEventResult result = new SPRemoteEventResult();
    
        return result;
    }
    

    Tenez compte des informations suivantes :

    • L’objet SPRemoteEventProperties est envoyé à votre service web de gestionnaire sous la forme d’un message SOAP contenant des informations contextuelles issues de SharePoint, notamment une propriété EventType qui identifie l’événement.

    • L’objet SPRemoteEventResult renvoyé par votre gestionnaire contient une propriété Status dont les valeurs possibles sont SPRemoteEventServiceStatus.Continue, SPRemoteEventServiceStatus.CancelNoError et SPRemoteEventServiceStatus.CancelWithError. La valeur par défaut de la propriété Status est Continue. Celle-ci indique à SharePoint de terminer l’événement. Les deux autres valeurs donnent les instructions suivantes à SharePoint :

      • Exécutez votre gestionnaire jusqu’à trois reprises supplémentaires.
      • Si vous obtenez toujours un statut d’annulation, annulez l’événement et restaurez toutes les opérations qu’il a effectuées dans le cadre de l’événement.
  7. Pour identifier l’événement qui est géré, ajoutez la structure de commutateur suivante, immédiatement après la ligne déclarant la variable result.

    switch (properties.EventType)
    {
        case SPRemoteEventType.AppInstalled:
            break;
        case SPRemoteEventType.AppUpgraded:
            break;
        case SPRemoteEventType.AppUninstalling:
            break;
    }
    

    Remarque

    Si vous disposez de gestionnaires pour les événements AppInstalled, AppUpdated et AppInstalling, leur propre URL est inscrite dans le manifeste de complément. Vous pouvez donc avoir des points de terminaison différents pour eux, mais cet article (et les outils de développement Office pour Visual Studio) supposent qu’ils ont exactement le même point de terminaison ; c’est pourquoi le code doit déterminer quel événement l’a appelé.

  8. Comme expliqué dans la section Inclure une logique de restauration et une logique « Déjà terminé » dans vos gestionnaires d’événements de complément, si votre logique d’installation rencontre un problème, il est préférable d’annuler l’installation du complément, de demander à SharePoint de restaurer l’installation et au gestionnaire de restaurer les modifications apportées.

    Pour cela, ajoutez simplement le code suivant dans le bloc case pour l’événement AppInstalled.

    case SPRemoteEventType.AppInstalled:
    try
    {
        // Add-in installed event logic goes here.
    }
    catch (Exception e)
    {
        result.ErrorMessage = e.ErrorMessage;
        result.Status = SPRemoteEventServiceStatus.CancelWithError;
    
        // Rollback logic goes here.
    }
    break;
    

    Remarque

    Déplacez le code d’installation dont l’exécution dure plus de 30 secondes dans le complément lui-même. Vous pouvez l’ajouter à la logique de première exécution effectuée quand le complément s’exécute pour la première fois. Le complément peut afficher un message indiquant quelque chose comme « Nous préparons les choses pour vous ». Le complément peut également inviter l’utilisateur à exécuter le code d’initialisation.

    Si la logique de première exécution n’est pas réalisable pour votre complément, vous pouvez également demander à votre gestionnaire d’événements de lancer un processus asynchrone distant, puis de renvoyer immédiatement un objet SPRemoteEventResult dont la propriété Status a pour valeur Continue. En revanche, cette stratégie n’a aucun moyen d’indiquer à SharePoint de restaurer l’installation du complément en cas d’échec du processus distant.

  9. Comme expliqué dans la section Stratégies d’architecture de gestionnaire d’événements de complément, nous vous recommandons d’utiliser la stratégie de délégation de gestionnaire, même si ce n’est pas possible dans tous les cas. Dans l’exemple de cet article, nous vous montrons comment implémenter la stratégie de délégation de gestionnaire quand vous ajoutez une liste au site web hôte. Pour plus d’informations sur la création d’un gestionnaire d’événements AppInstalled similaire qui n’utilise pas la stratégie de délégation de gestionnaire, consultez l’exemple SharePoint/PnP/Samples/Core.AppEvents.

    Voici la nouvelle version du bloc case AppInstalled. La logique d’initialisation qui s’applique à tous les événements dépasse le bloc switch. Étant donné que la liste installée est supprimée du gestionnaire AppUninstalling, la liste est identifiée ici.

    SPRemoteEventResult result = new SPRemoteEventResult();
    String listTitle = "MyList";
    
    switch (properties.EventType)
    {               
        case SPRemoteEventType.AppInstalled:
    
    try
    {
            string error = TryCreateList(listTitle, properties);
            if (error != String.Empty)
            {
                throw new Exception(error);            
            }
    }
        catch (Exception e)
    {
            // Tell SharePoint to cancel the event.
            result.ErrorMessage = e.Message;
            result.Status = SPRemoteEventServiceStatus.CancelWithError;               
        }
            break;
        case SPRemoteEventType.AppUpgraded:
        break;
        case SPRemoteEventType.AppUninstalling:
        break;
    }                      
    
  10. Ajoutez la méthode de création de la liste à la classe AppEventReceiver en tant que méthode private avec le code suivant. Notez que la classe TokenHelper dispose d'une méthode spéciale, optimisée afin d'obtenir un contexte client pour un événement de complément. La transmission de la valeur false pour le dernier paramètre garantit que le contexte est celui du site web hôte.

    private string TryCreateList(String listTitle, SPRemoteEventProperties properties)
    {    
        string errorMessage = String.Empty;          
    
        using (ClientContext clientContext =
            TokenHelper.CreateAppEventClientContext(properties, useAppWeb: false))
        {
            if (clientContext != null)
            {
            }
        }
        return errorMessage;
    }
    
    
  11. La logique de restauration est essentiellement une logique de gestion des exceptions et le modèle CSOM SharePoint (modèle objet client) dispose d’un élément ExceptionHandlingScope qui permet à votre service web de déléguer la gestion des exceptions au serveur SharePoint (consultez également la rubrique Procédure : Utiliser l’étendue de gestion des exceptions).

    Ajoutez le code suivant au bloc if dans l’extrait de code précédent.

    ExceptionHandlingScope scope = new ExceptionHandlingScope(clientContext); 
    
    using (scope.StartScope()) 
    { 
        using (scope.StartTry()) 
        { 
        }         
        using (scope.StartCatch()) 
        {                                 
        } 
        using (scope.StartFinally()) 
        { 
        } 
    } 
    clientContext.ExecuteQuery();
    
    if (scope.HasException)
    {
        errorMessage = String.Format("{0}: {1}; {2}; {3}; {4}; {5}", 
            scope.ServerErrorTypeName, scope.ErrorMessage, 
            scope.ServerErrorDetails, scope.ServerErrorValue, 
            scope.ServerStackTrace, scope.ServerErrorCode);
    }
    
  12. L’extrait de code ci-dessus contient seulement un appel à SharePoint (ExecuteQuery), ce qui n’est pas suffisant. Chaque objet référencé dans notre étendue des exceptions doit d’abord être chargé vers le client.

    Ajoutez le code suivant au-dessus du constructeur pour ExceptionHandlingScope.

    ListCollection allLists = clientContext.Web.Lists;
    IEnumerable<List> matchingLists =
        clientContext.LoadQuery(allLists.Where(list => list.Title == listTitle));
    clientContext.ExecuteQuery();
    
    var foundList = matchingLists.FirstOrDefault();
    List createdList = null;
    
  13. Pour créer une liste web hôte, le code doit être inséré dans le bloc StartTry, mais le code doit d’abord vérifier si la liste a déjà été ajoutée (comme expliqué dans la section Inclure une logique de restauration et une logique « Déjà terminé » dans vos gestionnaires d’événements de complément). Vous pouvez déléguer la logique if-then-else au serveur SharePoint à l’aide de la classe ConditionalScope (consultez la rubrique Procédure : Utilisation de l’étendue conditionnelle).

    Ajoutez le code suivant dans le bloc StartTry.

    ConditionalScope condScope = new ConditionalScope(clientContext, 
            () => foundList.ServerObjectIsNull.Value == true, true);
    using (condScope.StartScope())
    {
        ListCreationInformation listInfo = new ListCreationInformation();
        listInfo.Title = listTitle;
        listInfo.TemplateType = (int)ListTemplateType.GenericList;
        listInfo.Url = listTitle;
        createdList = clientContext.Web.Lists.Add(listInfo);                                
    }
    
  14. Le bloc StartCatch doit annuler la création de la liste, mais il doit d’abord vérifier que la liste a été créée. En effet, une exception pourrait avoir été déclenchée dans le bloc StartTry avant la fin de la création de la liste.

    Ajoutez le code suivant au bloc StartCatch.

    ConditionalScope condScope = new ConditionalScope(clientContext, 
            () => createdList.ServerObjectIsNull.Value != true, true);
    using (condScope.StartScope())
    {
        createdList.DeleteObject();
    } 
    

    Conseil

    RÉSOLUTION DES PROBLÈMES : pour vérifier si votre bloc StartCatch est inséré où il se doit, il suffit de générer une exception runtime sur le serveur SharePoint. Le fait d’utiliser une expression throw ou une division par zéro ne fonctionne pas car cela génère des exceptions côté client avant même que le runtime du client regroupe le code et l’envoie au serveur (avec la méthode ExecuteQuery).

    Ajoutez plutôt les lignes suivantes au bloc StartTry. Le runtime côté client les accepte, même si elles génèrent une exception côté serveur (mais c’est ce qu’il nous faut).

    List fakeList = clientContext.Web.Lists.GetByTitle("NoSuchList");

    clientContext.Load(fakeList);

La méthode TryCreateList complète doit ressembler à ceci. (le bloc StartFinally est obligatoire même s’il n’est pas utilisé) :

    private string TryCreateList(String listTitle, SPRemoteEventProperties properties)
    {    
        string errorMessage = String.Empty;  

        using (ClientContext clientContext = 
            TokenHelper.CreateAppEventClientContext(properties, useAppWeb: false))
        {
            if (clientContext != null)
            {
                ListCollection allLists = clientContext.Web.Lists;
                IEnumerable<List> matchingLists = 
                    clientContext.LoadQuery(allLists.Where(list => list.Title == listTitle));
                clientContext.ExecuteQuery();
                var foundList = matchingLists.FirstOrDefault();
                List createdList = null;

                ExceptionHandlingScope scope = new ExceptionHandlingScope(clientContext); 
                using (scope.StartScope()) 
                { 
                    using (scope.StartTry()) 
                    { 
                        ConditionalScope condScope = new ConditionalScope(clientContext, 
                                () => foundList.ServerObjectIsNull.Value == true, true);  
                        using (condScope.StartScope())
                        {
                            ListCreationInformation listInfo = new ListCreationInformation();
                            listInfo.Title = listTitle;
                            listInfo.TemplateType = (int)ListTemplateType.GenericList;
                            listInfo.Url = listTitle;
                            createdList = clientContext.Web.Lists.Add(listInfo);
                        }
                    } 
                    
                    using (scope.StartCatch()) 
                    { 
                        ConditionalScope condScope = new ConditionalScope(clientContext, 
                                () => createdList.ServerObjectIsNull.Value != true, true);
                        using (condScope.StartScope())
                        {
                            createdList.DeleteObject();
                        }    
                    } 

                    using (scope.StartFinally()) 
                    { 
                    } 
                } 
                clientContext.ExecuteQuery();

                if (scope.HasException)
                {
                        errorMessage = String.Format("{0}: {1}; {2}; {3}; {4}; {5}", 
                        scope.ServerErrorTypeName, scope.ErrorMessage, 
                        scope.ServerErrorDetails, scope.ServerErrorValue, 
                        scope.ServerStackTrace, scope.ServerErrorCode);
                }
            }
        }
        return errorMessage;
    }

Conseil

DÉBOGAGE : que vous utilisiez ou non la stratégie de délégation de gestionnaire, quand vous exécutez le code pas à pas avec le débogueur, rappelez-vous que, quel que soit le scénario dans lequel votre gestionnaire renvoie un état d’annulation, SharePoint va rappeler votre gestionnaire, au moins trois fois. Ainsi, le débogueur navigue dans le code jusqu’à quatre fois.

Conseil

ARCHITECTURE DU CODE : étant donné que vous pouvez installer des composants sur le site web de complément avec des balises déclaratives en dehors de votre gestionnaire, nous vous déconseillons d’interagir avec le site web de complément pendant les 30 secondes réservées à votre gestionnaire. Si vous le faites, rappelez-vous que votre code nécessite un objet ClientContext distinct pour le site web de complément. En effet, le site web de complément et le site web hôte sont des composants différents, tout comme une base de données SQL Server est différente de chacun d’eux. Ainsi, une méthode qui appelle le site web de complément se trouve dans le bloc try du bloc case AppInstalled, à l’instar de la méthode TryCreateList dans l’exemple. Toutefois, votre gestionnaire n’a pas besoin de restaurer les actions effectuées sur le site web de complément. S’il rencontre une erreur, il lui suffit d’annuler l’événement, car SharePoint supprime l’intégralité du site web de complément en cas d’annulation de l’événement.

Créer un récepteur d’événements de désinstallation de complément

  1. Définissez la propriété Gérer la désinstallation du complément du projet sur True. Les outils ne créent pas de fichier de service web supplémentaire s’il en existe déjà un, mais ils ajoutent un élément UninstallingEventEndpoint au manifeste du complément.

  2. Le code dans le bloc case AppUninstalling doit supprimer les artefacts du complément qui ne sont plus utiles après la suppression du complément de la corbeille de second niveau (c’est ce qui déclenche l’événement). Toutefois, vous devez dès que possible « retirer » les composants au lieu de les supprimer totalement. En effet, vous devez les restaurer si l’événement de désinstallation doit être restauré. Dans ce cas, le complément se trouve toujours dans la corbeille de second niveau. L’utilisateur peut alors le restaurer et recommencer à l’utiliser. Le fait de simplement recréer un composant qui a été supprimé de votre logique de restauration devrait suffire à refaire fonctionner le complément, mais les données ou paramètres de configuration du composant seront perdus.

    Cette stratégie est relativement simple pour les composants SharePoint, étant donné que les éléments peuvent être restaurés dans la corbeille de SharePoint et que des API CSOM permettent d’y accéder. Vous verrez comment plus loin. Différentes techniques peuvent être utiles sur d’autres plateformes. Par exemple, pour retirer une ligne d’une table SQL Server dans votre gestionnaire de désinstallation de complément, une procédure T-SQL stockée dans le gestionnaire peut ajouter une colonne IsDeleted à la table et la définir sur True pour la ligne. Si la procédure rencontre une erreur, la logique de restauration rétablit la valeur sur False. Si la procédure se termine sans erreur, elle peut définir un travail du minuteur pour supprimer la ligne plus tard, juste avant de renvoyer un indicateur de réussite.

    Parfois, il est utile de conserver des données, telles que des listes, même après la suppression du complément. Cependant, voici un gestionnaire d’événements de désinstallation qui supprime la liste créée avec le gestionnaire d’événements installé.

    case SPRemoteEventType.AppUninstalling:
    
    try
    {
        string error = TryRecycleList(listTitle, properties);
        if (error != String.Empty)
        {
            throw new Exception(error);
        }
    }
    catch (Exception e)
    {
        // Tell SharePoint to cancel the event.
        result.ErrorMessage = e.Message;
        result.Status = SPRemoteEventServiceStatus.CancelWithError;
    }
    break;
    
  3. Ajoutez la méthode d'assistance pour le recyclage de la liste. Notez les points suivants concernant ce code :

    • Le code recycle la liste au lieu de la supprimer de façon permanente. Cela rend sa restauration possible, y compris celle de ses données, en cas d'échec de l'événement, ce en quoi consiste le bloc StartCatch. Ainsi, en cas de réussite de la méthode et une fois l'événement terminé, le complément est supprimé de façon permanente de la corbeille secondaire, mais la liste est toujours dans la corbeille primaire.

    • Le code vérifie l'existence de la liste avant de la recycler, car un utilisateur pourrait l'avoir déjà recyclée dans l'interface SharePoint. De la même manière, le code de restauration vérifie l'existence de la liste dans la corbeille avant de la restaurer, car un utilisateur pourrait l'avoir déjà restaurée ou déplacée vers la corbeille secondaire.

    • Deux étendues conditionnelles permettent de savoir si une liste existe en vérifiant si une de ses références a pour valeur null. Cependant, elles possèdent toutes deux un bloc if interne qui teste la nullité de cet objet une deuxième fois. Les tests externes, avec des blocs d’étendues conditionnelles, s’exécutent sur le serveur, mais des tests internes de nullité doivent également être réalisés. En effet, le runtime du client parcourt le code ligne par ligne pour créer le message XML envoyé par la méthode ExecuteQuery au serveur. Quand les références aux objets foundList et recycledList sont trouvées, l’une de ces lignes génère une exception de référence Null, sauf si elles sont comprises dans les vérifications internes de nullité.

      private string TryRecycleList(String listTitle, SPRemoteEventProperties properties)
      {
          string errorMessage = String.Empty;
      
          using (ClientContext clientContext = 
              TokenHelper.CreateAppEventClientContext(properties, useAppWeb: false))
          {
              if (clientContext != null)
              {
                  ListCollection allLists = clientContext.Web.Lists;
                  IEnumerable<List> matchingLists = 
                      clientContext.LoadQuery(allLists.Where(list => list.Title == listTitle));
                  RecycleBinItemCollection bin = clientContext.Web.RecycleBin;
                  IEnumerable<RecycleBinItem> matchingRecycleBinItems = 
                      clientContext.LoadQuery(bin.Where(item => item.Title == listTitle));        
                  clientContext.ExecuteQuery();
      
                  List foundList = matchingLists.FirstOrDefault();
                  RecycleBinItem recycledList = matchingRecycleBinItems.FirstOrDefault();    
      
                  ExceptionHandlingScope scope = new ExceptionHandlingScope(clientContext);
                  using (scope.StartScope())
                  {
                      using (scope.StartTry())
                      {
                          ConditionalScope condScope = new ConditionalScope(clientContext, 
                              () => foundList.ServerObjectIsNull.Value == false, true);
                          using (condScope.StartScope())
                          {
                              if (foundList != null)
                              {
                                  foundList.Recycle();
                              }
                          }
                      }
                      using (scope.StartCatch())
                      {
                          ConditionalScope condScope = new ConditionalScope(clientContext, 
                              () => recycledList.ServerObjectIsNull.Value == false, true);
                          using (condScope.StartScope())
                          {
                              if (recycledList != null)
                              {
                                  recycledList.Restore(); 
                              }
                          }
                      }
                      using (scope.StartFinally())
                      {
                      }
                  }
                  clientContext.ExecuteQuery();
      
                  if (scope.HasException)
                  {
                      errorMessage = String.Format("{0}: {1}; {2}; {3}; {4}; {5}", 
                          scope.ServerErrorTypeName, scope.ErrorMessage, 
                          scope.ServerErrorDetails, scope.ServerErrorValue, 
                          scope.ServerStackTrace, scope.ServerErrorCode);
                  }
              }
          }
          return errorMessage;
      }
      

Pour déboguer et tester un récepteur d’événements de désinstallation de complément

  1. Ouvrez les pages suivantes dans des fenêtres ou des onglets distincts :

    • Contenu du site
    • Paramètres du site - Corbeille (_layouts/15/AdminRecycleBin.aspx?ql=1)
    • Corbeille - Corbeille de second niveau (_layouts/15/AdminRecycleBin.aspxView=2&?ql=1)
  2. Sélectionnez F5 et approuvez le complément quand vous y êtes invité. La page de démarrage du complément s’ouvre. Si vous comptez uniquement tester le gestionnaire de désinstallation, vous pouvez fermer cette fenêtre du navigateur. Toutefois, si vous déboguez le gestionnaire, laissez-le ouvert. Sa fermeture met fin à la session de débogage.

  3. Actualisez la page Contenu du site et, quand le complément s’affiche, supprimez-le.

  4. Actualisez la page Paramètres du site - Corbeille. Le complément apparaît en tête des éléments affichés. Cochez la case à côté du complément et cliquez sur Supprimer la sélection.

  5. Actualisez la page Corbeille - Corbeille de second niveau. Le complément apparaît en tête des éléments affichés. Cochez la case à côté du complément et cliquez sur Supprimer la sélection. SharePoint appelle immédiatement votre gestionnaire de désinstallation de complément.

Créer un récepteur d’événements mis à jour de compléments

Pour savoir comment créer un gestionnaire de mise à jour de compléments, consultez l’article Créer un gestionnaire pour l’événement de mise à jour dans les compléments SharePoint.

URL et restrictions d’hébergement sur les récepteurs d’événements de compléments de production

Le récepteur d’événements distants peut être hébergé dans le cloud ou sur un serveur local qui n’est pas utilisé en même temps comme serveur SharePoint. L’URL d’un récepteur de production ne peut pas spécifier un port particulier. Cela signifie que vous devez utiliser soit le port 443 pour HTTPS (recommandé) soit le port 80 pour HTTP. Si vous utilisez HTTPS et que le service de récepteur est hébergé sur le serveur local, mais que le complément se trouve sur SharePoint Online, le serveur d’hébergement doit obtenir un certificat approuvé publiquement auprès d’une autorité de certification. (Un certificat auto-signé est valable uniquement si le complément se trouve sur une batterie de serveurs SharePoint locale.)

Voir aussi