"""
w2lapp.groupadm.py: add/delete user entry to/from group entries

web2ldap - a web-based LDAP Client,
see http://www.web2ldap.de for details

(c) by Michael Stroeder <michael@stroeder.com>

This module is distributed under the terms of the
GPL (GNU GENERAL PUBLIC LICENSE) Version 2
(see http://www.gnu.org/copyleft/gpl.html)

$Id: groupadm.py,v 1.23 2002/01/11 12:46:11 michael Exp $
"""

import ldap,ldaputil.base,msbase, \
       pyweblib,w2lapp.core,w2lapp.gui

group_objectclasses = {
  'rfc822MailGroup':    ('mail','mail'),
  'mailGroup':          ('mgrprfc822mailmember','mail'),
  'posixGroup':         ('memberUid','uid'),
  'groupOfNames':       ('member',None),
  'groupOfUniqueNames': ('uniqueMember',None),
}
group_objectclasses_keys = group_objectclasses.keys()
needed_group_attrs = ['objectClass','cn','description']

def w2l_GroupAdm(sid,outf,command,form,ls,dn):

  result_dnlist = ls.readEntry(dn,['objectClass'])
  if not result_dnlist:
    raise w2lapp.core.ErrorExitClass(ls,dn,"""
      No search result when reading entry <strong>%s</strong>.""" % (
        w2lapp.gui.DisplayDN(sid,form,ls,dn)
      )
    )
  entry = msbase.CaseinsensitiveStringKeyDict(result_dnlist[0][1])

  if 'groupadm_add' in form.inputFieldNames or \
     'groupadm_remove' in form.inputFieldNames:

    for action in ['add','remove']:
      if 'groupadm_%s'%action in form.inputFieldNames:
        for group_dn in form.field['groupadm_%s'%action].value:
          objectClasses_lower = [
            oc.lower()
            for oc in ls.getObjectClasses(group_dn)
          ]
          modlist = []
          for objectClass in group_objectclasses_keys:
            if objectClass.lower() in objectClasses_lower:
              member_attrtype,value_attrtype = group_objectclasses[objectClass]
              if value_attrtype is None:
                member_value = dn.encode(ls.charset)
              else:
                if not entry.has_key(value_attrtype):
                  raise w2lapp.core.ErrorExitClass(ls,dn,"""
                    Object class %s requires entry to have member attribute %s.""" % (
                      objectClass,value_attrtype
                    )
                  )                
                member_value = entry[value_attrtype][0]
              modlist.append((
                {'add':ldap.MOD_ADD,'remove':ldap.MOD_DELETE}[action],
                member_attrtype,member_value
              ))
          if modlist:
            ls.modifyEntry(group_dn,modlist)

  # FIX ME!!! Group search does not work if searches are cached.
#  ls.flushCache()

  filter_components = []
  for oc in group_objectclasses.keys():
    group_member_attrtype,user_entry_attrtype = group_objectclasses[oc]
    if user_entry_attrtype is None:
      user_entry_attrvalue = dn
    else:
      user_entry_attrvalue = entry.get(user_entry_attrtype,[None])[0]
    if not user_entry_attrvalue is None:
      filter_components.append(
        (
          oc,
          group_member_attrtype,
          ldaputil.base.escape_filter_chars(user_entry_attrvalue.encode(ls.charset))
        )
      )

  all_group_filterstr = '(|%s)' % (''.join(
    [
      '(objectClass=%s)' % (oc)
      for oc,attr_type,attr_value in filter_components
    ]
  ))
  remove_group_filterstr = '(|%s)' % (''.join(
    [
      '(&(objectClass=%s)(%s=%s))' % (oc,attr_type,attr_value)
      for oc,attr_type,attr_value in filter_components
    ]
  ))
    
  # Search all groups entries where groupadm_memberdn is not member
  all_groups = []
  # Search all group entries where groupadm_memberdn is a member
  for group_dn,entry in ls.l.search_st(
    ls.searchRoot.encode(ls.charset),
    ldap.SCOPE_SUBTREE,
    all_group_filterstr,
    attrlist=needed_group_attrs,attrsonly=0,timeout=ls.timeout
  ):
    if group_dn!=None:
      ci_entry = msbase.CaseinsensitiveStringKeyDict(entry,default=[])
      all_groups.append(
        (
          group_dn,
          ', '.join(w2lapp.core.utf2display(form.accept_charset,entry['cn']))
        )
      )

  remove_groups = []
  # Search all group entries where groupadm_memberdn is a member
  for group_dn,entry in ls.l.search_st(
    ls.searchRoot.encode(ls.charset),
    ldap.SCOPE_SUBTREE,
    remove_group_filterstr,
    attrlist=needed_group_attrs,attrsonly=0,timeout=ls.timeout
  ):
    if group_dn!=None:
      ci_entry = msbase.CaseinsensitiveStringKeyDict(entry,default=[])
      remove_groups.append(
        (
          group_dn,
          ', '.join(w2lapp.core.utf2display(form.accept_charset,entry['cn']))
        )
      )

  # Group selection form
  w2lapp.gui.TopSection(
    sid,outf,form,ls,dn,
    'Group selection table',
    w2lapp.gui.MainMenu(sid,form,ls,dn),
    context_menu_list=[]
  )

  all_groups = [
    group_dn
    for group_dn in all_groups
    if not group_dn in remove_groups
  ]

  if all_groups:

    add_select_field = pyweblib.forms.Select(
      'groupadm_add','Add to group',1000,
      options=all_groups,default=[],size=15,multiSelect=1
    )
    remove_select_field = pyweblib.forms.Select(
      'groupadm_remove','remove to group',1000,
      options=remove_groups,default=[],size=15,multiSelect=1
    )

    outf.write("""<div id="MessageDiv">
<form action="%s/groupadm/%s"
      method="POST"
      enctype="application/x-www-form-urlencoded"
      accept-charset="%s"
>
  <input type="hidden" name="dn" value="%s">
  <p>Choose the group entries found below %s.</p>
  <input type="submit" value="Change group membership">
  <table>
""" % (
      form.script_name,sid,form.accept_charset,
      w2lapp.core.utf2display(form.accept_charset,dn),
      w2lapp.gui.DisplayDN(sid,form,ls,ls.searchRoot),
    ))

    outf.write("""
    <tr>
      <td width="50%%">Add to...</td>
      <td width="50%%">Remove from...</td>
    </tr>
    <tr>
      <td width="50%%">%s</td>
      <td width="50%%">%s</td>
    </tr>
""" % (add_select_field.inputHTML(),remove_select_field.inputHTML()))

    outf.write("""  
  </table>
</form>
</div>
""")

  else:
    outf.write("""
      <div id="ErrorMessageDiv">
        <h1>No group entries</h1>
        <p>
          No group entries found below %s.
        </p>
      </div>
      """ % (w2lapp.gui.DisplayDN(sid,form,ls,ls.searchRoot))
    )

  w2lapp.gui.PrintFooter(outf,form)
