Conditional Validation on a BlockType/PageType - episerver

I have a Block Type which has these two properties.
[CultureSpecific]
[Display(
Name = "Display PDF Button", GroupName = TabNames.PDFCustomisation, Order = 0)]
public virtual bool DisplayPdfButton { get; set; }
[CultureSpecific]
[Required]
[Display(
Name = "Download Pdf Text", GroupName = TabNames.PDFCustomisation, Order = 1)]
public virtual string DownloadPdfText { get; set; }
I only want DownloadPdfText to be required if the user sets DisplayPdfButton to True. - Is this possible to do in Episerver?

Another way is to implement an event handler for custom validation, for example the SavingContent or PublishingContent events.
That way, you can look at the type of content being saved and/or published, and decide whether to validate or not.
The event handler argument has properties that can be set to stop saving/publishing, and also specify a reason (look at the CancelAction and CancelReason properties).
Here is an example of such an event handler.

One way of doing this is to implement your own validation attribute. You can make it validate to false if DisplayPdfButton is checked and DownloadPdfText is empty. Read about how to do it here: https://world.episerver.com/documentation/Items/Developers-Guide/Episerver-CMS/9/Content/Properties/Property-types/Writing-custom-attributes/

Related

Episerver- Client validation not working when using custom attribute in specialized property

I have a specialized property in episerver named Location with a below given structure:
public virtual string Name{ get; set; }
public virtual string City { get; set; }
[RegularExpression("^(\\+\\s?)?((?<!\\+.*)\\(\\+?\\d+([\\s\\-\\.]?\\d+)?\\)|\\d+)([\\s\\-\\.]?(\\(\\d+([\\s\\-\\.]?\\d+)?\\)|\\d+))*(\\s?(x|ext\\.?)\\s?\\d+)?$", ErrorMessage = "Invalid Phone Number")]
public virtual string PhoneNumber { get; set; }
The phone property is not a required one & the given regex handles it fine. With the regex, validation kicks in the next instant.
Issue
Now when i create a custom attribute with the same regex in it ,the validation only kicks in when the page which includes this specialized location property is published.
Could some one shed some light on why client validation kicks in with regex & not with custom attribute?
Regards.

Conditionally include/exclude a field from being searched

We have a really wide index that we used for site-wide search of multiple pieces of content. I'd like to add a new field that only admins can search -- normal users shouldn't be able to search on it.
Currently, it looks like the only way to blacklist a search field is to use SearchParamaters.SearchFields, but this would require listing every single other field, which is not ideal, as our index grows occasionally, and would require remembering to add to this list.
Alternatively, we could use reflection to build this list, and we may go this route if it's our only option. Was just hoping there was another option I was overlooking.
While this isn't ideal, I ended up creating [Searchable] and [AdminSearchable] attributes, and decorated these on all the properties of our search document class:
[Searchable]
public string UserName { get; set; }
[Searchable]
public string UserDisplay { get; set; }
[AdminSearchable]
public string UserEmail { get; set; }
And then used these static fields to build the list of searchable fields, based on whether or not the current user is an admin:
private static readonly string[] _PublicSearchFeilds =
typeof(SearchDocument).GetProperties()
.Where(p => System.Attribute.IsDefined(p, typeof(SearchableAttribute)))
.Select(p => p.Name.ToLowerCamelCase()).ToArray();
private static readonly string[] _AdminSearchFeilds =
typeof(SearchDocument).GetProperties()
.Where(p =>
System.Attribute.IsDefined(p, typeof(SearchableAttribute)) ||
System.Attribute.IsDefined(p, typeof(AdminSearchableAttribute)))
.Select(p => p.Name.ToLowerCamelCase()).ToArray();
And passed this into SearchParameters.SearchFields.
There is currently no way to specify a list of fields to exclude from searches. Feel free to add this as a feature request on User Voice to help us prioritize.

Create an attribute programatically in EPiServer

