Injecting field errors in z3c.form form action handler - plone

I have a z3c.form where some errors cannot be known before a form action is executed. I'd like to show these errors on the fields, instead of in a global form status message. How any can construct and inject error to widgets in Form.update()?
Example:
#z3c.form.button.buttonAndHandler(_('Make Report'), name='report')
def report(self, action):
data, errors = self.extractData()
if errors:
self.status = "Please correct errors"
return
# Create sample item which we can consume in the page template
try:
self.output = make_report(self.context, self.request, data, filters=filters)
except zope.interface.Invalid as e:
self.status = e.message
self.errors = True
# How to target the error message to a particular field here
return
self.status = _(u"Report complete")

In formlib I solved this task with the set_invariant_error method you can find in this form:
- https://github.com/PloneGov-IT/rg.prenotazioni/blob/f6008c9bc4bac4baa61045c896ebd3312a51600d/rg/prenotazioni/browser/prenotazione_add.py#L305
I think it is reusable with little pain for z3c.form

Within a form action you can raise a WidgetActionExecutionError, passing the field name and an Invalid exception with the message you want to display. z3c.form will then take care of attaching the error to the right widget and rendering it so you don't have to do all the steps yourself.
For your code this would look like this:
from z3c.form.interfaces import WidgetActionExecutionError
#z3c.form.button.buttonAndHandler(_('Make Report'), name='report')
def report(self, action):
data, errors = self.extractData()
if errors:
self.status = "Please correct errors"
return
# Create sample item which we can consume in the page template
try:
self.output = make_report(self.context, self.request, data, filters=filters)
except zope.interface.Invalid as e:
raise WidgetActionExecutionError('target_field_name', e)
self.status = _(u"Report complete")
See http://developer.plone.org/reference_manuals/active/schema-driven-forms/customising-form-behaviour/validation.html#validating-in-action-handlers for another example.

Related

How to properly register an observer callback with ipywidgets FileUpload?

I'm registering a callback function via observe on the FileUpload widget to all events as I'm not providing a filter as 2nd argument to the observe method. This means, after an upload report_change is called multiple times with traitlets containing different objects of type list and dict. As can be seen from the code below I need to check for type and contents of the dict to make sure it is the correct one.
What is the proper way to register a callback handler to be called only when a file upload was successful and only with the actually uploaded object (which is a dict containing also some metadata)
import ipywidgets as widgets
uploader = widgets.FileUpload(
accept='.png',
multiple=False
)
image = widgets.Image()
def report_change(change):
new_value = change.new
if type(new_value) is dict and len(new_value) > 0:
first_key = next(iter(new_value))
if '.png' in first_key:
image_data = new_value[first_key]['content']
image.value = image_data
return
uploader.observe(report_change)
display(uploader)
display(image)

What is gluon Alert generic type?

I went through some documentations here and here and found that gluon dialogs Can* have a generic type which will be the generic type of the object to be returned when you call showAndWait(). But gluon alerts (com.gluonhq.charm.glisten.control.Alert which is a subclass of com.gluonhq.charm.glisten.control.Dialog) does not seem to have a generic type and does not also seem to allow you to give it a generic type.
The problem occured when I tried to call setOnHidden as:
boolean shown;
String report = "";
Alert al = new Alert(AlertType.ERROR);
al.setContentText(report);
al.setAutoHide(false);
al.setOnHidden(e->{
shown = false;
});
shown = true;
al.showAndWait();
and I got the following warning on the setOnHidden() Call:
The method setOnHidden(EventHandler) belongs to the raw type Dialog. References to generic type Dialog should be parameterized
Any clarifications about gluon dialogs or ways to get rid of the warning are most welcome.
Like in the built-in JavaFX Alert control, the implicit type of the Gluon's Alert control is the same: the JavaFX built-in ButtonType, so if you click the OK button, it will return ButtonType.OK.
As you can see at the Alert JavaDoc, the control has one or two default buttons: an OK button for all of them, and a Cancel button for the Confirmation alert. Each of these buttons has as default result ButtonType.OK and ButtonType.CANCEL.
So this works for both Alert controls:
alert.showAndWait().ifPresent(result -> {
if (result == ButtonType.OK) {
// do something;
}
});
One of the things you will notice with both OK and Cancel buttons: the alert will be dismissed, so you don't have to do it.
You can also provide your custom buttons. Then you'll need to take care of calling hide():
final Button myYesButton = new Button("Yes");
myYesButton.setOnAction(event -> {
alert.setResult(ButtonType.YES);
alert.hide();
});
alert.getButtons().add(myYesButton);
About the setOnHidden, see Javadoc. It requires a LifecycleEvent:
alert.setOnHidden((LifecycleEvent event) -> System.out.println("alert hidden"));
but you can use just:
alert.setOnHidden(event -> System.out.println("alert hidden"));
Finally, make sure you are importing the right control:
import com.gluonhq.charm.glisten.control.Alert;
...
Alert alert = new Alert(javafx.scene.control.Alert.AlertType.ERROR);

