Home | Trees | Indices | Help |
|
---|
|
1 from zope.interface import implements 2 from webut.skin import iskin 3 from ldaptor.protocols.ldap import ldapsyntax 4 from ldaptor.protocols.ldap import fetchschema 5 from ldaptor.protocols.ldap import distinguishedname 6 from ldaptor.apps.webui.uriquote import uriQuote, uriUnquote 7 from ldaptor.apps.webui.i18n import _ 8 from ldaptor.apps.webui import i18n 9 10 import os 11 from nevow import rend, loaders, inevow, url, tags 12 from formless import iformless, configurable, annotate, webform 13 from twisted.internet import defer 14 from twisted.python import log 15 21 22 multiLineAttributeTypes = { 23 'description'.upper(): 1, 24 }26 for name in attributeType.name: 27 if multiLineAttributeTypes.has_key(name.upper()): 28 assert not attributeType.single_value 29 return multiLineAttributeTypes[name.upper()] 30 return 03133 nonEditableAttributes = { 34 'objectClass': 1, 35 } 36344 d.addCallback(_redirect, context, newDN) 345 346 for attr,old,new in changes: 347 if new: 348 if self.entry.has_key(attr): 349 self.entry[attr].update(new) 350 else: 351 self.entry[attr]=new 352 if old: 353 for x in old: 354 if x=='': 355 continue 356 self.entry[attr].remove(x) 357 if old or new: 358 l=tags.ul() 359 changes_desc[tags.li[_("changing %s") % attr], l] 360 if old: 361 l[tags.li[_("remove "), ', '.join(map(repr, old))]] 362 if new: 363 l[tags.li[_("add "), ', '.join(map(repr, new))]] 364 365 d.addCallback(lambda _: self.entry.commit()) 366 d.addCallback(lambda e: EditStatus(e, changes_desc)) 367 return d 36838 super(EditForm, self).__init__(None) 39 self.entry=entry 40 self.attributeTypes=attributeTypes 41 self.objectClasses=objectClasses42 4547 formFields=self._getFormFields() 48 return annotate.MethodBinding( 49 'edit', 50 annotate.Method(arguments=formFields, 51 label=_('Edit')), 52 action=_('Edit'))5355 if not self.nonEditableAttributes.get(attr): 56 attrtype = self._get_attrtype(attr) 57 if attrtype.uiHint_multiline: 58 if attrtype.single_value: 59 assert len(values)==1 60 for val in values: 61 result.append(annotate.Argument( 62 'edit_'+attr, 63 annotate.Text(label=attr, 64 description=attrtype.desc or '', 65 default=val, 66 required=required, 67 ))) 68 result.append(annotate.Argument( 69 'old_'+attr, 70 annotate.String(label=attr, 71 description=attrtype.desc or '', 72 default=val, 73 required=required, 74 hidden=True, 75 ))) 76 else: 77 assert len(values)==1 # TODO handle multivalued multiline attributetypes 78 for val in values: 79 result.append(annotate.Argument( 80 'edit_'+attr, 81 annotate.Text(label=attr, 82 description=attrtype.desc or '', 83 default=val, 84 required=required, 85 ))) 86 result.append(annotate.Argument( 87 'old_'+attr, 88 annotate.String(label=attr, 89 description=attrtype.desc or '', 90 default=val, 91 required=required, 92 hidden=True, 93 ))) 94 else: 95 if attrtype.single_value: 96 assert len(values)==1 97 for val in values: 98 result.append(annotate.Argument( 99 'edit_'+attr, 100 annotate.String(label=attr, 101 description=attrtype.desc or '', 102 default=val, 103 required=required, 104 ))) 105 result.append(annotate.Argument( 106 'old_'+attr, 107 annotate.String(label=attr, 108 description=attrtype.desc or '', 109 default=val, 110 required=required, 111 hidden=True, 112 ))) 113 else: 114 # TODO maybe use a string field+button to add entries, 115 # multiselection list+button to remove entries? 116 values=map(str, values) 117 result.append(annotate.Argument( 118 'edit_'+attr, 119 annotate.Text(label=attr, 120 description=attrtype.desc or '', 121 default="\n".join(values), 122 required=required, 123 ))) 124 if values: 125 result.append(annotate.Argument( 126 'old_'+attr, 127 annotate.String(label=attr, 128 description=attrtype.desc or '', 129 default="\n".join(values), 130 required=required, 131 hidden=True, 132 )))133135 r=[] 136 r.append(annotate.Argument('context', 137 annotate.Context())) 138 assert self.entry 139 140 process = {} 141 for k in self.entry.keys(): 142 process[k.upper()]=k 143 144 # TODO sort objectclasses somehow? 145 objectClasses = list(self.entry[process["OBJECTCLASS"]]) 146 objectClassesSeen = {} 147 while objectClasses: 148 objclassName = objectClasses.pop() 149 if objectClassesSeen.has_key(objclassName): 150 continue 151 objectClassesSeen[objclassName]=1 152 objclass = None 153 for o in self.objectClasses: 154 for name in o.name: 155 if objclassName.upper()==name.upper(): 156 objclass = o 157 assert objclass, "objectClass %s must have schema"%objclassName 158 159 objectClasses.extend(objclass.sup or []) 160 161 162 for attr_alias in objclass.must: 163 found_one=0 164 real_attr = self._get_attrtype(str(attr_alias)) 165 for attr in real_attr.name: 166 if process.has_key(attr.upper()): 167 found_one=1 168 if process[attr.upper()] is not None: 169 self._one_formfield(attr, 170 self.entry[process[attr.upper()]], 171 required=True, 172 result=r) 173 for name in real_attr.name: 174 process[name.upper()]=None 175 176 if not found_one: 177 log.msg("Object doesn't have required attribute %s:\n%s"%(attr, self.entry)) 178 self._one_formfield(real_attr.name[0], 179 [], 180 required=True, 181 result=r) 182 183 for attr_alias in objclass.may: 184 found_one=0 185 real_attr = self._get_attrtype(str(attr_alias)) 186 for attr in real_attr.name: 187 if process.has_key(attr.upper()): 188 found_one=1 189 if process[attr.upper()] is not None: 190 self._one_formfield(attr, 191 self.entry[process[attr.upper()]], 192 required=False, 193 result=r) 194 195 if not found_one: 196 # a MAY attributetype not currently present in 197 # object, but user is of course free to add it. 198 attr=str(real_attr.name[0]) 199 self._one_formfield(attr, 200 ('',), 201 required=False, 202 result=r) 203 204 for name in real_attr.name: 205 process[name.upper()]=None 206 207 assert [v is None for k,v in process.items()], "All attributes must be in objectClasses MUST or MAY: %s"%process 208 return r209211 for a in self.attributeTypes: 212 for cur in a.name: 213 if name.upper() == cur.upper(): 214 a.uiHint_multiline=isAttributeTypeMultiLine(a) 215 return a 216 raise RuntimeError, "attribute type %s not known"%name217 220222 """Prune non-changes when old and new state is known.""" 223 if old is None: 224 old = [] 225 o={} 226 n={} 227 for x in old: 228 n[x]=n.get(x, 0)+1 229 for x in new: 230 o[x]=o.get(x, 0)+1 231 232 for k in o.keys(): 233 while o[k]>0: 234 try: 235 old.remove(k) 236 except ValueError: 237 break 238 o[k]-=1 239 240 for k in n.keys(): 241 while n[k]>0: 242 try: 243 new.remove(k) 244 except ValueError: 245 break 246 n[k]-=1 247 248 return old, new249251 entry = context.locate(inevow.ISession).getLoggedInRoot().loggedIn 252 user = entry.dn 253 254 d = defer.succeed(None) 255 256 changes = [] 257 for k,v in kw.items(): 258 if v is None: 259 v = '' 260 if k[:len("edit_")]=="edit_": 261 old=kw.get("old_"+k[len("edit_"):]) 262 attrtype = self._get_attrtype(k[len("edit_"):]) 263 assert attrtype 264 265 if attrtype.single_value or attrtype.uiHint_multiline: 266 v=v.replace('\r\n', '\n') 267 v=v.strip() 268 v=[v] 269 if v == ['']: 270 v = [] 271 if old is not None: 272 old=old.replace('\r\n', '\n') 273 old=old.strip() 274 old=[old] 275 else: 276 v=self._textarea_to_list(v) 277 if old is not None: 278 old=self._textarea_to_list(old) 279 280 o, v = self._prune_changes(old, v) 281 282 if ((old is None and v) 283 or (old is not None 284 and (o or v))): 285 attr=k[len("edit_"):] 286 changes.append((attr, old, v)) 287 #TODO 288 289 if not changes: 290 return EditStatus(self.entry, 'No changes!') 291 292 changes_desc=tags.ul() 293 newRDN = None 294 for rdn in self.entry.dn.split()[0].split(): 295 for attr,old,new in changes: 296 if (rdn.attributeType == attr 297 and old is not None 298 and rdn.value in old): 299 # Need to change the rdn 300 if newRDN is None: 301 newRDN = list(self.entry.dn.split()[0].split()) 302 newRDN.remove(rdn) 303 304 # Try to find a replacement RDN. Possibilities are 305 # new values from the edit form and old values 306 # currently in LDAP. 307 possible = list(new) 308 possible.extend(self.entry.get(rdn.attributeType, [])) 309 for o in old: 310 # Values to be removed are not acceptable as 311 # new RDN. 312 try: 313 possible.remove(o) 314 except ValueError: 315 pass 316 if not possible: 317 raise ldapsyntax.CannotRemoveRDNError \ 318 (rdn.attributeType, 319 rdn.value) 320 newRDN.append(distinguishedname.LDAPAttributeTypeAndValue(attributeType=attr, 321 value=possible[0])) 322 old.remove(rdn.value) 323 try: 324 new.remove(possible[0]) 325 except ValueError: 326 pass 327 if newRDN is not None: 328 newRDN = distinguishedname.RelativeDistinguishedName(newRDN) 329 changes_desc[tags.li[ 330 _("changing %s: changing RDN to say %s") \ 331 %(repr(attr), newRDN)]] 332 newDN = distinguishedname.DistinguishedName( 333 (newRDN,)+self.entry.dn.split()[1:] 334 ) 335 def _move(_): 336 return self.entry.move(newDN)337 d.addCallback(_move) 338 def _redirect(r, ctx, newDN): 339 request = inevow.IRequest(ctx) 340 u = url.URL.fromContext(ctx).curdir() 341 u = u.child(uriQuote(newDN)) 342 request.setComponent(iformless.IRedirectAfterPost, u) 343 return r370 implements(iskin.ISkinnable) 371 372 title = _('Ldaptor Edit Page') 373 374 addSlash = True 375 docFactory = loaders.xmlfile( 376 'edit-really.xhtml', 377 templateDir=os.path.split(os.path.abspath(__file__))[0]) 378441383 super(ReallyEditPage, self).__init__() 384 self.entry = entry 385 self.attributeTypes = attributeTypes 386 self.objectClasses = objectClasses387389 u=url.URL.fromContext(ctx) 390 u=u.parentdir().parentdir().clear() 391 l=[] 392 l.append(tags.a(href=u.sibling("search"))[_("Search")]) 393 l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) 394 return l395 401 404 407409 try: 410 obj = context.locate(inevow.IHand) 411 except KeyError: 412 return context.tag.clear() 413 414 if not isinstance(obj, EditStatus): 415 return context.tag.clear()[obj] 416 417 u=url.URL.fromContext(context) 418 u=u.parentdir().parentdir().clear() 419 420 return context.tag.clear()[ 421 _("Edited "), 422 tags.a(href=u.parentdir() 423 .child(obj.entry.dn) 424 .child("search"))[obj.entry.dn], 425 _(" successfully. "), 426 427 # TODO share implementation with entryLinks 428 '[', 429 tags.a(href=u.sibling('move').child(uriQuote(obj.entry.dn)))[_('move')], 430 '|', 431 tags.a(href=u.sibling('delete').child(uriQuote(obj.entry.dn)))[_('delete')], 432 '|', 433 tags.a(href=u.sibling('change_password').child(uriQuote(obj.entry.dn)))[_('change password')], 434 ']', 435 436 tags.p[obj.changes], 437 438 ]439 440 render_i18n = i18n.render()443 implements(iskin.ISkinnable) 444 445 title = _('Ldaptor Edit Page') 446 447 addSlash = True 448 docFactory = loaders.xmlfile( 449 'edit.xhtml', 450 templateDir=os.path.split(os.path.abspath(__file__))[0]) 451 455469 d.addCallback(_getSchema) 470 def _createEditPage((entry, attributeTypes, objectClasses)): 471 return ReallyEditPage(entry, attributeTypes, objectClasses) 472 d.addCallback(_createEditPage) 473 return d 474 475 render_i18n = i18n.render() 476457 dn = uriUnquote(name) 458 userEntry = inevow.ISession(context).getLoggedInRoot().loggedIn 459 460 e = ldapsyntax.LDAPEntryWithClient(dn=dn, 461 client=userEntry.client) 462 d = e.fetch() 463 def _getSchema(entry): 464 d = fetchschema.fetch(entry.client, entry.dn) 465 def cb((attributeTypes, objectClasses), entry): 466 return (entry, attributeTypes, objectClasses)467 d.addCallback(cb, entry) 468 return d
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0beta1 on Tue Apr 8 20:09:56 2008 | http://epydoc.sourceforge.net |