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

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.

Related

Filtering AutoQuery Results to Only Display Table Rows that Match Data in the Users Session

I'm working on a project that want's to control data access in a multi-tenant system. I've got a table set up which has a row on it that says what tenant the object applies to. Let's call this property
ClientObject.ClientOrgId
I want to set something up so that anytime this table is accessed the only results that are returned are results that match some piece of data in the users session. I.e.
ClientObject.ClientOrgId == UserSession.ClientOrgId
and I ideally want to do this restriction on the table model instead of re-implementing it for every query created.
I've found the Autofilter attribute in the service stack documentation, and it looks like the thing that I want to use, but I've been unable to get it working. An example of my code is below, and I'm not seeing any filtering whenever I set the user sessions ClientOrgID to anything different.
[Authenticate]
[Route("/clientObject", HttpMethods.Post)]
[Api("Creates a Client Object")]
public class CreateClientObject : ICreateDb<ClientObjectTableModel>, IReturn<ClientObjectMutationResponse>
{
[ValidateNotEmpty]
public string ClientName{ get; set; }
[ValidateNotEmpty]
public string ClientLocation { get; set; }
[ValidateNotEmpty]
[ValidateNotNull]
public Guid? ClientOrgId { get; set; }
}
[AutoFilter(QueryTerm.Ensure, nameof(ClientObjectTableModel.ClientOrgId), Eval= "userSession.ClientOrgId")]
public class ClientObjectTableModel : AuditBase
{
[AutoId]
public Guid Id { get; set; }
[Required]
public string ClientName { get; set; }
[Required]
public string ClientLocation { get; set; }
[Required]
public Guid ClientOrgId { get; set; }
}
I even went off the rails and tried something like
[AutoFilter(QueryTerm.Ensure, nameof(ClientObjectTableModel.ClientLocation), Value = "The Fourth Moon Of Mars")]
with the expectation that nothing would get returned, and yet I'm still seeing results.
All AutoQuery CRUD Attribute like [AutoFilter] should be applied to the AutoQuery Request DTO, not the data model.
Have a look at how to populate Tenant Ids with AutoPopulate and how it's later used to filter results with [AutoFilter].

Custom configuration properties - Entry has already been added

