Replacing Zone Content

Topics: Writing modules
Apr 4, 2011 at 6:33 AM
Edited Apr 4, 2011 at 6:34 AM

Hello. Below is an outline of what I'd like to accomplish. Advice please!

I need to be able to replace default content defined at the page level with content activated by certain conditions defined elsewhere.

My thinking was that I could achieve this using widgets and zones and a custom content part. I would create widget layers with certain conditions (custom IRuleProviders that I will write). However, this only supplies the overridden content. To supply the default content for a given page, I would define a ContentPart that has a field specifying what Zone it should be inserted into. So by default, no widget layer will insert any content into the specified zone and the page's data (for the custom ContentPart) will show up in that Zone instead. Then, given certain conditions that enable  a widget layer, the page's content for that zone will get replaced with that layer's widget content. 

How can one replace the existing content for a zone rather than appending content to it, and insure that the replaced content will be the content that shows up when the page is rendered? I need to insure that I can always override/replace any existing content for a specific zone (or rather, take the item with the highest priority defined by me and ignore the rest).

Am I on the right track, or is there a better way to accomplish this? I assume the replacing of zone content isn't too complex, and I'm simply too new with Orchard to know what to do.

Advice is appreciated!

Apr 4, 2011 at 9:07 AM

There was another thread discussing something similar.

Basically layers can't override widgets, they just add more on.

The way to think about it is simply that a widget needs a condition to display; but there are no filters to undisplay a widget. So you just need to think about how to compose your conditions to acheive what you're after.

For instance if you have a condition where widget A shows until a user has logged in when they get widget B, then you need to use the Anonymous layer rule for widget A, and the Authenticated rule for widget B.

If you give specific examples of conditions you're having trouble setting up then maybe I can suggest solutions. But basically you just need to write your own layer rules; one to test for your widget condition, one to test for the opposite.


Apr 4, 2011 at 9:31 AM

Thanks for the reply. That's rather discouraging news. I'm creating my own layer filter (an IRuleProvider) that operates on a lot more criteria than the two that come out of the box, many of which are dynamic. There is no way to insure that all the other layers (of which there could be many) have negating conditions. Further, I have the default content to worry about, which is different per page for a zone. Is there anyway to extend the default widget layout behavior to _not_ be compositional in nature? Otherwise I will have to roll my own widget-like layering system, which feels like i'm reinventing the wheel.

Apr 4, 2011 at 10:22 AM

The thing is, the compositional functionality is very elegant in its simplicity and allows for all kinds of situations.

Being able to do overriding like you want is very complicated. You're talking about a hierarchy of overriding widgets, to do such a thing effectively it would need to have a lot of flexibility and configuration options.

In your case I think you might need to design your IRuleProvider so it can control both positive and negative situations.

So your rule is formatted along these lines:

rule('parameter')  - positive rule

rule('!parameter') - negative rule

This way you can supervise all the logic in one rule class, and just test for +ve or -ve conditions for whether to apply each layer.

However it sounds like what you're trying to do might be even more complicated than this and maybe the Layers system isn't right for what you're doing.

Maybe instead you should be looking at a designing a Widget that always shows, and will on any given page simply select the appropriate content for whatever logic you have.

Without any further detail on the exact specification you're trying to implement, I'm only able offer general advice here :)

Apr 4, 2011 at 10:55 AM

Thanks for the reply. Considering the vagueness of my post, outstanding advice. You're right that the compositional functionality is elegant. I was hoping I could just plug in somewhere to override default behavior. However, I gave it some more thought. In my case, it's suitable for me to "group" layers at the rule level (e.g. layer1: url "~/about" and custom_rule "group1"; layer2: url "~/about" and custom_rule "group1"; two layers with the same rules), but change which layer is active within a group elsewhere based on additional criteria. The "custom_rule" rule will just check to see if it is the active layer in the layer group passed in, otherwise evaluate to false, which will cause the layer not to be shown. I was thinking to keep it all within the layer rules engine initially, but this will work. Thanks again for the advice.

Apr 4, 2011 at 7:19 PM

I may be missing something but it seems to me like you just need to not use the default layer(s) or modify their definition to include some negative condition of your choice.

Apr 5, 2011 at 11:03 AM
Edited Apr 5, 2011 at 11:04 AM

The issue is that the visibility of layers is determined at runtime based on dynamic conditions. However, I at most only want to show one layer from a set of certain layers at a time. My solution is to include a custom rule on the end of all the layer rules and have that rule evaluate to false for all the layers except the layer I currently want to show (for a specific layer group); the "active layer" is set elsewhere based on the dynamically changing conditions. (I hope this makes sense :) ). The only issue remaining is getting the name/id of the layer for the rule I am currently evaluating in IRuleProvider.Process() so I can check if it is the active layer or not; currently I'm passing it in as a parameter to my IRuleProvider, which is pretty gross. Is there a way to pull the current Widget Layer out of the work context, or get it from some other injected object?

Apr 5, 2011 at 12:50 PM

Probably not - the rules are simple yes/no operators and weren't intended to have any awareness of the layer they are operating on. I think passing it in as a parameter is probably the correct thing to do, even if it looks wrong :)

It's still hard for me to imagine your situation, I feel certain there must be a simpler way to achieve what you're doing, perhaps by actually having more layers to separate out the different features you are trying to control.

