orchardcmsorchardcms-1.10

Is there a way to alter an Orchard content shape's display type before placement and rendering happens?


We are trying to implement a generic reusable paywall as a ContentPart in Orchard 1.10. The goal is to obscure certain content from non-paying users while keeping its detail page accessible as a teaser.

The original idea was to use a ShapeTableProvider to inspect every "Content" shape and change its display type according to the permissions of the user. Then it would be possible to customize what we want to show in placement on a per-content basis. e.g. only display the Layout_Summary instead of the full Layout if users don't have access to a news article.

Something along these lines:

builder.Describe("Content").OnDisplaying(displaying => {
    if(displaying.Shape.Metadata.DisplayType == "Detail"  && !_authorizer.Authorize(ViewPaidContent) {
        displaying.Shape.Metadata.DisplayType == "DetailPaywall";      
    }
});

--

<Placement>
    <Match DisplayType="DetailPaywall">
        <Place Parts_Layout="-"/>
        ...
    </Match>  
</Placement>

However, it turns out that OnDisplaying happens too late, placement has already been decided and ContentDrivers get invoked. OnCreated happens too early and would be overridden again. I believe the relevant code that triggers these is the BuildDisplay method in Orchard.ContentManagement.DefaultContentDisplay. Whichever displayType parameter is passed here is the one that ends up getting used.

Is there any way to influence display type in code based on some condition, or a different approach we can use to achieve similar functionality?


Solution

  • The display type is usually determined by the driver for the part. What you should probably do instead is introduce an alternate:

    builder.Describe("Content").OnDisplaying(displaying => {
        if(displaying.Shape.Metadata.DisplayType == "Detail"  && !_authorizer.Authorize(ViewPaidContent) {
            displaying.Shape.Metadata.Alternates.Add("Content__Paywall");      
        }
    });
    

    You can then add a Content-Paywall.cshtml template override to your theme and you should be done.