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