I'm working on a per-user layer module which would allow each user to have their own layer (only visible when they're logged in) which they can customise as they want. So then the Anonymous layer provides the default widgets and the template for a new user's layer. Perhaps that's more what you're after?

Apr 5, 2011 at 5:05 PM

Yes, I bet :) Let me try to explain with a hypothetical scenario:

We have a site. It has many different affiliate sites that link to it. There are thus several ways to arrive at the site, either via traversing a link from one of the affiliates, or directly, etc. Our site has a bunch of pages with many zones and widgets. We want users who come to our site normally (not from an affiliate) to see a "default" state. When users come from an affiliate, however, we want some zones' widgets to change to reflect information relevant to that affiliate; maybe we also want to modify our about page to make that affiliate's information more prominent or something -- the point mostly being that there are many pages, and many of the pages have their own specific widgets for certain zones, some of which might change depending on the affiliate from which the user came. So we are not looking at even one layer per affiliate. The affiliates also may not directly overlap (different zones may be relevant to different affiliates).

To achieve this I've created the concept of "layer groups" that only allow one layer in a certain group to contribute a widget to a given zone. A separate service controls which layer in a group is active. This works, but passing in the layer name in the layer rule to my custom RuleProvider seems a bit much. I wonder if this explains a little more what I want to do? Maybe there's a much better way to achieve something that can provide this sort of functionality? I did start using orchard less than a week ago... thanks for all the advice!

Apr 5, 2011 at 5:42 PM

Maybe layers are not the right metaphor here, as everything seems to be dynamic. After all, zones and widgets are just shapes and one can inject the latter into the former from code. The layer stuff is just the default way of doing that but if you're ready to take it over, you should be able to do exactly as you want.

Apr 5, 2011 at 5:43 PM
Edited Apr 5, 2011 at 5:44 PM

Something that I'm not sure if you're aware of is that a rule can hide a layer as well as show it. (By setting ruleContext.Result = false)

So you set up your various default layers with their "show" rules. Then as the final rule(s) on each have a set of "hide" rules that will respond to specific affiliates or affiliate conditions / options.

Finally have a set of affiliate layers with "show" rules according to how it should be set up for different affiliates (these would probably in all cases have the same set of "show" rules but  the inverses to the "hide" rules of the default layers). On these layers you include your replacement widgets.

Hope that makes sense ;)

This sounds to me a bit more straightforward than layer groups and overriding situations, each layer has a straight-up known and easily debuggable set of show/hide conditions.

The problem I can imagine creeping in with groups and overrides is that, particularly once you had loads of affiliates and therefore tons of layers and rules, you'd enter situations where you're thinking "why won't that layer display?" And it'll be really hard to untangle the set of conditions contributing to any given display state.

Maybe what I've described won't fit all the required permutations of your system. But I'd think you'd have trouble defining any system that can easily handle so many varying conditions and edge cases. But if what you've got works, stick with it :)

BTW, I haven't been using Orchard all that long either, I've just had plenty of time to get stuck in and it has a lot of similar concepts to a framework I was developing myself, so I've taken to it quite easily!

Edit: Just saw betrand's reply, and yes if layers aren't giving you what you need then just write your own system to push shapes into zones in whatever manner suits you.

Apr 6, 2011 at 9:28 AM
Edited Apr 6, 2011 at 9:39 AM

@randompete - I guess my explanation was pretty poor. Sorry. Using ruleContext.Result = false is actually how i'm implementing the layer groups idea. I tack on my own custom rule to all the layers that i want to be part of the group, doing: other_rules and layer_group_rule ("layer_name", "group_name"). This then only allows one layer from the same layer group to be active at once. Sorry if I didn't explain this clearly. 

Apr 6, 2011 at 9:38 AM
bertrandleroy wrote:

Maybe layers are not the right metaphor here, as everything seems to be dynamic. After all, zones and widgets are just shapes and one can inject the latter into the former from code. The layer stuff is just the default way of doing that but if you're ready to take it over, you should be able to do exactly as you want.

A very good idea. I took a look at Orchard.Widgets.Filters.WidgetFilter.cs to see what it was doing. I was thinking I could implement a simpler "priority" based widget idea (simpler than the layer groups idea I've been talking about), where the WidgetFilter would, instead of simply aggregating all the widgets from all the layers that matched, use the priority for a widget in a layer to determine which to show. This seems potentially like a very nice feature, and allows for easily overriding a "default" widget that appears everywhere on your site without trying to come up with appropriate rules.

I see this within WidgetFilter.OnResultExecuting(), which starts on line 30 (in release 1.0):


// Build and add shape to zone.
var zones = workContext.Layout.Zones;
foreach (var widgetPart in widgetParts) {
    if (activeLayerIds.Contains(widgetPart.As<ICommonPart>().Container.ContentItem.Id)) {
        var widgetShape = _contentManager.BuildDisplay(widgetPart);
        zones[widgetPart.Record.Zone].Add(widgetShape, widgetPart.Record.Position);


This could be changed to use the priority to swap out the active widget for a zone. If two widgets have the same priority, then they could be inserted in the old manner (same as code above), so that the current behavior would be preserved by default.

It looks like the only way to achieve this, short of modifying Orchard code directly (which I don't want to do), is to copy the entire Orchard.Widgets module, and use mine instead of the original? 

Though it might be best to depart from the layers entirely and do my own thing.