Today I’d like to show you a new extension that allows to programmatically hide tabs and properties in edit mode. Using this feature, the editor won’t see the unused controls when working with content.
Hiding tabs
In my previous article I showed a plugin that allows to convert pages in edit mode. It can be useful for complex models. For simple class hierarchy we don’t have to use models inheritance, but instead one content type can contains properties from all classes.
For example, we have to implement BlogPostPage, that has two specific versions: Recipe (with additional Title and Body) and SponsoredBlogPost (with additional CustomerLogo and SiteUrl).
We can add three page types for each class or create one BlogPostPage that contains properties from BlogPost, Recipe and SponsoredBlogPost.
There will be also BlogPostType enum property that defines which type we are currently using.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public class BlogPostPage : SitePageData { [Display(GroupName = SystemTabNames.Content, Order = 310)] [CultureSpecific] public virtual XhtmlString MainBody { get; set; } [Display(Name = "Blog post type", GroupName = SystemTabNames.Content)] [SelectOne(SelectionFactoryType = typeof(BlogPostTypeSelectionFactory))] public virtual BlogPostType BlogPostType { get; set; } [Display(Name = "Recipe title", GroupName = Global.GroupNames.Content)] public virtual string RecipeTitle { get; set; } [Display(Name = "Recipe content", GroupName = Global.GroupNames.Content)] public virtual XhtmlString RecipeContent { get; set; } [Display(Name = "Sponsored content Customer URL", GroupName = Global.GroupNames.Content)] public virtual Url SponsoredContentUrl { get; set; } [Display(Name = "Sponsored content Customer Logo", GroupName = Global.GroupNames.Content)] [UIHint(UIHint.Image)] public virtual ContentReference SponsoredContentCustomerLogo { get; set; } } public enum BlogPostType { Standard, Recipe, Sponsored } |
Now all properties are display under the content tab. The Editor can be confused about which properties should be used.
In next step we could use separated tabs to show Recipe specific properties and SponsoredBlogPost specific properties.
But the best option would be to show the Recipe tab only when BlogPostType is set to “Recipe”. Using my plugin you can hide tabs based on custom condition. Conditions are set on the on the server, without adding single line of JavaScript. To hide or show the tab based on property value you can use ShowTabWhenPropertyEquals and HideTabWhenPropertyEquals attributes. For the example above, for BlogPostType it will look like:
1 2 3 4 5 |
[Display(Name = "Blog post type", GroupName = SystemTabNames.Content, Order = 90)] [SelectOne(SelectionFactoryType = typeof(BlogPostTypeSelectionFactory))] [ShowTabWhenPropertyEquals(Global.GroupNames.Recipe, (int)BlogPostType.Recipe)] [ShowTabWhenPropertyEquals(Global.GroupNames.Sponsored, (int)BlogPostType.Sponsored)] public virtual BlogPostType BlogPostType { get; set; } |
As you can see, ShowTabWhenPropertyEquals was used twice on a single property. The first attribute usage is to display Recipe tab and second to display Sponsored tab.
After running the site, tabs will be automatically displayed based on property value.
Hiding properties
The similar situation is with the properties. Sometimes the visibility of property depends on on value of another property. For example we can have “Show related articles” checkbox. When checkbox is unchecked, the Header and “Related articles” properties are not used in view mode and it would be good to hide them.
To hide property when value of another property changed use ShowPropertyWhenValueEquals or HidePropertyWhenValueEquals attribute.
1 2 3 4 5 6 7 8 9 10 |
[Display(Name = "Show related articles", GroupName = "Related articles", Order = 1)] [ShowPropertyWhenValueEquals(nameof(RelatedArticlesHeader), true)] [ShowPropertyWhenValueEquals(nameof(RelatedArticles), true)] public virtual bool ShowRelatedArticles { get; set; } [Display(Name = "Header", GroupName = "Related articles", Order = 2)] public virtual string RelatedArticlesHeader { get; set; } [Display(Name = "Related articles", GroupName = "Related articles", Order = 3)] public virtual ContentArea RelatedArticles { get; set; } |
You can see that ShowPropertyWhenValueEquals was used twice on one property, first time to control the visibility of RelatedArticlesHeader and second for RelatedArticles.
From now Textbox and ContentArea will be displayed only when checkbox is checked.
More advanced scenario
The extension handles also more complex conditions when hiding tabs and properties. For example, to show a tab we have to check value of more than one property. To implement this we should use ILayoutVisibilityResolver.
We need to implement two methods:
Of course the resolver can be used together with attribute based conditions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
using System.Collections.Generic; using System.Linq; using Alloy.HideTabs.LayoutVisibilityResolver; using AlloyTemplates.Models.Pages; using EPiServer; using EPiServer.Core; using EPiServer.ServiceLocation; namespace AlloyTemplates.Business { [ServiceConfiguration(typeof(ILayoutVisibilityResolver))] public class DefaultLayoutVisibilityResolver: ILayoutVisibilityResolver { public IEnumerable<string> GetHiddenTabs(IContent content) { var originalType = content.GetOriginalType(); if (originalType != typeof(BlogPostPage)) { return Enumerable.Empty<string>(); } var blogPostPage = (BlogPostPage)content; List<string> hiddenTabs = new List<string>(); if (blogPostPage.Text1 != "show" || blogPostPage.Text2 != "secret" || blogPostPage.Text3 != "tab") { hiddenTabs.Add("Secret"); } return hiddenTabs; } public IEnumerable<string> GetHiddenProperties(IContent content) { return Enumerable.Empty<string>(); } } } |
The “Secret” will be displayed only when 3 textboxes have specific values.
The full source code is available on github. I also created a nuget package.