1# Copyright (c) 2004 Divmod. 2# See LICENSE for details. 3 4from zope.interface import implements, providedBy 5 6from formless.iformless import IConfigurable, IActionableType, IBinding 7from formless.annotate import Argument, ElementBinding, GroupBinding, Object, TypedInterface 8 9from nevow import inevow 10from nevow.context import WovenContext 11 12class Configurable(object): 13 implements(IConfigurable) 14 15 bindingDict = None 16 17 def __init__(self, original): 18 self.original = original 19 self.boundTo = self 20 21 def getBindingNames(self, context): 22 ## Todo: remove this getattr 23 ifs = providedBy(getattr(self, 'boundTo', self)) 24 ifs = [ 25 x for x in ifs if x is not IConfigurable and x is not TypedInterface 26 ] 27 bindingNames = [] 28 self.bindingDict = bindingDict = {} 29 for interface in ifs: 30 ## TypedInterfaces have a __spec__ attribute which is a list of all Typed properties and 31 ## autocallable methods 32 for binding in getattr(interface, '__spec__', []): 33 bindingDict[binding.name] = binding 34 if binding.name not in bindingNames: 35 bindingNames.append(binding.name) 36 if IActionableType.providedBy(binding.typedValue): 37 acts = binding.typedValue.actions 38 if acts is None: 39 acts = [] 40 for action in acts: 41 bindingDict[action.name] = action 42 return bindingNames 43 44 def getDefault(self, forBinding): 45 ## TODO: Clean this up, it's a mess 46 if not isinstance(forBinding, Argument): 47 name = forBinding.name 48 if hasattr(self, name): 49 return getattr(self, name) 50 ## Todo: Only do this in ConfigurableAdapter instead of Configurable 51 if hasattr(self.boundTo, name): 52 return getattr(self.boundTo, name) 53 if self.original is not self.boundTo and hasattr(self.original, name): 54 return getattr(self.original, name) 55 return forBinding.default 56 57 def getBinding(self, context, name): 58 if self.bindingDict is None: 59 self.getBindingNames(context) 60 if self.bindingDict is None: 61 self.bindingDict = {} 62 binding = getattr(self, 'bind_%s' % name, getattr(self.boundTo, 'bind_%s' % name, None)) 63 if binding is not None: 64 binding = binding(context) 65 else: 66 try: 67 binding = self.bindingDict[name] 68 except KeyError: 69 raise RuntimeError, "%s is not an exposed binding on object %s." % (name, self.boundTo) 70 binding.boundTo = self.boundTo 71 return binding 72 73 def postForm(self, ctx, bindingName, args): 74 """Accept a form post to the given bindingName. The bindingName 75 can be dotted to indicate an attribute of this Configurable, eg 76 addresses.0.changeEmail. The post arguments are given in args. 77 Return a Resource which will be rendered in response. 78 """ 79 from formless import iformless 80 from nevow.tags import invisible 81 request = ctx.locate(inevow.IRequest) 82 pathSegs = bindingName.split('.') 83 configurable = self 84 85 cf = ctx.locate(iformless.IConfigurableFactory) 86 ## Get the first binding 87 firstSeg = pathSegs.pop(0) 88 binding = configurable.getBinding(ctx, firstSeg) 89 ctx.remember(binding, IBinding) 90 ctx.remember(configurable, IConfigurable) 91 ## I don't think this works right now, it needs to be fixed. 92 ## Most cases it won't be triggered, because we're just traversing a 93 ## single binding name 94 for seg in pathSegs: 95 assert 1 == 0, "Sorry, this doesn't work right now" 96 binding = configurable.getBinding(ctx, seg) 97 child = self.boundTo 98 if not isinstance(binding, GroupBinding): 99 accessor = inevow.IContainer(configurable.boundTo, None) 100 if accessor is None: 101 child = getattr(configurable.boundTo, binding.name) 102 else: 103 child = accessor.child(ctx, binding.name) 104 ## If it's a groupbinding, we don't do anything at all for this path segment 105 106 ## This won't work right now. We need to push the previous configurable 107 ## as the configurableFactory somehow and ask that for hte next binding 108 ## we also need to support deferreds coming back from locateConfigurable 109 assert 'black' is 'white', "Deferred support is pending" 110 configurable = cf.locateConfigurable(ctx, child) 111 ctx = WovenContext(ctx, invisible(key=seg)) 112 ctx.remember(binding, IBinding) 113 ctx.remember(configurable, IConfigurable) 114 115 bindingProcessor = iformless.IInputProcessor(binding) 116 rv = bindingProcessor.process(ctx, binding.boundTo, args) 117 ctx.remember(rv, inevow.IHand) 118 ctx.remember('%r success.' % bindingName, inevow.IStatusMessage) 119 return rv 120 121 def summary(self): 122 return "An instance of %s" % self.__class__.__name__ 123 124 postLocation = None 125 126class NotFoundConfigurable(Configurable): 127 def getBinding(self, context, name): 128 raise RuntimeError, self.original 129 130 131class TypedInterfaceConfigurable(Configurable): 132 def __init__(self, original): 133 self.original = original 134 self.boundTo = original 135 136 def summary(self): 137 return "An instance of %s" % self.original.__class__.__name__ 138 139 def __repr__(self): 140 return "TypedInterfaceConfigurable(%r)" % self.original 141 142 143class ListConfigurable(TypedInterfaceConfigurable): 144 def getBinding(self, context, name): 145 eb = ElementBinding(name, Object()) 146 eb.boundTo = self.original 147 return eb 148 149 150class GroupConfigurable(TypedInterfaceConfigurable): 151 def __init__(self, original, groupInterface): 152 TypedInterfaceConfigurable.__init__(self, original) 153 self.groupInterface = groupInterface 154 155 bindingDict = None 156 157 def getBindingNames(self, context): 158 bindingNames = [] 159 self.bindingDict = bindingDict = {} 160 interface = self.groupInterface 161 for binding in getattr(interface, '__spec__', []): 162 bindingDict[binding.name] = binding 163 if binding.name not in bindingNames: 164 bindingNames.append(binding.name) 165 if IActionableType.providedBy(binding.typedValue): 166 acts = binding.typedValue.actions 167 if acts is None: 168 acts = [] 169 for action in acts: 170 bindingDict[action.name] = action 171 return bindingNames 172 173 174