Partager via


Exemple de complément SharePoint synchroniser des groupes de termes

L’exemple Core.MMSSync vous montre comment utiliser un complément hébergé par un fournisseur pour synchroniser une taxonomie source et cible. Ce complément synchronise deux magasins de termes dans le service de métadonnées managées : un magasin de termes source et un magasin de termes cible.

Les objets suivants sont utilisés pour synchroniser les groupes de termes :

  • TermStore
  • ChangeInformation

Utilisez cette solution si vous souhaitez :

  • Synchronisez deux taxonomies. Par exemple, vous pouvez utiliser SharePoint Online et SharePoint Server en local pour différents jeux de données, mais ils utilisent la même taxonomie.
  • Synchronisez uniquement les modifications apportées à un groupe de termes spécifique.

Avant de commencer

Pour commencer, téléchargez l’exemple de complément Core.MMSSync à partir du projet Office 365 Developer Patterns and Practices sur GitHub.

Remarque

Le code dans cet article est fourni tel quel, sans garantie d’aucune sorte, expresse ou implicite, y compris mais sans s’y limiter, aucune garantie implicite d’adéquation à un usage particulier, à une qualité marchande ou une absence de contrefaçon.

Avant d’exécuter ce complément, vous devez avoir l’autorisation d’accéder au magasin de termes dans le service de métadonnées managées. La figure suivante illustre le centre d’administration Office 365 où ces autorisations sont assignées.

Capture d’écran illustrant le Centre d’administration SharePoint avec le magasin de termes, la zone de recherche du magasin de termes de taxonomie et les zones d’administrateurs du magasin de termes mis en surbrillance.

Pour attribuer des autorisations au magasin de termes :

  1. Dans le Centre d’administration Office 365, choisissez Magasin de termes.

  2. Dans MAGASIN DE TERMES DE TAXONOMIE, sélectionnez le jeu de termes auquel vous voulez attribuer un administrateur.

  3. Dans Administrateurs de magasin de termes, entrez le compte d’organisation nécessitant des autorisations d’administrateur de magasin de termes.

Utilisation de l’exemple de complément Core.MMSSync

Lorsque vous démarrez le complément, vous voyez une application console Core.MMSSync, comme illustré dans la figure suivante. Vous êtes invité à entrer les informations suivantes :

  • URL du centre d’administration Office 365 qui contient le magasin de termes source (il s’agit de l’URL du service de métadonnées managées source). Par exemple, vous pouvez entrer https://contososource-admin.sharepoint.com.
  • Nom d’utilisateur et mot de passe d’un administrateur de magasin de termes sur votre service de métadonnées managées source.
  • URL du centre d’administration Office 365 qui contient le magasin de termes cible (il s’agit de l’URL du MMS cible). Par exemple, vous pouvez entrer https://contosotarget-admin.sharepoint.com.
  • Nom d’utilisateur et mot de passe d’un administrateur de magasin de termes sur votre service de métadonnées managées cible.
  • Type d’opération que vous souhaitez effectuer. Vous pouvez :
    • Déplacer un groupe de termes (scénario 1) à l’aide de l’objet TermStore .
    • Traitez les modifications (scénario 2) à l’aide de l’objet ChangeInformation .

Importante

Cet exemple de complément fonctionne avec SharePoint Online et SharePoint Server en local.

Capture d’écran de l’application console demandant la saisie d’informations.

Après avoir sélectionné votre scénario, entrez le nom du groupe de termes que vous souhaitez synchroniser à partir de votre source avec votre service de métadonnées managé cible, comme illustré dans la figure suivante. Par exemple, vous pouvez entrer Enterprise.

Capture d’écran de la liste déroulante du magasin de termes de taxonomie.

Scénario 1 - Déplacer un groupe de termes

Lorsque vous sélectionnez Déplacer un groupe de termes, le complément vous invite à entrer un groupe de termes à synchroniser, puis appelle la méthode CopyNewTermGroups dans MMSSyncManager.cs. CopyNewTermGroups effectue ensuite les opérations suivantes pour copier un groupe de termes du magasin de termes source vers le magasin de termes cible :

  1. Récupère les objets de magasin de termes source et cible.

  2. Vérifie que les langues des magasins de termes source et cible correspondent.

  3. Vérifie que le groupe de termes sources n’existe pas dans le magasin de termes cible, puis copie le groupe de termes sources dans le magasin de termes cible à l’aide de CreateNewTargetTermGroup.

