1# -*- test-case-name: nevow.test -*-
2# Copyright (c) 2004-2005 Divmod, Inc.
3# See LICENSE for details.
4
5from zope.interface import Attribute
6from zope.interface.interface import InterfaceClass, Interface
7
8class ITyped(Interface):
9    """Typeds correspond roughly to <input> tags in HTML, or
10    with a complex type, more than one <input> tag whose input
11    is processed and coerced as a unit.
12    """
13    def coerce(val, configurable):
14        """Coerce the input 'val' from a string into a value suitable
15        for the type described by the implementor. If coercion fails,
16        coerce should raise InputError with a suitable error message
17        to be shown to the user. 'configurable' is the configurable object
18        in whose context the coercion is taking place.
19
20        May return a Deferred.
21        """
22
23    label = Attribute("""The short label which will describe this
24        parameter/properties purpose to the user.""")
25
26    description = Attribute("""A long description which further describes the sort
27        of input the user is expected to provide.""")
28
29    default = Attribute("""A default value that may be used as an initial value in
30        the form.""")
31
32    complexType = Attribute("""Whether or not this Typed
33        is a "simple" type and the infrastructure should render label,
34        description, and error UI automatically, or this type is
35        "complex" in which case it will be required to render all UI
36        including UI which is normally common to all Typed UI.
37
38        This MAY BE DEPRECATED if a better implementation is
39        devised.
40        """)
41
42
43
44class IBinding(Interface):
45    """A Binding corresponds (very) roughly to a <form> tag in HTML.
46    A Binding is an object which associates a string name with a Typed
47    instance. At the most basic level, Binding instances represent named
48    python properties and methods.
49    """
50    def getArgs():
51        """Return a copy of this bindings Typed instances; if this binding is a
52        Property binding, it will be a list of one Typed istance; if this binding is a
53        MethodBinding, it will be a list of all the Typed instances describing
54        the method's arguments.
55
56        These copies are used during the duration of a form post (initial
57        rendering, form posting, error handling and error correction) to
58        store the user-entered values temporarily in the case of an error
59        in one or more input values.
60        """
61
62    def getViewName():
63        """Todo: remove?
64        """
65
66    def configure(boundTo, results):
67        """Configure the object "boundTo" in the manner appropriate
68        to this Binding; if this binding represents a property, set the
69        property; if this binding represents a method, call the method.
70        """
71
72    def coerce(val, configurable):
73        """TODO This is dumb. remove it and leave it on ITyped
74
75        Make the code that calls coerce call it on the typed directly
76        """
77
78    default = Attribute("""The default value for this binding.""")
79
80
81
82
83class IInputProcessor(Interface):
84    """handle a post for a given binding
85    """
86    def process(context, boundTo, data):
87        """do something to boundTo in response to some data
88
89        Raise a formless.InputError if there is a problem
90        """
91
92
93## Freeform interfaces
94
95class IConfigurableFactory(Interface):
96    """A configurable factory is used to find and/or create configurables.
97    A "Configurable" object is any object which either:
98     - Implements IConfigurable directly
99     - Implements a TypedInterface, thus providing enough information
100       about the types of objects needed to allow the user to change
101       the object as long as the input is validated
102    """
103    def locateConfigurable(context, name):
104        """Return the configurable that responds to the name.
105        """
106
107
108class IConfigurableKey(Interface):
109    """The key of the configurable which is being rendered
110    """
111
112
113class IFormDefaults(Interface):
114    """Default values for the current form
115    """
116    def setDefault(key, value, context=None):
117        """Sets the 'key' parameter to the default 'value'
118        """
119
120    def getDefault(key, context=None):
121        """Gets the default value from the parameter 'key'
122        """
123
124    def getAllDefaults(key):
125        """Gets the defaults dict for the 'key' autocallable
126
127        >>> class IMyForm(annotate.TypedInterface):
128        ...     def doSomething(name=annotate.String()):
129        ...         pass
130        ...     doSomething = annotate.autocallable(doSomething)
131        >>> class Page(rend.Page):
132        ...     implements(IMyForm)
133        ...     docFactory = loaders.stan(t.html[t.head[t.title['foo']],t.body[render_forms]])
134        ...
135        ...     def render_forms(self, ctx, data):
136        ...         defaults_dict = iformless.IFormDefaults(ctx).getAllDefaults('doSomething')
137        ...         defaults_dict['name'] = 'fooo'
138        ...         return webform.renderForms()
139        """
140
141    def clearAll():
142        """Clears all the default values
143        """
144
145class IFormErrors(Interface):
146    """An object which keeps track of which forms have which errors
147    """
148
149
150class IBindingRenderer(Interface):
151    """Something which can render a formless Binding.
152
153    A Binding represents an atomic form which can be
154    submitted to cause a named thing to occur;
155    a MethodBinding will cause a method to be called;
156    a PropertyBinding will cause a property to change.
157    """
158
159
160class IActionRenderer(Interface):
161    """An alternate rendering of a formless Binding which
162    is usually represented as a smaller, no-input-fields
163    toolbar-style button. Should call a MethodBinding.
164
165    An action is distinct from a MethodBinding in that
166    an action does not explicitly solicit input from the user,
167    but instead gathers all the information it needs to run
168    a method from implicit context, such as the render
169    context, or the current state of the selection.
170    """
171
172
173class ITypedRenderer(Interface):
174    """Something which can render a formless Typed.
175    Renders input fields in html which will gather information
176    from the user which will be passed to the Typed.coerce
177    method when the entire form is submitted.
178    """
179
180
181class IRedirectAfterPost(Interface):
182    """A marker interface which can be set to something which can be cast
183    to a string to indicate that the browser should be redirected to the
184    resultant url after posting the form successfully. This component can
185    be set by any form post validators, or by the configurable method which
186    is being automatically called if it has access to the request.
187
188    Set this using the following:
189
190    request.setComponent(IRedirectAfterPost, "http://example.com/")
191    """
192
193
194## Configurable interfaces
195
196class IAutomaticConfigurable(Interface):
197    """An object is said to implement IAutomaticConfigurable if
198    it implements any TypedInterfaces. When this object
199    is configured, discovering binding names, discovering bindings,
200    and posting forms along with calling methods in response to
201    form posts and setting properties in response to form posts.
202    """
203
204_notag = object()
205
206class _MetaConfigurable(InterfaceClass):
207    """XXX this is an unpleasant implementation detail; phase it out completely
208    as we move towards zope.interface.
209    """
210
211    def __call__(self, other, default=_notag):
212        """ XXX use TypedInterfaceConfigurable as a fallback if this interface doesn't
213        work for some reason
214        """
215
216        result = InterfaceClass.__call__(self, other, _notag)
217        if result is not _notag:
218            return result
219
220        from formless.annotate import TypedInterface
221        if TypedInterface.providedBy(other):
222            from formless.configurable import TypedInterfaceConfigurable
223            return TypedInterfaceConfigurable(other)
224        if default is _notag:
225            raise TypeError('Could not adapt', other, self)
226        return default
227
228_BaseMetaConfigurable = _MetaConfigurable('_BaseMetaConfigurable', (), {})
229
230class IConfigurable(_BaseMetaConfigurable):
231    """An adapter which implements TypedInterfaces for an object
232    of the type for which it is registered, provides properties
233    which will get and set properties of the adaptee, and methods
234    which will perform operations on the adaptee when called.
235
236    Web Specific Note: When you implement this interface, you should
237    subclass freeform.Configurable instead of implementing directly,
238    since it contains convenience functionality which eases implementing
239    IConfigurable.
240    """
241    def getBindingNames(context):
242        """Return a list of binding names which are the names of all
243        the forms which will be rendered for this object when this
244        object is configured.
245        """
246
247    def getBinding(context, name):
248        """Return a Binding instance which contains Typed instances
249        which describe how to render a form which will gather input
250        for the 'name' binding (either a property or a method)
251        """
252
253    def postForm(context, bindingName, arguments):
254        """Handle a form post which configures something about this
255        object.
256        """
257
258    postLocation = Attribute("""The location of this object in the
259    URL. Forms described by bindings returned from getBindingNames
260    will be posted to postLocation + '/freeform_post!' + bindingName
261    """)
262
263
264
265
266
267## Under consideration for deprecation
268
269
270class IActionableType(Interface):
271    """A type which can have action methods associated with it.
272    Currently only List. Probably can be extended to more things.
273    """
274    def attachActionBindings(possibleActions):
275        """Attach some MethodBinding instances if they are actions
276        for this ActionableType.
277        """
278
279    def getActionBindings():
280        """Return a list of the MethodBinding instances which represent
281        actions which may be taken on this ActionableType.
282        """
283