Hi Scott,
Thanks for the suggestion, will take this into consideration.
How much does the mark-up for different display options typically vary? Just different css classes? Or is the mark-up significantly different?
/Martin
Also, would the display options in your example need to vary both on the block and the placeholder? i.e. a small placeholder for promo spots in a sidebar would presumably only work with the narrow option, while the same block in a large placeholder would support both narrow and full.
/Martin
Thanks Martin,
The markup can sometimes in the views adapt such in the case of images where we server picture elements pushing through a dynamic image resizer but for the most part the markup itself is the same in the view. Where the markup differs is the wrapper classes that are added to the each blocks div element when going through our custom Content Area Renderer.
In our expereince the blocks don't have to different where they are put in as they area standardised in the UX and Design process for each supported size but certain blocks will never have certain sizes. For example we have full width hero images/videos that are only built to be used Full width and therefore should never have the option to display in Narrow or Wide. If we do ever need to vary anything by the container area its added to we would use Tags to render different Views or just adapt the blocks in our component SASS to where the block is added, we would only ever need the restriction of the sizes to be set at Block level, not contextually.
However to make it more Flexible maybe the option as passing the Layout options in to the PropertyFor would be nice if you needed to override them? However for our scenario is about the fact that every block is design as a component and can only support set sizes.
+1 for official support for this.
It is certainly possible to do right now, we have this functionality in our in-house site framework and have had since CMS 7.5.
If you know what display options you have for each model type, you can modify the display options menu by overriding the script "\modules\_protected\CMS\CMS.zip\[VERSION]\ClientResources\epi-cms\contentediting\command\SelectDisplayOption.js" and feed it with what display options are available by setting the variable this.popup.displayOptions in the _onModelChange function. We output the available display options for each modely type in a global js variable using the ClientResources framework in epi and reference that is the selectdisaplyoptions.js-script. Bit of a hack, but it works and is used in multiple live sites.
Thanks @Erik, yeah I saw an article a while ago for version 7.5 which had stopped working due to being on 9 at the time. Dispite having experience in a lot of other JS frameworks Dojo is something I've never quite got round to playing with or extending episerver other than via C#. Is there's any change you'd post a blog or some examples with something that works with 10/11? It would be great to see something fresh that would help get this working
I'm not sure about how episerver feels about me posting their source code, but ill try to only post our code and point to where it shoudl be located:
So first you would need to have some data to work with, we do this by outputting some global Js using the ClientResources framework, this is roughly how ours look:
/// <summary> /// Provides the client resources required by Encore, avoids having to modify module.config /// </summary> [ClientResourceProvider] public class ClientResourceProvider : IClientResourceProvider { private static EncoreConfigurationSection Configuration => EncoreConfigurationSection.Instance; /// <summary> /// This method is called when the client resources are collected /// </summary> /// <returns> /// A list of <see cref="T:EPiServer.Framework.Web.Resources.ClientResource" />s that this provider exposes. /// </returns> public IEnumerable<ClientResource> GetClientResources() { // this is the script you generate with display option restrictions yield return new ClientResource { Name = "epi-cms.widgets.base", Path = Paths.ToClientResource("", "encore/scripts/epi/displayoptionrestrictions.js"), ResourceType = ClientResourceType.Script }; // this will override the built-in episerver script file with our hacked version // there is possibly a better way, more "correct" way to do this, but this works for us yield return new ClientResource { Name = "epi-cms.widgets.base", Path = Paths.ToClientResource("", "encore/scripts/epi/selectdisplayoption.js"), ResourceType = ClientResourceType.Script }; } }
2. The data outputted by the disaplyoptionrestrictions.js looks like this, as you can see, there is both a list of content types with what tags they support, as well as a full list of all display options, the latter collection is for reference and retrieved by getting a service of the type EPiServer.Web.DisplayOptions and just json serializing all registered display options. How you want to populate the supported tags is up to you, but what you need to output is what tags a model supports (both bloks and pages works), the script will show all dispaly options if a model isnt present, so default is "show all".
//this is a list of what display options are supported by specific content types //only content types that are restricted will be present here var encoreSupportedTags = { "acme.core.models.pages.articlepage": ["full","half"], "__encore.alldisplayoptions": [{"id":"full","name":"Full","description":"","tag":"full","iconClass":"epi-icon__layout--full"},{"id":"twothirds","name":"Two thirds","description":"","tag":"twothirds","iconClass":"epi-icon__layout--two-thirds"},{"id":"half","name":"Halven","description":"","tag":"half","iconClass":"epi-icon__layout--half"},{"id":"onethird","name":"One third","description":"","tag":"onethird","iconClass":"epi-icon__layout--one-third"},{"id":"onequarter","name":"One quarter","description":"","tag":"onequarter","iconClass":"epi-icon__layout--one-quarter"}] };
3. Last, we need to modify our own version of the selectdisplayoptions.js script originally found in "\modules\_protected\CMS\CMS.zip\[VERSION]\ClientResources\epi-cms\contentediting\command\SelectDisplayOption.js". If you copy the uncompressed version from epi, you need to insert the following code around line 65, after the line "isAvailable = isAvailable && (this.model instanceof ContentBlockViewModel);":
// begin display option hack if (isAvailable && window.encoreSupportedTags != null) { if (window.encoreSupportedTags[this.model.typeIdentifier] != null) { var op = []; var i = options.length; while (i--) { if (window.encoreSupportedTags[this.model.typeIdentifier].indexOf(options[i].id) > -1) { op.push(options[i]); } } options = op; this.popup.displayOptions = op; } else if (window.encoreSupportedTags['__encore.alldisplayoptions'] != null) { var allOpts = []; for (var j = 0; j < window.encoreSupportedTags['__encore.alldisplayoptions'].length; j++) { allOpts.push(window.encoreSupportedTags['__encore.alldisplayoptions'][j]); } options = allOpts; this.popup.displayOptions = allOpts; } } // end display option hack
If everything works, your dispaly options menu should be limited for content types that has them, like below where the first block is an article page, and the second a regualr teaser without restrictions:
Fantastic thanks @Erik you're a legend. I'll take a look at this later :-) this should certainly help to get this sorted until it's offical :-)
Thanks again @Erik, I have took the base ideas of your code and hooked it in to a handler that generates all my restrictions through my existing custom attributes that I used on my project for restricting and controlling default sizes and allowed types and it's working like a charm. Legend!!
No not as yet but I've posted a full solution to this here https://world.episerver.com/blogs/scott-reed/dates/2018/4/controlling-episerver-display-options-via-a-custom-attribute/
+ there should be a option to "define" default display option (not the case when block is created and display option is not set, but block gets very specific display option). either when block is added to the specific area or when block is created in any area. both cases should be covered. I did it for my Bootstrap aware area renderer, but should be OOTB.
Yes, I also had a few comments on my blog post around some advance options so as far as I see it joining up what Valdis, myself and others have commented
I think it would also be good if these options could re-evaluate when any changes are made to layout instead of page loading. As if in our "layout rules service" we wanted to control the options related to sibligs or be aware of the context of where they are this would allow them to adapt as the page changes.
The top 2 highlighted are the most important to start I think, with the rest nice to haves.
This is what I wish would be OOTB, we have these in our custom solutions as both attributes on blocks and through naming convention for view files:
if somebody is going to implement this, some of the inspiration could be found also here :) - https://github.com/valdisiljuconoks/EPiBootstrapArea
Currently in the CMS we define the different display options at a application leve via an InitializationModule. This then appies these sizes to all blocks across the site.
Within the Alloy Demo and also my company's standard base project we have features to allow when previewing blocks and adding them to page to show a message if an unsupported sizes is used. However is is really poor usability when you have no way for a user to know a block doesn't support a size until they have selected it.
Can we make it so we continue to use an InitializationModule to set up the options but we then have an Attribute which you would set on a block such as.
Then the UI only displays the availible options per block in the CMS. It would also be have to have the DisplayOptions service updated to have methods to return supported sizes programatically.