The project in which I am currently working has an elaborate custom built system for allowing web editors to plan, review and discuss their articles in EPiServer‘s edit mode (as described by my collegue Stefan Forsberg in his post series about Reviewing pages in EPiServer). For this functionality to work, we are maintaining a number of hidden properties which are for instance linking together published pages with their corresponding review copies. A drawback with storing information in such hidden EPiServer fields is that while debugging, turning the Display in Edit Mode option on and off is quite vital. This resulting in any web editors currently working with your page types will gain access to new fields which are best left alone. I wrote a simple ControlAdapter showing hidden properties for users with certain specified roles; code available at GitHub.
ControlAdapter allowing access to EPiServer properties without Display in Edit Mode
The EPiServer user control responsible for adding visible properties to the EditPanel is EPiServer.UI.Edit.EditPageControl. By creating a ControlAdapter and overriding the OnInit method it is possible to edit the current page’s PropertyDataCollection before it is being used for setting up the form.
EditPageControlAdapter.cs
public class EditPageControlAdapter : ControlAdapter { private readonly List<string> _hiddenProperties = new List<string>(); private readonly string[] _allowedRoles = { "Administrators", "SuperUsers" }; protected override void OnInit(EventArgs e) { if (!_allowedRoles.Any(role => EPiServer.Security .PrincipalInfo.CurrentPrincipal.IsInRole(role))) { base.OnInit(e); return; } var editPageControl = (EditPageControl) Control; foreach (var propertyData in editPageControl .CurrentPage.Property) { if (propertyData.DisplayEditUI) continue; propertyData.DisplayEditUI = true; _hiddenProperties.Add(string.Format("{0} ({1})", propertyData.TranslateDisplayName(), propertyData.Name)); } base.OnInit(e); }
As the hidden properties should not be displayed to normal editors, there is need to filter on something (18-22). In this example I have just added a string array of valid user roles to the backend code (14), but if deployed to production it would probably be nice to have such settings elsewhere. If the current user is a member of a valid role, we can loop through the properties of the current page (25). The display/hide value of the PropertyData is stored in the DisplayEditUI property; setting this to true here will only affect the rendering, not alter the actual value belonging to the PageType.
In order to make the administrator aware that there are properties that they normally should not touch, we gathered some information about them while changing the DisplayEditUI value (30). In an override of the OnPreRender method we simply compile some sort of message, enclose it in a div-tag, adding it to the control collection. I added some inline styles to this sample for the warning to be more visible.
protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); if (!_hiddenProperties.Any()) return; var names = string.Join(", ", _hiddenProperties); var message = new HtmlGenericControl("div") { InnerText = string.Concat("The following properties are hidden for normal editors: ", names) }; message.Attributes.Add("style", "color:red;margin-top:5px;margin-left:5px;"); var editForm = Control.FindControl("EditForm") as PropertyDataForm; editForm.Controls.AddAt(0, message); } }
Of course, highlighting the normally hidden properties in some way would likely be even more helpful to have administrators think twice, but that would probably mean another ControlAdapter and a bit more work. If this is implemented in a production environment though, it may be a good idea; for now, making sure that only the right people have access to the hidden fields will suffice.
The output of this functionality is shown in the following image for a user belonging to either of the example’s roles Administrators or SuperUsers. As can be seen in the angry red warning text the hidden properties are Hidden1, Hidden2 and AlloyTech’s own ThirdBody.
For a normal web editor or others not sharing these elevated privileges the normal view is shown clicking on the Edit-tab.
Do not forget to add a simple mapping to the browser file in the App_Browsers directory of the solution for this to work. If you are curious about how this works, please see the previous post about Surviving IE, or: How to render different markup depending on devices and browsers.
App_Browsers\AdapterMappings.browser
<?xml version="1.0" encoding="utf-8"?> <browsers> <browser refID="Default"> <controlAdapters> <adapter controlType="EPiServer.UI.Edit.EditPageControl" adapterType="EPiServer.CodeSample.EditPageControlAdapter" /> </controlAdapters> </browser> </browsers>
Update: Separating the hidden EPiServer properties from the visible ones
I had another idea regarding making the hidden properties more visible while implementing this in my current project. We have about 25 hidden properties involved for certain pages due to the previously mentioned page review functionality, and that made a terrible mess on the Content-tab as well as in the red crit-wall-of-text up at the top. While this may work well for normal websites with only a few hidden fields, it simply would not do here.
Since I was not about to do another adapter, I simply added a new tab using PageTypeBuilder and had it require full access (15).
HiddenProperties.cs
public class HiddenProperties : Tab { public override string Name { get { return "Hidden properties (Warning, be careful!)"; } } public override AccessLevel RequiredAccess { get { return AccessLevel.FullAccess; } } public override int SortIndex { get { return 1000; } } }
Then I moved all of the hidden properties to the new tab (203); since the system does not care about tabs, this does not affect the functionality.
RandomPage.cs
[PageTypeProperty( EditCaption = "A hidden property", DisplayInEditMode = false, Tab = typeof(HiddenProperties), HelpText = "This property is not visible", SortOrder = 2020 )] public virtual DateTime? SomeHiddenProperty { get; set; }
I also removed the property names from the red warning block, and replaced them with a simple static warning letting the user know that they should be careful.