Fermer

décembre 18, 2020

Restriction des types disponibles en fonction du contexte du site dans Episerver


Valdis Iljuconoks m'a précédemment aidé à comprendre comment implémenter efficacement les restrictions AllowedTypes avec des interfaces, quelque chose comme [AllowedTypes(typeof(INestedContent))] – qui est une belle solution pour construire une bibliothèque de blocs. Cela fait que nos blocs et leurs zones de contenu ne concernent que des interfaces spécifiques. Dans notre cas, nous avons généralement des couches telles que IPageContent (pour les rayures, les structures en grille, etc.), INestedContent (pour les héros, les teasers, les formulaires, les cartes, les vidéos, etc.) et ICallToAction (pour les boutons, les boutons d'image, etc.). Ceci est extrêmement utile si vous ne voulez pas que tous vos blocs connaissent tous vos autres blocs, ou si vos blocs vivent dans des projets de fonctionnalités indépendants.

Cette solution, cependant, commence à montrer quelques faiblesses lorsque vous passez à une implémentation multisite avec une couche de bloc partagée. Dans de nombreux cas, nous créerons finalement des blocs spécifiques à un site Web donné. Dans une solution SCORE, par exemple, ces blocs implémenteraient l'interface IPageContent ou INestedContent afin qu'ils se plug-and-play bien avec la couche de bloc commune. Le problème se pose, cependant, lorsque vous voulez restreindre les blocs à SiteA et ne pas les autoriser à être créés sur SiteB et SiteC.

Pour mettre en place ce scénario, Valdis a à nouveau une très belle série de blogs sur le support MVC Area dans Episerver. Si ce concept est nouveau pour vous, je vous recommande vivement de lire ici et ici .

Pour étendre cette configuration, ce dont j'avais besoin était la capacité de décorer mon partage global blocs avec un nouvel attribut: [RestrictTo(new [] {"SiteA", "SiteX"})] . Si l'éditeur se trouve sur une page pour SiteA ou SiteX, il devrait alors pouvoir insérer un bloc de ce type. Si l'éditeur est sur SiteB ou SiteC, ce bloc ne doit pas être une option disponible. D'après tout ce que je peux dire, le seul mécanisme prêt à l'emploi pour prendre en charge cela dans Episerver est de restreindre les blocs par rôle d'utilisateur. Cela ne fonctionnerait pas efficacement dans mon cas (et dans de nombreux cas), car nos rôles d'utilisateur ne correspondent pas toujours 1: 1 aux sites Web. Alors comment faire?

Un petit code

 SAS IDC Episerver Commerce Guide

Tout d’abord, définissons l’attribut en question. C’est assez simple:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
 classe publique RestrictToAttribute: Attribute
{
    chaîne publique [] Sites {get; ensemble; }

    public RestrictToAttribute (chaîne [] sites)
    {
        Sites = sites;
    }
} 

Deuxièmement, pour utiliser cet attribut, nous devrions faire quelque chose comme ceci pour nos blocs (cela fonctionne également pour PageData si vous en avez besoin):

[ContentType(...)]
 [RestrictTo(new[] {"SiteA"})] / / déplacez ceci vers const quelque part dans votre solution
classe publique SiteSpecificBlock: BlockData, INestedContentBlock
{
    // une implication spécifique ici
} 

Du point de vue du consommateur et en tant que développeur entrant dans un projet, cela semble être une manière très conviviale d'obtenir des restrictions basées sur le site. C'est vraiment facile à suivre et à exploiter. Pour que cela fonctionne, ce que nous devons faire est de shim dans notre propre implémentation de EPiServer.DataAbstraction.Internal.DefaultContentTypeAvailablilityService . Malheureusement, cela fait partie de l'API interne d'Episerver, donc si vous connaissez une meilleure façon de gérer cela, veuillez laisser un commentaire ci-dessous!

Pour shim dans notre propre implémentation, ajoutez ceci à votre configuration de conteneur:

 services. RemoveAll (typeof (ContentTypeAvailabilityService));
services.AddTransient  ();

