Package ldaptor :: Package apps :: Package webui :: Module add
[hide private]
[frames] | no frames]

Source Code for Module ldaptor.apps.webui.add

  1  from zope.interface import implements 
  2  from twisted.internet import defer 
  3  from twisted.python import plugin 
  4  from webut.skin import iskin 
  5   
  6  from ldaptor.protocols.ldap import ldapsyntax, distinguishedname 
  7  from ldaptor.protocols.ldap import fetchschema 
  8  from ldaptor import numberalloc, interfaces 
  9  from ldaptor.apps.webui import iwebui 
 10  from ldaptor.apps.webui.uriquote import uriQuote, uriUnquote 
 11  from ldaptor.apps.webui.i18n import _ 
 12  from ldaptor.apps.webui import i18n 
 13   
 14  import os 
 15  from nevow import rend, inevow, loaders, url, tags 
 16  from formless import annotate, webform, iformless, configurable 
 17   
18 -def mapNameToObjectClass(objectClasses, name):
19 name = name.upper() 20 objclass = None 21 for oc in objectClasses: 22 for ocName in oc.name: 23 if ocName.upper()==name: 24 objclass = oc 25 return objclass
26
27 -def mapNameToAttributeType(attributeTypes, name):
28 name = name.upper() 29 attrtype = None 30 for at in attributeTypes: 31 for atName in at.name: 32 if atName.upper()==name: 33 attrtype = at 34 return attrtype
35
36 -class UnknownAttributeType(Exception):
37 """LDAP Attribute type not known""" 38
39 - def __init__(self, name):
40 Exception.__init__(self) 41 self.name = name
42
43 - def __str__(self):
44 return self.__doc__ + ': ' + repr(self.name)
45
46 -class AddOCForm(configurable.Configurable):
47 - def __init__(self, objectClasses):
48 super(AddOCForm, self).__init__(None) 49 structural = [] 50 auxiliary = [] 51 for oc in objectClasses: 52 if oc.type == 'STRUCTURAL': 53 structural.append(oc) 54 elif oc.type == 'AUXILIARY': 55 auxiliary.append(oc) 56 structural.sort() 57 auxiliary.sort() 58 59 60 class KludgeNevowChoice(object): 61 """ 62 A kludge that allows using Choice with both Nevow 0.3 and 63 newer. 64 """ 65 def __init__(self, oc): 66 self.name = oc.name 67 self.desc = oc.desc
68 def __str__(self): 69 """ 70 For Choice in Nevow 0.4 and newer. Nevow 0.3 will use 71 integer indexes. Actual stringification for display 72 purposes happens in strObjectClass. 73 """ 74 return self.name[0]
75 76 formFields = [ 77 annotate.Argument('ctx', 78 annotate.Context()), 79 annotate.Argument('request', 80 annotate.Request()), 81 annotate.Argument('structuralObjectClass', 82 annotate.Choice(label=_('Object type to create'), 83 choices=[KludgeNevowChoice(x) for x in structural], 84 stringify=strObjectClass)), 85 ] 86 for oc in auxiliary: 87 formFields.append(annotate.Argument( 88 'auxiliary_%s' % oc.name[0], 89 annotate.Boolean(label=oc.name[0], 90 description=oc.desc or ''))) 91 self.formFields = formFields 92
93 - def getBindingNames(self, ctx):
94 return ['add']
95
96 - def bind_add(self, ctx):
97 return annotate.MethodBinding( 98 'add', 99 annotate.Method(arguments=self.formFields, 100 label=_('Add')), 101 action=_('Add'))
102
103 - def add(self, ctx, request, structuralObjectClass, **kw):
104 assert structuralObjectClass is not None 105 structuralObjectClass = str(structuralObjectClass) 106 auxiliaryObjectClasses = [] 107 for k,v in kw.items(): 108 assert k.startswith('auxiliary_') 109 if k.startswith('auxiliary_'): 110 k = k[len('auxiliary_'):] 111 if v: 112 auxiliaryObjectClasses.append(k) 113 u = url.URL.fromContext(ctx) 114 u = u.child('manual').child('+'.join([structuralObjectClass] 115 + auxiliaryObjectClasses)) 116 return u
117
118 -class AddForm(configurable.Configurable):
119 - def __init__(self, chosenObjectClasses, attributeTypes, objectClasses):
120 super(AddForm, self).__init__(None) 121 self.chosenObjectClasses=chosenObjectClasses 122 self.nonUserEditableAttributeType_objectClass=[ 123 oc.name[0] for oc in self.chosenObjectClasses] 124 self.attributeTypes=attributeTypes 125 self.objectClasses=objectClasses 126 self.formFields=self._getFormFields()
127
128 - def _nonUserEditableAttributeType_getFreeNumber(self, attributeType, context):
129 cfg = context.locate(interfaces.ILDAPConfig) 130 entry = context.locate(inevow.ISession).getLoggedInRoot().loggedIn 131 client = entry.client 132 o=ldapsyntax.LDAPEntry(client=client, 133 dn=cfg.getBaseDN()) 134 d=numberalloc.getFreeNumber(ldapObject=o, 135 numberType=attributeType, 136 min=1000) 137 d.addCallback(lambda x, a=attributeType: (a, [str(x)])) 138 return d
139 140 nonUserEditableAttributeType_uidNumber=_nonUserEditableAttributeType_getFreeNumber 141 nonUserEditableAttributeType_gidNumber=_nonUserEditableAttributeType_getFreeNumber 142
143 - def _get_attrtype(self, name):
144 for a in self.attributeTypes: 145 for cur in a.name: 146 if name.upper() == cur.upper(): 147 a.uiHint_multiline=0 #TODO 148 return a 149 raise UnknownAttributeType, name
150
151 - def _one_formfield(self, attr, result, must=False):
152 attrtype = self._get_attrtype(attr) 153 name=attr 154 if must: 155 name=name+"*" 156 if attrtype.uiHint_multiline: 157 if attrtype.single_value: 158 typed = annotate.Text(label=name, 159 description=attrtype.desc or '', 160 required=must) 161 else: 162 typed = annotate.Text(label=name, 163 description=attrtype.desc or '', 164 required=must) 165 else: 166 if attrtype.single_value: 167 typed = annotate.String(label=name, 168 description=attrtype.desc or '', 169 required=must) 170 else: 171 # TODO maybe use a string field+button to add entries, 172 # multiselection list+button to remove entries? 173 typed = annotate.Text(label=name, 174 description=attrtype.desc or '', 175 required=must) 176 177 result.append(annotate.Argument('add_'+attr, typed))
178
179 - def _getFormFields(self):
180 r=[] 181 r.append(annotate.Argument('context', 182 annotate.Context())) 183 184 process = {} 185 186 # TODO sort objectclasses somehow? 187 objectClasses = list(self.chosenObjectClasses) 188 objectClassesSeen = {} 189 190 self.nonUserEditableAttributes = [] 191 while objectClasses: 192 objectClass = objectClasses.pop() 193 objclassName = objectClass.name[0] 194 195 if objectClassesSeen.has_key(objclassName): 196 continue 197 objectClassesSeen[objclassName]=1 198 199 for ocName in objectClass.sup or []: 200 objclass = mapNameToObjectClass(self.objectClasses, ocName) 201 assert objclass, "Objectclass %s must have schema" %objclassName 202 objectClasses.append(objclass) 203 204 for attr_alias in objectClass.must: 205 real_attr = self._get_attrtype(str(attr_alias)) 206 207 if hasattr(self, 'nonUserEditableAttributeType_'+real_attr.name[0]): 208 self.nonUserEditableAttributes.append(real_attr.name[0]) 209 else: 210 for attr in real_attr.name: 211 if not process.has_key(attr.upper()): 212 process[attr.upper()]=0 213 if not process[attr.upper()]: 214 self._one_formfield(attr, result=r, must=True) 215 for name in real_attr.name: 216 process[name.upper()]=1 217 218 for attr_alias in objectClass.may: 219 real_attr = self._get_attrtype(str(attr_alias)) 220 221 if hasattr(self, 'nonUserEditableAttributeType_'+real_attr.name[0]): 222 self.nonUserEditableAttributes.append(real_attr.name[0]) 223 else: 224 for attr in real_attr.name: 225 if not process.has_key(attr.upper()): 226 process[attr.upper()]=0 227 if not process[attr.upper()]: 228 self._one_formfield(attr, result=r) 229 for name in real_attr.name: 230 process[name.upper()]=1 231 232 assert [v==1 for k,v in process.items()], "TODO: %s"%process 233 return r
234 235
236 - def getBindingNames(self, ctx):
237 return ['add']
238
239 - def bind_add(self, ctx):
240 return annotate.MethodBinding( 241 'add', 242 annotate.Method(arguments=self.formFields, 243 label=_('Add')), 244 action=_('Add'))
245
246 - def _textarea_to_list(self, t):
247 return filter(lambda x: x, [x.strip() for x in t.split("\n")])
248
249 - def _getDNAttr(self):
250 attr_alias = self.chosenObjectClasses[0].must[0] 251 attrType = mapNameToAttributeType(self.attributeTypes, attr_alias) 252 assert attrType is not None 253 dn_attribute = attrType.name[0] 254 return dn_attribute
255
256 - def add(self, context, **kw):
257 cfg = context.locate(interfaces.ILDAPConfig) 258 dnAttr = self._getDNAttr() 259 assert kw.has_key('add_'+dnAttr), 'Must have attribute dn %s points to.' % dnAttr 260 assert kw['add_'+dnAttr], 'Attribute %s must have value.' % 'add_'+dnAttr 261 # TODO ugly 262 rdn=distinguishedname.RelativeDistinguishedName( 263 attributeTypesAndValues=[ 264 distinguishedname.LDAPAttributeTypeAndValue(attributeType=dnAttr, 265 value=kw['add_'+dnAttr]), 266 ]) 267 268 #TODO verify 269 changes = [] 270 for k,v in kw.items(): 271 if hasattr(self, "nonUserEditableAttributeType_"+k): 272 raise "Can't set attribute %s when adding." % k 273 elif k[:len("add_")]=="add_": 274 if not v: 275 continue 276 attrtype = self._get_attrtype(k[len("add_"):]) 277 assert attrtype 278 279 if attrtype.single_value or attrtype.uiHint_multiline: 280 v=[v] 281 else: 282 v=self._textarea_to_list(v) 283 284 if v and [1 for x in v if x]: 285 attr=k[len("add_"):] 286 changes.append(defer.succeed((attr, v))) 287 #TODO 288 289 for attributeType in self.nonUserEditableAttributes: 290 thing=getattr(self, 'nonUserEditableAttributeType_'+attributeType) 291 if callable(thing): 292 changes.append(thing(attributeType, context)) 293 else: 294 changes.append(defer.succeed((attributeType, thing))) 295 296 dl=defer.DeferredList(changes, fireOnOneErrback=1) 297 #dl.addErrback(lambda x: x[0]) # throw away index 298 def _pruneSuccessFlags(l): 299 r=[] 300 for succeeded,result in l: 301 assert succeeded 302 r.append(result) 303 return r
304 305 dl.addCallback(_pruneSuccessFlags) 306 dl.addCallback(self._process2, context, rdn, kw) 307 return dl
308
309 - def _process2(self, changes, context, rdn, kw):
310 cfg = context.locate(interfaces.ILDAPConfig) 311 user = context.locate(inevow.ISession).getLoggedInRoot().loggedIn 312 313 if not changes: 314 return _("No changes!") #TODO 315 316 changes_desc="" 317 mod={} 318 for attr,new in changes: 319 if new: 320 if attr not in mod: 321 mod[attr]=[] 322 mod[attr].extend(new) 323 changes_desc=changes_desc+"<br>adding %s: %s"%(repr(attr), ', '.join(map(repr, new))) 324 325 if not mod: 326 return _("No changes (2)!") #TODO 327 328 e = ldapsyntax.LDAPEntryWithClient(client=user.client, 329 dn=iwebui.ICurrentDN(context)) 330 d = e.addChild(rdn, mod) 331 #d.addCallback(lambda e: "Added %s successfully." % e.dn) 332 d.addErrback(lambda reason: _("Failed: %s.") % reason.getErrorMessage()) 333 return d
334
335 -class ReallyAddPage(rend.Page):
336 implements(iskin.ISkinnable) 337 338 title = _('Ldaptor Add Page') 339 340 addSlash = True 341 342 docFactory = loaders.xmlfile( 343 'add-really.xhtml', 344 templateDir=os.path.split(os.path.abspath(__file__))[0]) 345
346 - def data_header(self, ctx, data):
347 u=url.URL.fromContext(ctx) 348 u=u.parentdir().parentdir().parentdir().clear() 349 l=[] 350 l.append(tags.a(href=u.sibling("search"))[_("Search")]) 351 l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) 352 353 return l
354
355 - def render_form(self, context, data):
356 return webform.renderForms()
357
358 - def render_passthrough(self, context, data):
359 return context.tag.clear()[data]
360
361 - def render_status(self, context, data):
362 try: 363 obj = context.locate(inevow.IHand) 364 except KeyError: 365 return context.tag.clear() 366 367 e = interfaces.ILDAPEntry(obj, None) 368 if e is None: 369 return context.tag.clear()[obj] 370 371 u=url.URL.fromContext(context) 372 u=u.parentdir().parentdir().parentdir().clear() 373 374 return context.tag.clear()[ 375 _("Added "), 376 tags.a(href=u.parentdir().child(e.dn).child("search"))[e.dn], 377 _(" successfully. "), 378 379 # TODO share implementation with entryLinks 380 '[', 381 tags.a(href=u.sibling('edit').child(uriQuote(e.dn)))[_('edit')], 382 '|', 383 tags.a(href=u.sibling('move').child(uriQuote(e.dn)))[_('move')], 384 '|', 385 tags.a(href=u.sibling('delete').child(uriQuote(e.dn)))[_('delete')], 386 '|', 387 tags.a(href=u.sibling('change_password').child(uriQuote(e.dn)))[_('change password')], 388 ']', 389 ]
390 391 render_i18n = i18n.render()
392
393 -class SmartObjectAddPage(ReallyAddPage):
394 - def __init__(self, smartObject):
395 super(SmartObjectAddPage, self).__init__() 396 self.smartObject = smartObject
397
398 - def configurable_(self, context):
399 return self.smartObject
400
401 - def render_overview(self, ctx, data):
402 return tags.invisible()
403
404 -class ManualAddPage(ReallyAddPage):
405 - def __init__(self, 406 structuralObjectClass, 407 auxiliaryObjectClasses, 408 attributeTypes, 409 objectClasses):
410 super(ManualAddPage, self).__init__() 411 self.structuralObjectClass = structuralObjectClass 412 self.auxiliaryObjectClasses = auxiliaryObjectClasses 413 self.attributeTypes = attributeTypes 414 self.objectClasses = objectClasses
415
416 - def configurable_(self, context):
417 a = AddForm(chosenObjectClasses=[self.structuralObjectClass] 418 + self.auxiliaryObjectClasses, 419 attributeTypes=self.attributeTypes, 420 objectClasses=self.objectClasses) 421 return a
422
423 - def render_overview(self, ctx, data):
424 if self.auxiliaryObjectClasses: 425 return ctx.tag.clear()[ 426 _('Using objectclasses %s and %s.') % ( 427 self.structuralObjectClass.name[0], 428 ', '.join([oc.name[0] for oc in self.auxiliaryObjectClasses]), 429 )] 430 else: 431 return ctx.tag.clear()[ 432 _('Using objectclass %s.') % ( 433 self.structuralObjectClass.name[0], 434 )]
435
436 -def strObjectClass(oc):
437 if oc.desc is not None: 438 return '%s: %s' % (oc.name[0], oc.desc) 439 else: 440 return '%s' % (oc.name[0],)
441
442 -class ChooseSmartObject(object):
443 - def __init__(self, pluginNames):
444 self.plugins = list(pluginNames) 445 self.plugins.sort()
446
447 - def getBindingNames(self, ctx):
448 return ['add']
449
450 - def bind_add(self, ctx):
451 return annotate.MethodBinding( 452 'add', 453 annotate.Method(arguments=[ 454 annotate.Argument('context', annotate.Context()), 455 annotate.Argument('smartObjectClass', annotate.Choice(choicesAttribute='plugins')), 456 ], 457 label=_('Add')), 458 action=_('Add'))
459
460 - def add(self, context, smartObjectClass):
461 request = context.locate(inevow.IRequest) 462 u = url.URL.fromContext(context) 463 return u.child('smart').child(smartObjectClass)
464
465 -class AddPage(rend.Page):
466 implements(iskin.ISkinnable) 467 468 title = _('Ldaptor Add Page') 469 470 addSlash = True 471 472 docFactory = loaders.xmlfile( 473 'add.xhtml', 474 templateDir=os.path.split(os.path.abspath(__file__))[0]) 475
476 - def __init__(self, attributeTypes, objectClasses):
477 super(AddPage, self).__init__() 478 self.attributeTypes = attributeTypes 479 self.objectClasses = objectClasses
480
481 - def listPlugins(self):
482 for plug in plugin.getPlugIns('ldaptor.apps.webui.smartObject'): 483 yield plug.name
484
485 - def havePlugins(self):
486 for plug in plugin.getPlugIns('ldaptor.apps.webui.smartObject'): 487 return True 488 return False
489
490 - def getPlugin(self, name):
491 for plug in plugin.getPlugIns('ldaptor.apps.webui.smartObject'): 492 if plug.name == name: 493 return plug 494 raise KeyError, name
495
496 - def data_header(self, ctx, data):
497 u=url.URL.fromContext(ctx) 498 u=u.parentdir().clear() 499 l=[] 500 l.append(tags.a(href=u.sibling("search"))[_("Search")]) 501 return l
502
503 - def configurable_objectClass(self, context):
504 return AddOCForm(self.objectClasses)
505
506 - def render_objectClassForm(self, context, data):
507 return webform.renderForms('objectClass')
508
509 - def configurable_smartObject(self, context):
510 return ChooseSmartObject(self.listPlugins())
511
512 - def render_smartObjectForm(self, context, data):
513 if self.havePlugins(): 514 return webform.renderForms('smartObject') 515 else: 516 return context.tag.clear()
517
518 - def render_passthrough(self, context, data):
519 return context.tag.clear()[data]
520
521 - def locateChild(self, request, segments):
522 ret = super(AddPage, self).locateChild(request, segments) 523 if ret != rend.NotFound: 524 return ret 525 526 if segments[0] == 'manual': 527 if not segments[1:]: 528 return rend.NotFound 529 path=segments[1] 530 unquoted=uriUnquote(path) 531 objectClasses = unquoted.split('+') 532 assert len(objectClasses) >= 1 533 534 structName=objectClasses[0] 535 structuralObjectClass = mapNameToObjectClass(self.objectClasses, 536 structName) 537 assert structuralObjectClass is not None, \ 538 "objectClass %s must have schema"%structName 539 540 auxiliaryObjectClasses = [] 541 for auxName in objectClasses[1:]: 542 oc = mapNameToObjectClass(self.objectClasses, auxName) 543 assert oc is not None, "objectClass %s must have schema"%oc 544 auxiliaryObjectClasses.append(oc) 545 r = ManualAddPage(structuralObjectClass=structuralObjectClass, 546 auxiliaryObjectClasses=auxiliaryObjectClasses, 547 attributeTypes=self.attributeTypes, 548 objectClasses=self.objectClasses) 549 return r, segments[2:] 550 elif segments[0] == 'smart': 551 if not segments[1:]: 552 return rend.NotFound 553 name = segments[1] 554 if not name: 555 return rend.NotFound 556 plug = self.getPlugin(name) 557 module = plug.load() 558 add = module.add() 559 r = SmartObjectAddPage(add) 560 return r, segments[2:] 561 else: 562 return rend.NotFound
563 564 render_i18n = i18n.render()
565
566 -def getResource(baseObject, request):
567 entry = request.getSession().getLoggedInRoot().loggedIn 568 client = entry.client 569 570 d = fetchschema.fetch(client, baseObject) 571 def cbAddPage(schema): 572 attributeTypes, objectClasses = schema 573 return AddPage(attributeTypes, objectClasses)
574 d.addCallback(cbAddPage) 575 return d 576