In Kentico 7, I'm trying to perform actions based on what alternative form is being submitted.
public partial class CMSModuleLoader
{
private class ObjectEventsAttribute : CMSLoaderAttribute
{
public override void Init()
{
ObjectEvents.Insert.Before += My_Create_Account_Page;
}
private void My_Create_Account_Page(object sender, ObjectEventArgs e)
{
if (e.Object is BizFormItem && e.Object != null)
{
BizFormItem formEntry = (BizFormItem)e.Object;
BizFormInfo form = formEntry.BizFormInfo;
if (form.FormName == "MyOpenAccount")
{
// somehow determine which alternative form this is
// do stuff with the fields in that alternative form
}
}
}
}
}
I've been up and down the documentation and found no solution. I could add a field that I would give a default value of the alt. form name, but that opens me up to editors deleting that field, and it still doesn't tell me what other fields are in the alternative form. Any other ideas?
I'm afraid that alternative form name is not accessible if you use ObjectEvents approach. But the information is known by the "Online form" (BizForm) webpart. So you probably have to customize it or create a copy. Bizform control has AlternativeFormFullName property and you can hook on one of its events like OnAfterSave.
Related
I would like to go through all fields in form data.
I know that in form I could do something like this :
// Go through all fields with IFormFieldVisitor
box.visitFields(new IFormFieldVisitor() {
#Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
if (field instanceof MyClass) {
...
}
return true;
}
}, 0);
but form data doesn't have this options. How to do this in form data.
You can obtain them using
AbstractFormData.getFields to obtain the top-level fields. If you need nested fields as well, have a look at the more complex AbstractFormData.getAllFieldsRec().
AbstractFormData.getAllProperties to obtain properties that you have defined by annotating the getters and setters with #FormData
That was the simple case.
Now, if you are using the Scout Extension mechanism to add new elements to an existing form (and it's formdata), you will have to take those contributions into account.
If you need to do this, you can refer to the source code of the AbstractForm.importFormData to see how Scout implements this.
I want to create input forms which validate user input and prevent the model from being saved with invalid data. I have been using databinding which works up to a point but my implementation is not as intuitive as I would like.
Imagine an input which contains '123' and the value must not be empty. The user deletes the characters one by one until empty. The databinding validator shows an error decoration.
However, if the user saves the form and reloads it, then a '1' is displayed in the field - i.e. the last valid input. The databinding does not transmit the invalid value into the model.
I have a ChangeListener but this is called before the databinding so at that point the invalid state has not been detected.
I would like the error to be displayed in the UI but the model remains valid (this is already so). Also, for as long as the UI contains errors, it should not be possible to save the model.
/**
* Bind a text control to a property in the view model
**/
protected Binding bindText(DataBindingContext ctx, Control control,
Object viewModel, String property, IValidator validator)
{
IObservableValue value = WidgetProperties.text(SWT.Modify).observe(
control);
IObservableValue modelValue = BeanProperties.value(
viewModel.getClass(), property).observe(viewModel);
Binding binding = ctx.bindValue(value, modelValue, getStrategy(validator), null);
binding.getTarget().addChangeListener(listener);
ControlDecorationSupport.create(binding, SWT.TOP | SWT.LEFT);
return binding;
}
private UpdateValueStrategy getStrategy(IValidator validator)
{
if (validator == null)
return null;
UpdateValueStrategy strategy = new UpdateValueStrategy();
strategy.setBeforeSetValidator(validator);
return strategy;
}
private IChangeListener listener = new IChangeListener()
{
#Override
public void handleChange(ChangeEvent event)
{
// notify all form listeners that something has changed
}
};
/**
* Called by form owner to check if the form contains valid data e.g. before saving
**/
public boolean isValid()
{
System.out.println("isValid");
for (Object o : getDataContext().getValidationStatusProviders())
{
ValidationStatusProvider vsp = (ValidationStatusProvider) o;
IStatus status = (IStatus)vsp.getValidationStatus()
.getValue();
if (status.matches(IStatus.ERROR))
return false;
}
return true;
}
Your best bet is to steer clear of ChangeListeners - as you've discovered, their order of execution is either undefined or just not helpful in this case.
Instead, you want to stick with the 'observable' as opposed to 'listener' model for as long as possible. As already mentioned, create an AggregateValidationStatus to listen to the overall state of the DataBindingContext, which has a similar effect to your existing code.
Then you can either listen directly to that (as below) to affect the save ability, or you could even bind it to another bean.
IObservableValue statusValue = new AggregateValidationStatus(dbc, AggregateValidationStatus. MAX_SEVERITY);
statusValue.addListener(new IValueChangeListener() {
handleValueChange(ValueChangeEvent event) {
// change ability to save here...
}
});
You can use AggregateValidationStatus to observe the aggregate validation status:
IObservableValue value = new AggregateValidationStatus(bindContext.getBindings(),
AggregateValidationStatus.MAX_SEVERITY);
You can bind this to something which accepts an IStatus parameter and it will be called each time the validation status changes.
We need users to be able to save a draft of their form in MVC4.
We already implemented a "Save Draft" button which disables the
client side validation entirely and submits the form.
Now on the server side the plan is to ignore any form fields which
have validation errors. For example, if a zip code field has "abc"
then we'll ignore that field and save the rest. If any field are
required but not provided those will also be ignored.
We're trying to figure out an elegant way to do this in MVC4 via
IModelBinder.
public class CustomModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var result = base.BindModel(controllerContext, bindingContext);
if (!controllerContext.Controller.ViewData.ModelState.IsValid)
{
foreach( var state in controllerContext.Controller.ViewData.ModelState)
{
if( state.Value.Errors.Count > 0) {
var fieldName = state.Key;
// Clear original data field that has the error.
}
}
// Now redo the binding without the fields that presented a problem.
result = base.BindModel(controllerContext, bindingContext);
}
return result;
}
}
So the problem above is how to clear the specific field data so that it can re-run
the data binding and generate the model without the fields that cause validation errors?
I want 2 checkboxes for yes/no answers, and require that either one be ticked. Is there a built-in validator for a 'group' of checkboxes?
As far as I know there isn't.
But you can do something like this in your form. Manually inside your form class, extend the isValid method to check that at least checkbox_1 or checkbox_2 is ticket. And you should do what Adrian said use radio instead. But the principle is the same.
<?php
class App_Your_Form extends Zend_Form
{
public function init()
{
... Your stuff ...
}
public function isValid($data)
{
$isValid = parent::isValid($data);
if ($this->getValue('checkbox_1') != '1' && $this->getValue('checkbox_2') != '1') {
$this->getElement('checkbox_1')->setErrors(array('You have to set check at least chexbkox_1 or checkbox 2'));
$isValid = false;
}
return $isValid;
}
}
If you use the radio element you only need setRequired(true). Same actually works for the MultiCheckbox Zend element.
$element->setRequired(true);
I want to password protect a webpage in Wicket so the user may only access it if he/she has logged in.
I'd also like the page to show the login page, and then after logging in the original page the user was trying to get to.
How is this done with wicket? I've already created a login page and extended the session class.
The framework-supplied way is to provide an IAuthorizationStrategy instance for your application, e.g., by adding to your Application init() method:
init() {
...
getSecuritySettings().setAuthorizationStrategy(...)
}
A working example of Wickets authorization functionality is on Wicket Stuff here, which demonstrates some reasonably complex stuff. For really simple cases, have a look at the SimplePageAuthorizationStrategy. At a very basic level, this could be used like so (taken from the linked Javadoc):
SimplePageAuthorizationStrategy authorizationStrategy = new SimplePageAuthorizationStrategy(
MySecureWebPage.class, MySignInPage.class)
{
protected boolean isAuthorized()
{
// Authorize access based on user authentication in the session
return (((MySession)Session.get()).isSignedIn());
}
};
getSecuritySettings().setAuthorizationStrategy(authorizationStrategy);
Edit in response to comment
I think the best way forward, if you're just going to use something like SimplePageAuthorizationStrategy rather than that class itself. I did something like this to capture pages that are annotated with a custom annotation:
IAuthorizationStrategy authorizationStrategy = new AbstractPageAuthorizationStrategy()
{
protected boolean isPageAuthorized(java.lang.Class<Page.class> pageClass)
{
if (pageClass.getAnnotation(Protected.class) != null) {
return (((MySession)Session.get()).isSignedIn());
} else {
return true;
}
}
};
Then you'd need to register an IUnauthorizedComponentInstantiationListener similar to what is done in SimplePageAuthorizationStrategy (link is to the source code), which should be something like:
new IUnauthorizedComponentInstantiationListener()
{
public void onUnauthorizedInstantiation(final Component component)
{
if (component instanceof Page)
{
throw new RestartResponseAtInterceptPageException(MySignInPage.class);
}
else
{
throw new UnauthorizedInstantiationException(component.getClass());
}
}
});