This may be a very simple question but I'm very new to EPiServer, so pls help.
I'm working on the EPiServer Relate demo site. I want to progrmatically create a new attribute on Episerver.Common.Security.IUser type. I have created attributes using CMS edit mode Admin options. But I want to know how to do this in code.
You may want to use CommunityAttributeBuilder (https://github.com/Geta/Community.EntityAttributeBuilder) that is similar to PageTypeBuilder for CMS. Currently it's supporting CMS6, I'll commit v7 as soon I will finish testing.
By decorating your class properties with special attribute you will find those created in target site.
For instance:
[CommunityEntity(TargetType = typeof(IUser))]
public class UserAttributes : IClubUserAttributes
{
[CommunityEntityMetadata]
public virtual int AccessType { get; set; }
[CommunityEntityMetadata]
public virtual string Code { get; set; }
[CommunityEntityMetadata]
public virtual int EmployeeKey { get; set; }
[CommunityEntityMetadata]
public virtual bool IsAdmin { get; set; }
}
Library will scan all assemblies and look for types decorated with CommunityEntity attribute, if found one then properties will be scanned and those decorated with CommunityEntityMetadata attribute will be automatically created in DB.
It also supports strongly-typed interface over IUser type:
var metadata = user.AsAttributeExtendable<UserAttributes>();
metadata.AccessType = info.AccessType;
metadata.Code = info.Code;
metadata.EmployeeKey = info.EmployeeKey;
metadata.IsAdmin = info.IsAdmin;
More info about library could be found - http://world.episerver.com/Blogs/Valdis-Iljuconoks/Dates/2012/6/Community-Attribute-Builder-final/
More info about internals (if interested) could be found here - http://www.tech-fellow.lv/2012/06/when-you-need-something-stronger/
You need to use the AttributeHandler class.
Joel has written a great guide with example code here

Passing multiple parameters to Prism's EventAggregator

I'm using Prism's EventAggregator for loosely coupled communication between my module's ViewModels. I have have several properties (e.g. FirstName, LastName) in ViewModelA which need to update properties in ViewModelB when their values change. My current solution involves:
ViewModelA publishes an Event with the new value for FirstName as the payload:
public string FirstName
{
get {return firstName;}
set
{
this.firstName = value;
eventAggregator.GetEvent<PatientDetailsEvent>().Publish(firstName);
}
}
ViewModelB is subscribed to the Event and changes its FirstName property accordingly:
public PatientBannerViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
eventAggregator.GetEvent<PatientDetailsEvent>().Subscribe(UpdateBanner, ThreadOption.UIThread);
}
public void UpdateBanner(string firstName)
{
this.FirstName = firstName;
}
This works fine for a single property. It doesn't work for multiple, different properties because ViewModelB has no idea what property has changed on ViewModelA . ViewModelB knows what the new value is, but it doesn't know which of its properties to update.
I could create separate Events for each property but this seems repetitive. It seems cleaner to just use one Event. Ideally, when publishing the Event, ViewModelA should tell ViewModelB which property has changed. How can I do this?
Sorry, I found the answer to my question in this post. This blog post by Rachel Lim is also helpful.
What we need is for ViewModelA (the publisher) to tell ViewModelB (the subscriber) two pieces of information:
What property has changed on ViewModelA
What is the new value of this property
We need to communicate 2 pieces of information (i.e. properties) but Prism's EventAggregator takes only one parameter, the payload. This is the problem.
To pass multiple pieces of information (properties) via the EventAggregator you can publish an instance of a class which defines these properties as the EventAggregator's payload. I called this class PatientDetailsEventParameters and it defines two properties:
public class PatientDetailsEventParameters
{
public string PatientProperty { get; set; }
public string Value { get; set; }
}
I created this class in an Infrastructure assembly (the same place where I define my Events) which all my other assemblies have a reference to.
You can then publish an instance of this class as the payload (instead of a string which holds only 1 value). This allows for multiple parameters to be passed into the payload.
public string FirstName
{
get
{
return firstName;
}
set
{
this.firstName = value;
eventAggregator.GetEvent<PatientDetailsEvent>().Publish(new PatientDetailsEventParameters() {Value = firstName, PatientProperty = "firstName"});
}
}
You can see here that a new instance of my PatientDetailsEventParameters is created when the PatientDetailsEvent is published. The two properties Value and PatientProperty are also set. PatientProperty is a string which tells ViewModelB (i.e. the subscriber) what property has changed. Value is the new value of the property that has changed.

In winforms what is the best (or a good) way to bind a form (view) to a strongly-typed object?

For example if I have a Name object
public class Name
{
public string First { get; set; }
public string Middle { get; set; }
public string Last { get; set; }
}
and I have a form with 3 textboxes on it, named txtFirstName, txtMiddleName, txtLastName
I want some way to automatically bind the domain object to these text boxes.
I'm very used to working with asp.net-mvc but I'm trying to transfer this knowledge to winforms 0_0
You want a "Data Source", specifically an "object data source".
This will get you started, from the "Data" menu, select "Add New Data Source..." You want to select "Object".
Data Source Configuration Wizard at
http://msdn.microsoft.com/en-us/library/w4dd7z6t(VS.80).aspx.
How to: Connect to Data in an Object at http://msdn.microsoft.com/en-us/library/5xf878ky.aspx.
Name n = new Name { First = "test", Last = "last", Middle = "midddle" };
textBox1.DataBindings.Add("Text", n, "First");
I'm not quite sure if this is what you are asking but, you can override the tostring method of your object
public override string ToString()
{
return string.Format("first:{0}, middle:{1} last:{2}", First, Middle, Last);
}
then you can set it as the datasource of a control.

Resources