Vous pouvez définir les paramètres TermGroupExclusions, TermGroupToCopy et TermSetInclusions pour filtrer les termes traités.

Le code suivant montre les méthodes CopyNewTermGroups et CreateNewTargetTermGroup dans MMSSyncManager.cs.

public bool CopyNewTermGroups(ClientContext sourceContext, ClientContext targetContext, List<string> termGroupExclusions = null, string termGroupToCopy = null)
        {
            TermStore sourceTermStore = GetTermStoreObject(sourceContext);
            TermStore targetTermStore = GetTermStoreObject(targetContext);

            
            List<int> languagesToProcess = null;
            if (!ValidTermStoreLanguages(sourceTermStore, targetTermStore, out languagesToProcess))
            {
                Log.Internal.TraceError((int)EventId.LanguageMismatch, "The target termstore default language is not available as language in the source term store, syncing cannot proceed.");
                return false;
            }

            // Get a list of term groups to process. Exclude site collection-scoped groups and system groups.
            IEnumerable<TermGroup> termGroups = sourceContext.LoadQuery(sourceTermStore.Groups.Include(g => g.Name,
                                                                                                       g => g.Id,
                                                                                                       g => g.IsSiteCollectionGroup,
                                                                                                       g => g.IsSystemGroup))
                                                                                              .Where(g => g.IsSystemGroup == false &amp;&amp; g.IsSiteCollectionGroup == false);
            sourceContext.ExecuteQuery();

            foreach (TermGroup termGroup in termGroups)
            {
                // Skip term group if you only want to copy one particular term group.
                if (!String.IsNullOrEmpty(termGroupToCopy))
                {
                    if (!termGroup.Name.Equals(termGroupToCopy, StringComparison.InvariantCultureIgnoreCase))
                    {
                        continue;
                    }
                }

                // Skip term groups that you do not want to copy.
                if (termGroupExclusions != null &amp;&amp; termGroupExclusions.Contains(termGroup.Name, StringComparer.InvariantCultureIgnoreCase))
                {
                    Log.Internal.TraceInformation((int)EventId.CopyTermGroup_Skip, "Skipping {0} as this is a system termgroup", termGroup.Name);
                    continue;
                }

                // About to start copying a term group.
                TermGroup sourceTermGroup = GetTermGroup(sourceContext, sourceTermStore, termGroup.Name);
                TermGroup targetTermGroup = GetTermGroup(targetContext, targetTermStore, termGroup.Name);

                if (sourceTermGroup == null)
                {
                    continue;
                }
                if (targetTermGroup != null)
                {
                    if (sourceTermGroup.Id != targetTermGroup.Id)
                    {
                        // Term group exists with a different ID, unable to sync.
                        Log.Internal.TraceWarning((int)EventId.CopyTermGroup_IDMismatch, "The term groups have different ID's. I don't know how to work it.");
                    }
                    else
                    {
                        // Do nothing as this term group was previously copied. Term group changes need to be 
                        // picked up by the change log processing.
                        Log.Internal.TraceInformation((int)EventId.CopyTermGroup_AlreadyCopied, "Termgroup {0} was already copied...changes to it will need to come from changelog processing.", termGroup.Name);
                    }
                }
                else
                {
                    Log.Internal.TraceInformation((int)EventId.CopyTermGroup_Copying, "Copying termgroup {0}...", termGroup.Name);
                    this.CreateNewTargetTermGroup(sourceContext, targetContext, sourceTermGroup, targetTermStore, languagesToProcess);
                }
            }

            return true;
        }



