2

I am using Plone 4.3 and I have a form.SchemaForm plone.directives have an interface that has a start field from IEventBasic and a validator:

from datetime import timedelta
from plone.directives import form
from plone.app.contenttypes.interfaces import IEvent
from z3c.form import validator
from zope.component import provideAdapter
from zope.interface import Invalid

class IMyObject(form.SchemaForm)
    my_field_a = schema.TextLine(title='a_field')
    ...

class MyObject(Item):
    implements(IMyObject, IEvent)

class EndMyObjectValidator(validator.SimpleFieldValidator):
    def validate(self,value):
        #code for checking if end field is within a certain range from start field
        if self.end > self.start + timedelta(days=6):
            raise Invalid('The end date is not within range of the start date's week')

validator.WidgetValueDiscriminators(EndMyObjectValidator, field=IEventBasic['end'])
provideAdapter(EndMyObjectValidator)

In my type file (my.object.myobject.xml under profiles/default/types), I place the behavior in the behaviors section.

<behaviors>
    <element value="plone.app.event.dx.behaviors.IEventBasic"/>
</behaviors>

The problem is it validates the end field in any Event object or any object that implements the IEventBasic interface/schema.

I thought maybe since the Plone documentation says that the parameters 'view' and 'context' of WidgetValueDiscriminators accept an interface, then I could do either:

validator.WidgetValidatorDiscriminators(EndMyObjectValidator, view=IMyObject, field=IEventBasic['end'])

or

validator.WidgetValidatorDiscriminators(EndMyObjectValidator, context=IMyObject,field=IEventBasic['end']

Unfortunately, none of those trigger at all. I guess I'm misunderstanding what the context and view parameters actually do. How can I make it so the validators are specifically for dealing with MyObject?

Source: http://docs.plone.org/develop/addons/schema-driven-forms/customising-form-behaviour/validation.html

For now I am doing:

...
from gpcl.container.my_container import MyContainer

...

class EndMyObjectValidator(validator.SimpleFieldValidator):

    def validate(self,value):
        if self.widgets.form.portal_type <> 'my.object.myobject':
            return

        ...

validator.WidgetValueDiscriminators(EndMyObjectValidator, field=IEventBasic['end'])
provideAdapter(EndMyObjectValidator)

Update 2: I removed my comment before because it was for an unrelated problem. I changed the way I was checking for the type.

Patrick Downey
  • 965
  • 8
  • 13

1 Answers1

3

Ok, register your own Add Form and Subclassing the Default Dexterity Add Form. More Information about Validating in action handlers and Custom Add Forms

in my task.py (Contenttype and so on...):

# -*- coding: utf-8 -*-
from zope.interface import implementer
from zope.interface import Invalid
from z3c.form import button
from z3c.form import validator
from z3c.form import util
from z3c.form.interfaces import ActionExecutionError
from z3c.form.interfaces import WidgetActionExecutionError
from plone.dexterity.content import Item
from plone.dexterity.browser import add
from viisionar.training.interfaces import ITask
from Products.statusmessages.interfaces import IStatusMessage
from my.addon import _

@implementer(ITask)
class Task(Item):
    pass

class AddForm(add.DefaultAddForm):

    portal_type = 'Task'

    def __init__(self, context, request, ti=None):
        super(AddForm, self).__init__(context, request, ti=None)

    @button.buttonAndHandler(_('Save'), name='save')
    def handleAdd(self, action):

        print "Handle Add"
        data, errors = self.extractData()

        if errors:
            self.status = self.formErrorsMessage
            return

        # Your Custom validation

        # Debug Do what yo want

        print data

        if error:
            """
            # Global Portal Message
            raise ActionExecutionError(Invalid(_(u"Please provide a valid end date")))

            # or

            # Error Message in Widget
            raise WidgetActionExecutionError('IEventBasic.end', Invalid(u"Please put the the right end date"))
            """
        # /Your Custom validation

        obj = self.createAndAdd(data)

        if obj is not None:
            # mark only as finished if we get the new object
            self._finishedAdd = True
            IStatusMessage(self.request).addStatusMessage(
                self.success_message, "info"
            )

class AddView(add.DefaultAddView):
    form = AddForm

in my configure.zcml i register the custom Add form

<adapter
    for="
        Products.CMFCore.interfaces.IFolderish
        zope.publisher.interfaces.browser.IDefaultBrowserLayer
        plone.dexterity.interfaces.IDexterityFTI"
    provides="zope.publisher.interfaces.browser.IBrowserPage"
    factory="my.addon.task.AddView"
    name="Task" />

<class class="my.addon.task.AddView">
    <require
        permission="cmf.AddPortalContent"
        interface="zope.publisher.interfaces.browser.IBrowserPage"/>
</class>

in my task.xml Definition:

<property name="factory">Task</property>
<property name="schema">my.addon.interfaces.ITask</property>
<property name="klass">my.addon.task.Task</property>
<property name="behaviors">
  <element value="plone.app.content.interfaces.INameFromTitle" />
  <element value="plone.app.event.dx.behaviors.IEventBasic"/>
</property>
1letter
  • 331
  • 2
  • 8
  • Unfortunately, nothing seems to trigger. I did a print statement and nothing printed. I registered it in the configure and called WidgetValidatorDiscriminators like you showed. Also, I did not mention it, but I'm using another validator for the Start field. When I try to add a validator for the start field based on what you showed in your response, I end up with a ConfigurationConflictError. – Patrick Downey Oct 31 '16 at 11:22
  • Please check what happen when you remove the context argument from the WidgetValidatorDiscriminators. – 1letter Nov 01 '16 at 15:42
  • I removed the context from validator.WidgetValidatorDiscriminators, but that doesn't have any effect. – Patrick Downey Nov 01 '16 at 17:13
  • Thank you for your responses. I might be misunderstanding your answer in the update, but it looks like: you make the validator, you register the validator adapter in configure.zcml (the alternative to provideAdapter), then call value.WidgetValidatorDiscriminators, and in your Validator, check to see if the view has the interface used in the form. Checking by the interface would be more appropriate, but this seems to be called for every object that uses Start brought in by IEventBasic. Perhaps its not possible after all to register for a specific content type because its from a behavior? – Patrick Downey Nov 03 '16 at 12:28
  • I have updated my Answer above. Please check this. It's another way, but it is a solution – 1letter Nov 03 '16 at 17:18
  • I was trying to avoid creating a custom Add Form because I'm dealing with prepOverlay and I'm relying on the validation before the submit is clicked (that relates to another question I asked unfortunately), but this would ideally give me more control. Anyhow, thanks. – Patrick Downey Nov 04 '16 at 17:49