I'm developing a windows service that reads information from the app.config at start-up which should allow us to change internal thread configuration without redeploying the service.
I created some custom configuration sections and elements as follows (implementation omitted):
public class MyConfigurationSection
{
[ConfigurationProperty("threads")]
[ConfigurationCollection(typeof(MyThreadCollection), AddItemName="addThread")>
public MyThreadCollection threads { get; }
}
public class MyThreadCollection
{
protected override void CreateNewElement();
protected override object GetElementKey(ConfigurationElement element);
}
public class MyThreadElement
{
[ConfigurationProperty("active", DefaultValue=true, IsRequired=false)>
public bool active { get; set; }
[ConfigurationProperty("batchSize", DefaultValue=10, IsRequired=false)>
public int batchSize { get; set; }
[ConfigurationProperty("system", IsRequired=true)>
public string system { get; set; }
[ConfigurationProperty("department", IsRequired=true)>
public string department { get; set; }
[ConfigurationProperty("connection", IsRequired=true)>
public MyThreadConnectionElement connection { get; set; }
}
public class MyThreadConnectionElement
{
[ConfigurationProperty("server", IsRequired=true)>
public string server { get; set; }
[ConfigurationProperty("database", IsRequired=true)>
public string database { get; set; }
[ConfigurationProperty("timeout", DefaultValue=15, IsRequired=false)>
public int timeout { get; set; }
}
Then I add some elements to the app.config as follows:
<configurationSection>
<threads>
<addThread
active="True"
batchSize="50"
system="MySystem1"
department="Department1">
<connectionString
server="MyServer"
database="Database1" />
</addThread>
<addThread
active="True"
batchSize="30"
system="MySystem2"
department="Department2">
<connectionString
server="MyServer"
database="Database2" />
</addThread>
</threads>
</configurationSection>
Everything works - configuration is read, threads are created, and the processes run.
The problem is, I would like both these threads to have the same system name/value -- both should be MySystem -- but when I do that and run the program, I get a The entry 'MySystem' has already been added. exception.
I figured it might be because a property has to be explicitly configured to allow duplicates, but I don't know how and I couldn't find a property of the ConfigurationProperty class that might allow that, other than IsKey, but from its description it didn't seem like the answer, and trying it didn't solve the problem. Am I on the right track here?
Initially the system property was named name and I though that just maybe any property named name is treated as a unique identifier, so I changed it to system but it didn't change anything.
I tried the <clear /> tag as some other, similar posts suggested, without success.
Do I need to add another hierarchy to the configuration section -- Config -> Department -> Thread instead of Config -> Thread? I'd prefer to not take this approach.
Thanks for any and all input.
I actually found the problem and solution quite some time ago, but forgot to post the answer; thanks #tote for reminding me.
When implementing the ConfigurationElementCollection class, the GetElementKey(ConfigurationElement) method can be overridden. Without immediately realising what the method is for I overrode it and simply returned the system property value, and, since more than one configuration element had the same system name, technically they had the same key, which is why the error occurred.
The solution for me was to return the system and the department values as system.department which resulted in unique keys.

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

Best approach: Set/change password dialog

I have a usercontrol which is responsible for presenting creation and change of users.
The usercontrol is bound to an entity delivered by a RIA Service:
[MetadataType(typeof(User.UserMetadata))]
public partial class User
{
internal class UserMetadata
{
protected UserMetadata() {}
[Required]
public string Name { get; set; }
[Exclude]
public string PasswordHash { get; set; }
[Exclude]
public int PasswordSalt { get; set; }
[Required]
public string ShortName { get; set; }
[Include]
public IEnumerable<UserRole> UserRoles { get; set; }
}
[DataMember]
[RegularExpression("^.*[^a-zA-Z0-9].*$", ErrorMessageResourceName = "BadPasswordStrength", ErrorMessageResourceType = typeof(ErrorResources))]
[StringLength(25, MinimumLength = 6)]
public string NewPassword { get; set; }
}
When creating a new user, the field "NewPassword" is required - but when changing properties of an existing user, it is not (it is used for password-changes).
What is the best approach to solve this? I have several ideas, but they all feels a little bit crappy :-)
Thanks
It appears you are passing your current passwords back to the GUI. There is no need to ever do that. That would create a potential security hole
Suggest you treat password changing as a separate service call, not just a simple record editing exercise. RIA services supports Invoke Operations which are basically arbitrary direct-calls to your RIA service. The only restriction on Invoke operations is that they cannot return complex types (not a problem for this example).
Pass your current logged-in user identifier, the current password (encoded) and the new password (encoded) and do all the work server side. Return a simple success boolean value.
Just some suggestions, I am happy to see other people's ideas on this one :)

Coming from a relational database background, how should I model relationships in db4o (or any object database)?

I'm experimenting with db4o as a data store, so to get to grips with it I thought I'd build myself a simple issue tracking web application (in ASP.NET MVC). I've found db4o to be excellent in terms of rapid development, especially for small apps like this, and it also negates the need for an ORM.
However, having come from a SQL Server/MySQL background I'm a little unsure of how I should be structuring my objects when it comes to relationships (or perhaps I just don't properly understand the way object databases work).
Here's my simple example: I have just two model classes, Issue and Person.
public class Issue
{
public string ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime? SubmittedOn { get; set; }
public DateTime? ResolvedOn { get; set; }
public Person AssignedBy { get; set; }
public Person AssignedTo { get; set; }
}
public class Person
{
public string ID { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
The ID properties are just GUID strings generated by the .NET Guid.NewGuid() helper.
So here's how I initially thought the application would work; please ignore any security concerns etc and assume we already have a few Person objects stored in the database:
User logs in. Query the database for the Person which matches the username and password, and store his/her GUID id as a session variable. Redirect to app home screen.
Logged in user creates a new issue ticket, selecting the user to assign it to from a drop-down list. They fill in the other details (Title, Description etc), and then submit the form.
Query the Person objects in the database (by their GUID ID's) to get an object representing the logged in user and one representing the user the ticket has been assigned to. Create a new Person object (populated with the posted form data), assign the Person objects to the Issue object's AssignedBy and AssignedTo properties, and store it.
This would mean I have two Person objects stored against each Issue record. But what happens if I update the original Person—do all the stored references to that Person in the various issue objects update, or do I have to handle that manually? Are they references, or copies?
Would it be better/more efficient to just store a GUID string for the AssignedBy and AssignedTo fields (as below) and then look up the original person based on that each time?
public class Issue
{
public string ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime? SubmittedOn { get; set; }
public DateTime? ResolvedOn { get; set; }
public string AssignedByID { get; set; }
public string AssignedToID { get; set; }
}
I think I'm just stuck in a certain way of thinking which is confusing me. If someone could explain it clearly that would be most helpful!
Object-Databases try to provide the same semantics as objects in memory. The rule of thumb is: It works like objects in memory. Object databases store references between the objects in the database. When you update the object, that object is updates. And if you have a reference to that objects, you see the changed version.
In your case, the Issue-objects refer to the person object. When you update that person, all Issues which refer to it 'see' that update.
Of course, primitive types like int, strings, longs etc are handled like value objects and not a reference objects. Also arrays are handled like value objects in db4o, this means a array is stored together with the object and not as a reference. Everything else is stored as a reference, even collections like List or Dictionaries.
Please take a look at:
http://developer.db4o.com/Documentation/Reference/db4o-7.4/java/reference/html/reference/basic_concepts/database_models/object-relational_how_to.html
Best!

Resources