plone.directives form - passing variables with a redirect

I have a plone form that basically gets search terms, performs a search, and then directs the user to another form. For this second form, I need to pass a couple variables.
class MySearch(form.SchemaForm):
grok.context(IMyContext)
grok.name('my-search')
ignoreContext = True
schema = ISearchSchema
#button.buttonAndHandler(_(u'Search Method'))
def searchMethod(self, action):
""" group update/removal """
data, errors = self.extractData()
if errors:
self.status = self.formErrorsMessage
return
results = somecall(data['term'])
if results:
self.request.set('myvar',results['myvar'])
self.request.response.redirect('##my-results')
else:
IStatusMessage(self.request).addStatusMessage(_(u"No results found"),"info")
return
This doesn't work - I guess a new request is generated so myvar is immediately lost. I could put this in a query string and include it in the redirect, but would prefer to send it as POST data if possible. I also tried something like
return getMultiAdapter((self.context,self.request),name='my-results')()
to see if I could use that as a starting point to passing in variables, but that just returns me to my-search.
The parameters set on the request object are not taken into account (nor should they) when issuing a redirect.
Append a query string to the redirection URL instead; urllib.urlencode() does the job admirably:
from urllib import urlencode
self.request.response.redirect('##my-results?' + urlencode({'myvar': results['myvar']}))
The .redirect() call returns a 303 See Other response with the URL you passed in the the method as the Location header; the browser then opens that new location, and will include any GET parameters you added to the URL.

Value passed with request.setAttribute() is not available by request.getParameter()

I give a string variable a value in the normal execution of the code ,but if an exception happen I will give it another value , the problem is that in catch block the value is still the same as i assign first .
Here is my code ,first I assign page value "addUser" inside try block and in catch I give it "ErrorPage" value , I send the value of page within http request to forword method and inside it i print the value of page.
I cause an error in the excution of the code an i want it to go through catch block , and it does , but when it send the page value to the forword function the value of page is "addUser" not "ErrorPage" although i assign it to "ErrorPage" !!
String page = "addUser";
try {
// ...
request.setAttribute("page", page);
forward(request, response);
} catch (SQLException e) {
page = "ErrorPage";
request.setAttribute("page", page);
forward(request, response);
}
and here is the forword function
String page = request.getParameter("page");
System.out.println("page is " + page); // each time it prints addUSer
Can someone help? and thanx in advance.
You're calling request.getParameter() instead of request.getAttribute() to obtain the value. Since you've set it as request attribute, you should also get it as request attribute.
So:
request.setAttribute("foo", foo);
is only available by
Object foo = request.getAttribute("foo"); // NOT getParameter().
The getParameter() is only for HTTP request parameters as you can specify in request URL or in input fields of HTML forms.
In addition to BalusC's point"
In your code you have declared two "String page" variable. This will not compile. I think the parameter that you are passing to the request in the catch must be the "other" page variable. But it is hard to tell since this is not a true example.

Workflow Foundation: How to create Receive activity with custom message in xaml designer?

I need to have Receive activity which can receive my custom data. I found examples, but all use coded workflows like such
public class ProcessRequest : Activity
{
public ProcessRequest()
{
Variable<MyData> request = new Variable<MyData> { Name = "request" };
Receive receiveRequest = new Receive
{
ServiceContractName = "IProcessRequest",
OperationName = "Foo",
CanCreateInstance = true,
Content = ReceiveContent.Create(new OutArgument<MyData>(request))
};
}
}
The main point is that Receive.Content property. It is not clear for me how I can do it in XAML designer. What I have to set in the dialog of the Content property - Message or Parameters and what to set inside those options?
Thanks for the light!
I got it :-) I have to use Message option inside the Content dialog. Then put the name of the variable (in my case request) into the field Message data and browse MyData type for the Message type field. Piece of cake ;-)

Resources