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

Source Code for Module ldaptor.apps.webui.search

  1  from zope.interface import implements,Interface 
  2  from twisted.internet import defer 
  3  from webut.skin import iskin 
  4  from ldaptor.protocols.ldap import ldapclient, ldapsyntax 
  5  from ldaptor.protocols.ldap import distinguishedname, ldapconnector 
  6  from ldaptor.protocols import pureldap 
  7  from ldaptor import ldapfilter, interfaces 
  8  from twisted.internet import reactor 
  9  from ldaptor.apps.webui import config, iwebui 
 10  from ldaptor.apps.webui.uriquote import uriQuote 
 11  from ldaptor.apps.webui.i18n import _ 
 12  from ldaptor.apps.webui import i18n 
 13  from ldaptor import weave 
 14   
 15  import os 
 16  from nevow import rend, inevow, loaders, url, tags 
 17  from formless import annotate, webform, iformless, configurable 
 18   
19 -class IMove(Interface):
20 """Entries being moved in the tree.""" 21 pass
22
23 -class MoveItem(configurable.Configurable):
24 - def getBindingNames(self, ctx):
25 return ['move', 'cancel']
26
27 - def bind_move(self, ctx):
28 return annotate.MethodBinding( 29 'move', 30 annotate.Method(arguments=[ 31 annotate.Argument('context', annotate.Context()), 32 ], 33 label=_('Move')), 34 action=_('Move'))
35
36 - def bind_cancel(self, ctx):
37 return annotate.MethodBinding( 38 'cancel', 39 annotate.Method(arguments=[ 40 annotate.Argument('context', annotate.Context()), 41 ], 42 label=_('Cancel')), 43 action=_('Cancel'))
44
45 - def _remove(self, context):
46 session = context.locate(inevow.ISession) 47 move = session.getComponent(IMove) 48 if move is None: 49 return 50 try: 51 move.remove(self.original) 52 except ValueError: 53 pass
54
55 - def move(self, context):
56 cfg = context.locate(interfaces.ILDAPConfig) 57 newDN = distinguishedname.DistinguishedName( 58 self.original.dn.split()[:1] 59 + iwebui.ICurrentDN(context).split()) 60 origDN = self.original.dn 61 d = self.original.move(newDN) 62 d.addCallback(lambda dummy: _('Moved %s to %s.') % (origDN, newDN)) 63 def _cb(r, context): 64 self._remove(context) 65 return r
66 d.addCallback(_cb, context) 67 return d
68
69 - def cancel(self, context):
70 self._remove(context) 71 return _('Cancelled move of %s') % self.original.dn
72
73 -def strScope(scope):
74 if scope == pureldap.LDAP_SCOPE_wholeSubtree: 75 return _('whole subtree') 76 elif scope == pureldap.LDAP_SCOPE_singleLevel: 77 return _('single level') 78 elif scope == pureldap.LDAP_SCOPE_baseObject: 79 return _('baseobject') 80 else: 81 raise RuntimeError, 'scope is not known: %r' % scope
82
83 -class SearchForm(configurable.Configurable):
84 implements(inevow.IContainer) 85 86 filter = None 87
88 - def __init__(self):
89 super(SearchForm, self).__init__(None) 90 self.data = {}
91
92 - def getBindingNames(self, ctx):
93 return ['search']
94
95 - def bind_search(self, ctx):
96 l = [] 97 l.append(annotate.Argument('ctx', annotate.Context())) 98 for field in config.getSearchFieldNames(): 99 l.append(annotate.Argument('search_%s' % field, 100 annotate.String(label=field))) 101 l.append(annotate.Argument('searchfilter', 102 annotate.String(label=_("Advanced search")))) 103 l.append(annotate.Argument( 104 'scope', 105 annotate.Choice(label=_("Search depth"), 106 choices=[ pureldap.LDAP_SCOPE_wholeSubtree, 107 pureldap.LDAP_SCOPE_singleLevel, 108 pureldap.LDAP_SCOPE_baseObject, 109 ], 110 stringify=strScope, 111 default=pureldap.LDAP_SCOPE_wholeSubtree))) 112 113 return annotate.MethodBinding( 114 name='search', 115 action=_("Search"), 116 typeValue=annotate.Method(arguments=l, 117 label=_('Search')))
118
119 - def search(self, ctx, scope, searchfilter, **kw):
120 filt=[] 121 for k,v in kw.items(): 122 assert k.startswith('search_') 123 if not k.startswith("search_"): 124 continue 125 k=k[len("search_"):] 126 if v is None: 127 continue 128 v=v.strip() 129 if v=='': 130 continue 131 132 # TODO escape ) in v 133 # TODO handle unknown filter name right (old form open in browser etc) 134 filter_ = config.getSearchFieldByName(k, vars={'input': v}) 135 filt.append(ldapfilter.parseFilter(filter_)) 136 if searchfilter: 137 try: 138 filt.append(ldapfilter.parseFilter(searchfilter)) 139 except ldapfilter.InvalidLDAPFilter, e: 140 raise annotate.ValidateError( 141 {'searchfilter': str(e), }, 142 partialForm=inevow.IRequest(ctx).args) 143 144 if filt: 145 if len(filt)==1: 146 query=filt[0] 147 else: 148 query=pureldap.LDAPFilter_and(filt) 149 else: 150 query=pureldap.LDAPFilterMatchAll 151 152 self.data.update(kw) 153 154 # annotate.Choice in nevow 0.3 maps choices to a list, and 155 # passes indexes to this list to client. annotate.Choice in 156 # 0.4pre converts choice to string and back with callbacks, 157 # defaulting to str, and leaving the value as string. We 158 # can't use the 0.4pre mechanism as long as we need 0.3 159 # compatibility, so work around that by explicitly making sure 160 # scope is an integer. 161 scope = int(scope) 162 163 self.data['scope'] = scope 164 self.data['searchfilter'] = searchfilter 165 self.filter = query 166 return self
167
168 - def child(self, context, name):
169 fn = getattr(self, 'child_%s' % name, None) 170 if fn is None: 171 return None 172 else: 173 return fn(context)
174
175 - def child_filter(self, context):
176 return self.filter.asText()
177
178 - def child_results(self, context):
179 assert self.filter is not None 180 cfg = context.locate(interfaces.ILDAPConfig) 181 182 c=ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) 183 curDN = iwebui.ICurrentDN(context) 184 d=c.connectAnonymously(curDN, 185 cfg.getServiceLocationOverrides()) 186 187 def _search(proto, dn, searchFilter, scope): 188 baseEntry = ldapsyntax.LDAPEntry(client=proto, dn=dn) 189 d=baseEntry.search(filterObject=searchFilter, 190 scope=scope, 191 sizeLimit=20, 192 sizeLimitIsNonFatal=True) 193 def _cb(result, proto): 194 proto.unbind() 195 return result
196 d.addBoth(_cb, proto) 197 return d
198 d.addCallback(_search, curDN, self.filter, self.data['scope']) 199 return d 200
201 - def child_base(self, context):
202 cfg = context.locate(interfaces.ILDAPConfig) 203 204 c=ldapconnector.LDAPClientCreator(reactor, ldapclient.LDAPClient) 205 d=c.connectAnonymously(iwebui.ICurrentDN(context), 206 cfg.getServiceLocationOverrides()) 207 208 def _search(proto, base): 209 baseEntry = ldapsyntax.LDAPEntry(client=proto, 210 dn=base) 211 d=baseEntry.search(scope=pureldap.LDAP_SCOPE_baseObject, 212 sizeLimit=1) 213 def _cb(result, proto): 214 proto.unbind() 215 return result
216 d.addBoth(_cb, proto) 217 return d 218 d.addCallback(_search, iwebui.ICurrentDN(context)) 219 220 def _first(results, dn): 221 assert len(results)==1, \ 222 "Expected one result, not %r" % results 223 return {'dn': dn, 224 'attributes': results[0], 225 } 226 d.addCallback(_first, iwebui.ICurrentDN(context)) 227 228 return d 229
230 - def __nonzero__(self):
231 return self.filter is not None
232
233 -def getSearchForm(ctx):
234 try: 235 hand = ctx.locate(inevow.IHand) 236 except KeyError: 237 pass 238 else: 239 if isinstance(hand, SearchForm): 240 return hand 241 return SearchForm()
242
243 -class SearchPage(rend.Page):
244 implements(iskin.ISkinnable) 245 246 title = _('Ldaptor Search Page') 247 248 addSlash = True 249 250 docFactory = loaders.xmlfile( 251 'search.xhtml', 252 templateDir=os.path.split(os.path.abspath(__file__))[0]) 253
254 - def render_form(self, ctx, data):
255 conf = getSearchForm(ctx) 256 formDefaults = ctx.locate(iformless.IFormDefaults) 257 methodDefaults = formDefaults.getAllDefaults('search') 258 for k,v in conf.data.items(): 259 if v is not None: 260 methodDefaults[k] = str(v) 261 return webform.renderForms()
262
263 - def render_keyvalue(self, context, data):
264 return weave.keyvalue(context, data)
265
266 - def render_keyvalue_item(self, context, data):
267 return weave.keyvalue_item(context, data)
268
269 - def render_passthrough(self, context, data):
270 return context.tag.clear()[data]
271
272 - def data_status(self, context, data):
273 try: 274 obj = context.locate(inevow.IStatusMessage) 275 except KeyError: 276 return '' 277 278 if isinstance(obj, SearchForm): 279 return '' 280 else: 281 return obj
282
283 - def render_data(self, ctx, data):
284 return ctx.tag.clear()[data]
285
286 - def render_if(self, context, data):
287 r=context.tag.allPatterns(str(bool(data))) 288 return context.tag.clear()[r]
289
290 - def data_search(self, ctx, data):
291 return getSearchForm(ctx)
292
293 - def data_header(self, ctx, data):
294 u=url.URL.fromContext(ctx) 295 u=u.parentdir().clear() 296 l=[] 297 l.append(tags.a(href=u.sibling("add"))[_("add new entry")]) 298 return l
299 311 316
317 - def render_linkedDN(self, ctx, data):
318 dn = data 319 cfg = ctx.locate(interfaces.ILDAPConfig) 320 baseDN = iwebui.ICurrentDN(ctx) 321 322 ctx.tag.clear() 323 while (dn!=baseDN 324 and dn!=distinguishedname.DistinguishedName(stringValue='')): 325 firstPart=dn.split()[0] 326 327 u = url.here.parentdir().parentdir().child(dn) 328 segments = inevow.ICurrentSegments(ctx) 329 if segments[-1] == '': 330 u = u.child(segments[-2]).child(segments[-1]) 331 else: 332 u = u.child(segments[-1]) 333 for segment in inevow.IRemainingSegments(ctx): 334 u = u.child(segment) 335 ctx.tag[tags.a(href=u)[str(firstPart)], ','] 336 dn=dn.up() 337 338 ctx.tag['%s\n' % str(dn)] 339 return ctx.tag
340 353
354 - def render_listLen(self, context, data):
355 if data is None: 356 length = 0 357 else: 358 length = len(data) 359 return context.tag.clear()[length]
360
361 - def render_mass_change_password(self, ctx, data):
362 u = url.URL.fromContext(ctx) 363 u = u.parentdir().sibling("mass_change_password") 364 u = u.child(uriQuote(data)) 365 return ctx.tag(href=u)
366
367 - def data_move(self, context, data):
368 session = context.locate(inevow.ISession) 369 if not session.getLoggedInRoot().loggedIn: 370 return [] 371 move = session.getComponent(IMove) 372 if move is None: 373 return [] 374 return move
375
376 - def locateConfigurable(self, context, name):
377 if name == '': 378 return getSearchForm(context) 379 elif name.startswith('move_'): 380 dn = name[len('move_'):] 381 382 session = context.locate(inevow.ISession) 383 move = session.getComponent(IMove) 384 if move is not None: 385 for entry in move: 386 if entry.dn == dn: 387 return iformless.IConfigurable(MoveItem(entry)) 388 389 raise KeyError, name
390
391 - def render_move(self, context, data):
392 return webform.renderForms('move_%s' % data.dn)[context.tag]
393 394 render_i18n = i18n.render()
395
396 -def getSearchPage():
397 r = SearchPage() 398 return r
399