Cela garantira que toute personne qui demande ContentTypeAvailabilityService du conteneur recevra à la place le RestrictedContentTypeAvailabilityService (même le runtime Episerver lorsqu'il dessine l'interface d'édition). À partir de là, nous devons simplement manipuler la liste des types que le service d’Episerver renvoie, en tenant compte de l’attribut RestrictTo . Nous hériterons du service d'Epi et appellerons leur méthode de base, avec un filtrage spécial superposé:

 public class RestrictedContentTypeAvailabilityService: DefaultContentTypeAvailablilityService
{
    private readonly IContentLoader _contentLoader;
    private en lecture seule ISiteDefinitionResolver _siteDefinitionResolver;

    public RestrictedContentTypeAvailabilityService (
        ServiceAccessor  contentTypeRepositoryAccessor,
        IAvailableModelSettingsRepository modelRepository,
        IAvailableSettingsRepository typeSettingsRepository,
        GroupDefinitionRepository groupDefinitionRepository,
        IContentLoader contentLoader,
        Cache ISynchronizedObjectInstanceCache,
        ISiteDefinitionResolver siteDefinitionResolver)
        : base (contentTypeRepositoryAccessor, modelRepository, typeSettingsRepository, groupDefinitionRepository,
            contentLoader, cache)
    {
        if (contentLoader == null)
            throw new ArgumentNullException ("contentLoader");
        if (siteDefinitionResolver == null)
            throw new ArgumentNullException ("siteDefinitionResolver");

        _contentLoader = contentLoader;
        _siteDefinitionResolver = siteDefinitionResolver;
    }

    // cette méthode est appelée à chaque fois que "Ajouter un nouveau bloc" ou "Ajouter une nouvelle page" est appelé - il récupère les types disponibles
    public override IList  ListAvailable (contenu IContent, bool contentFolder, utilisateur IPrincipal)
    {
        var baseList = base.ListAvailable (contenu, contentFolder, utilisateur);
        return Filter (baseList, contenu) .ToList ();
    }

    // pour filtrer, regardez simplement chaque type de modèle renvoyé et vérifiez s'il a l'attribut RestrictTo
    // s'il possède l'attribut, assurez-vous que SiteDefinition.Current est contenu dans la liste des
    // sites Web autorisés. Si tel est le cas, autorisez le retour du modèle, sinon
    protected virtual IEnumerable  Filter (IList  contentTypes, IContent content)
    {
        var siteDefinition = content! = null
            ? _siteDefinitionResolver.GetByContent (content.ContentLink, false, false)
            : SiteDefinition.Current;


        foreach (var targetType dans contentTypes)
        {
            if (siteDefinition == null)
                yield return targetType;

            var modelType = targetType.ModelType;

            if (modelType! = null)
            {
                // tentative de récupérer une instance de RestrictTo du modèle
                var attributeVal = (RestrictToAttribute) Attribute.GetCustomAttribute (modelType, typeof (RestrictToAttribute));

                if (attributeVal! = null)
                {
                    var currentSite = siteDefinition.Name;

                    // comparer le nom du contexte du site actuel à la liste des sites dans l'attribut
                    if (attributVal.Sites.Any (x =>
                        x.Equals (currentSite, StringComparison.InvariantCultureIgnoreCase)))
                    {
                        yield return targetType;
                    }
                }
                autre
                {
                    yield return targetType;
                }
            }
            autre
            {
                yield return targetType;
            }
        }
    }
} 

Encore une fois, faites-moi savoir s'il y a une meilleure façon de gérer cela – mais pour l'instant, c'est la meilleure solution que je puisse trouver au problème. Amusez-vous bien!

À propos de l'auteur <! -: dmccurry, Architecte de solutions ->

Je suis un développeur Sitecore certifié, un singe de code et un nerd général. J'ai sauté dans l'espace .NET il y a 10 ans pour travailler sur des applications de classe entreprise et je n'ai jamais regardé en arrière. J'adore créer des choses, des Legos aux logiciels qui résolvent de vrais problèmes. Ai-je mentionné J'adore les jeux vidéo?

Plus de cet auteur






Source link