private void CreateNewTargetTermGroup(ClientContext sourceClientContext, ClientContext targetClientContext, TermGroup sourceTermGroup, TermStore targetTermStore, List<int> languagesToProcess)
        {
            TermGroup destinationTermGroup = targetTermStore.CreateGroup(sourceTermGroup.Name, sourceTermGroup.Id);
            if (!string.IsNullOrEmpty(sourceTermGroup.Description))
            {
                destinationTermGroup.Description = sourceTermGroup.Description;
            }

            TermSetCollection sourceTermSetCollection = sourceTermGroup.TermSets;
            if (sourceTermSetCollection.Count > 0)
            {
                foreach (TermSet sourceTermSet in sourceTermSetCollection)
                {
                    sourceClientContext.Load(sourceTermSet,
                                              set => set.Name,
                                              set => set.Description,
                                              set => set.Id,
                                              set => set.Contact,
                                              set => set.CustomProperties,
                                              set => set.IsAvailableForTagging,
                                              set => set.IsOpenForTermCreation,
                                              set => set.CustomProperties,
                                              set => set.Terms.Include(
                                                        term => term.Name,
                                                        term => term.Description,
                                                        term => term.Id,
                                                        term => term.IsAvailableForTagging,
                                                        term => term.LocalCustomProperties,
                                                        term => term.CustomProperties,
                                                        term => term.IsDeprecated,
                                                        term => term.Labels.Include(label => label.Value, label => label.Language, label => label.IsDefaultForLanguage)));

                    sourceClientContext.ExecuteQuery();

                    TermSet targetTermSet = destinationTermGroup.CreateTermSet(sourceTermSet.Name, sourceTermSet.Id, targetTermStore.DefaultLanguage);
                    targetClientContext.Load(targetTermSet, set => set.CustomProperties);
                    targetClientContext.ExecuteQuery();
                    UpdateTermSet(sourceClientContext, targetClientContext, sourceTermSet, targetTermSet);

                    foreach (Term sourceTerm in sourceTermSet.Terms)
                    {
                        Term reusedTerm = targetTermStore.GetTerm(sourceTerm.Id);
                        targetClientContext.Load(reusedTerm);
                        targetClientContext.ExecuteQuery();

                        Term targetTerm;
                        if (reusedTerm.ServerObjectIsNull.Value)
                        {
                            try
                            {
                                targetTerm = targetTermSet.CreateTerm(sourceTerm.Name, targetTermStore.DefaultLanguage, sourceTerm.Id);
                                targetClientContext.Load(targetTerm, term => term.IsDeprecated,
                                                                     term => term.CustomProperties,
                                                                     term => term.LocalCustomProperties);
                                targetClientContext.ExecuteQuery();
                                UpdateTerm(sourceClientContext, targetClientContext, sourceTerm, targetTerm, languagesToProcess);
                            }
                            catch (ServerException ex)
                            {
                                if (ex.Message.IndexOf("Failed to read from or write to database. Refresh and try again.") > -1)
                                {
                                    // This exception was due to caching issues and generally is thrown when terms are reused across groups.
                                    targetTerm = targetTermSet.ReuseTerm(reusedTerm, false);
                                }
                                else
                                {
                                    throw ex;
                                }
                            }
                        }
                        else
                        {
                            targetTerm = targetTermSet.ReuseTerm(reusedTerm, false);
                        }

                        targetClientContext.Load(targetTerm);
                        targetClientContext.ExecuteQuery();

                        targetTermStore.UpdateCache();

                        // Refresh session and term store references to force reload of the term just added. You need 
                        // to do this because there can be an update change event following next, and if you don't,
                        // the newly created term set cannot be obtained from the server.
                        targetTermStore = GetTermStoreObject(targetClientContext);

                        // Recursively add the other terms.
                        ProcessSubTerms(sourceClientContext, targetClientContext, targetTermSet, targetTerm, sourceTerm, languagesToProcess, targetTermStore.DefaultLanguage);
                    }
                }
            }
            targetClientContext.ExecuteQuery();
        }

Scénario 2 - Traiter les modifications

Lorsque vous sélectionnez Traiter les modifications, le complément vous invite à entrer un groupe de termes à synchroniser, puis appelle la méthode ProcessChanges dans MMSSyncManager.cs. ProcessChanges utilise la méthode GetChanges de la classe ChangedInformation pour récupérer toutes les modifications apportées aux groupes, aux ensembles de termes et aux termes dans le service de métadonnées managées source. Les modifications sont ensuite appliquées au service de métadonnées managées cible.

Remarque

Ce document inclut uniquement certaines parties de la méthode ProcessChanges . Pour passer en revue l’ensemble de la méthode, ouvrez la solution Core.MMSSync dans Visual Studio.


La méthode ProcessChanges commence par créer un objet TaxonomySession .

Log.Internal.TraceInformation((int)EventId.TaxonomySession_Open, "Opening the taxonomy session");
            TaxonomySession sourceTaxonomySession = TaxonomySession.GetTaxonomySession(sourceClientContext);
            TermStore sourceTermStore = sourceTaxonomySession.GetDefaultKeywordsTermStore();
            sourceClientContext.Load(sourceTermStore,
                                            store => store.Name,
                                            store => store.DefaultLanguage,
                                            store => store.Languages,
                                            store => store.Groups.Include(group => group.Name, group => group.Id));
            sourceClientContext.ExecuteQuery();


Ensuite, il récupère les modifications à l’aide de l’objet ChangeInformation et en définissant la date de début sur l’objet ChangeInformation . Cet exemple récupère toutes les modifications apportées au cours de l’année précédente.

Log.Internal.TraceInformation((int)EventId.TermStore_GetChangeLog, "Reading the changes");
            ChangeInformation changeInformation = new ChangeInformation(sourceClientContext);
            changeInformation.StartTime = startFrom;
            ChangedItemCollection termStoreChanges = sourceTermStore.GetChanges(changeInformation);
            sourceClientContext.Load(termStoreChanges);
            sourceClientContext.ExecuteQuery();


La méthode GetChanges renvoie une objet ChangedItemCollection, qui énumère toutes les modifications qui se produisent dans le magasin de termes, comme illustré dans l’exemple de code suivant. La dernière ligne de l’exemple vérifie si l’objet ChangedItem était un groupe de termes. ProcessChanges inclut du code pour effectuer des vérifications similaires sur l’objet ChangedItem pour les ensembles de termes et les termes.

foreach (ChangedItem _changeItem in termStoreChanges)
                {
                    
                    if (_changeItem.ChangedTime < startFrom)
                    {
                        Log.Internal.TraceVerbose((int)EventId.TermStore_SkipChangeLogEntry, "Skipping item {1} changed at {0}", _changeItem.ChangedTime, _changeItem.Id);
                        continue;
                    }

                    Log.Internal.TraceVerbose((int)EventId.TermStore_ProcessChangeLogEntry, "Processing item {1} changed at {0}. Operation = {2}, ItemType = {3}", _changeItem.ChangedTime, _changeItem.Id, _changeItem.Operation, _changeItem.ItemType);

                    #region Group changes
                    if (_changeItem.ItemType == ChangedItemType.Group)


Le type d’élément modifié peut être un groupe de termes, un ensemble de termes ou un terme. Chaque type d’élément modifié a des opérations différentes que vous pouvez effectuer sur celui-ci. Le tableau suivant répertorie les opérations que vous pouvez effectuer sur chaque type d’élément modifié.


Qu’est-ce qui a changé ? (ChangedItemType) Opérations que vous pouvez effectuer sur le type d’élément modifié (ChangedOperationType)
Group

Delete group

Ajout d’un groupe

Modifier le groupe

TermSet

Supprimer un ensemble de termes

Déplacer un ensemble de termes

Copier l’ensemble de termes

Ajouter un ensemble de termes

Modifier l’ensemble de termes

Terme

Supprimer un terme

Déplacer un terme

Copier le terme

Terme de modification de chemin

Terme de fusion

Ajouter un terme

Modifier le terme

Le code suivant montre comment effectuer une opération de suppression lorsqu’un groupe de termes a été supprimé dans le service de métadonnées managées source.

#region Delete group
                        if (_changeItem.Operation == ChangedOperationType.DeleteObject)
                        {
                            TermGroup targetTermGroup = targetTermStore.GetGroup(_changeItem.Id);
                            targetClientContext.Load(targetTermGroup, group => group.Name);
                            targetClientContext.ExecuteQuery();

                            if (!targetTermGroup.ServerObjectIsNull.Value)
                            {
                                if (termGroupExclusions == null || !termGroupExclusions.Contains(targetTermGroup.Name, StringComparer.InvariantCultureIgnoreCase))
                                {
                                    Log.Internal.TraceInformation((int)EventId.TermGroup_Delete, "Deleting group: {0}", targetTermGroup.Name);
                                    targetTermGroup.DeleteObject();
                                    targetClientContext.ExecuteQuery();
                                }
                            }
                        }
                        #endregion

Voir aussi