"""
w2lapp.delete.py: delete an entry

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: delete.py,v 1.27 2002/02/01 12:50:13 michael Exp $
"""

import ldap,ldap.async,ldaputil.base, \
       w2lapp.core,w2lapp.cnf,w2lapp.gui


class DeleteLeafs(ldap.async.AsyncSearchHandler):
  """
  Class for deleting entries which are results of a search.
  
  DNs of Non-leaf entries are collected in DeleteLeafs.nonLeafEntries.
  """
  _entryResultTypes = ldap.async._entryResultTypes

  def __init__(self,l):
    ldap.async.AsyncSearchHandler.__init__(self,l)
    self.nonLeafEntries = []
    self.deletedEntries = 0

  def startSearch(self,searchRoot,searchScope):
    if not searchScope in [ldap.SCOPE_ONELEVEL,ldap.SCOPE_SUBTREE]:
      raise ValueError, "Parameter searchScope must be either ldap.SCOPE_ONELEVEL or ldap.SCOPE_SUBTREE."
    self.nonLeafEntries = []
    self.deletedEntries = 0
    ldap.async.AsyncSearchHandler.startSearch(
      self,
      searchRoot,
      searchScope,
      filterStr='(objectClass=*)',
      attrList=['hasSubordinates','subordinateCount'],
      attrsOnly=0,
    )

  def _processSingleResult(self,resultType,resultItem):
    if self._entryResultTypes.has_key(resultType):
      # Don't process search references
      dn,entry = resultItem
      hasSubordinates = entry.get(
        'hasSubordinates',
        entry.get('hassubordinates',['FALSE']
        )
      )[0]
      subordinateCount = entry.get(
        'subordinateCount',
        entry.get('subordinatecount',['0'])
      )[0]
      if hasSubordinates=='TRUE' or int(subordinateCount):
        self.nonLeafEntries.append(dn)
      else:
        try:
          self._l.delete_s(dn)
        except ldap.NOT_ALLOWED_ON_NONLEAF,e:
          self.nonLeafEntries.append(dn)
        else:
          self.deletedEntries = self.deletedEntries+1
        

def DelTree(l,dn,scope=ldap.SCOPE_ONELEVEL):
  """
  Recursively delete entries below or including entry with name dn.
  """
  leafs_deleter = DeleteLeafs(l)
  leafs_deleter.startSearch(dn,scope)
  leafs_deleter.processResults()
  deleted_entries = leafs_deleter.deletedEntries
  non_leaf_entries = leafs_deleter.nonLeafEntries[:]
  while non_leaf_entries:
    dn = non_leaf_entries.pop()
    leafs_deleter.startSearch(dn,ldap.SCOPE_SUBTREE)
    leafs_deleter.processResults()
    deleted_entries = deleted_entries+leafs_deleter.deletedEntries
    non_leaf_entries.extend(leafs_deleter.nonLeafEntries)
  return deleted_entries # DelTree()


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

  delete_confirm = form.getInputValue('delete_confirm',[None])[0]
  delete_attr = map(str,form.getInputValue('delete_attr',[]))

  if delete_confirm:

    if delete_confirm=='yes':

      # Recursive delete of whole sub-tree

      if delete_attr:
        scope = ldap.SCOPE_BASE
      else:
        scope = int(form.getInputValue('scope',[str(ldap.SCOPE_BASE)])[0])

      if scope in [ldap.SCOPE_ONELEVEL,ldap.SCOPE_SUBTREE]:

        ##########################################################
        # Recursive delete of entries in sub-tree
        ##########################################################

	deleted_entries_count = DelTree(ls.l,dn,scope)
        old_dn = dn
        if scope==ldap.SCOPE_SUBTREE:
          dn = ldaputil.base.ParentDN(dn)
          ls.setDN(dn)
        w2lapp.gui.SimpleMessage(
          sid,outf,form,ls,dn,
          'Deleted sub-tree',
          'Deleted %d entries %s %s.' % (
            deleted_entries_count,
            {
              1:'below',
              2:'below and including',
            }[scope],
            w2lapp.gui.DisplayDN(sid,form,ls,old_dn)
          ),
          main_menu_list=w2lapp.gui.MainMenu(sid,form,ls,dn),
          context_menu_list=[]
        )

      elif scope==ldap.SCOPE_BASE and delete_attr:

        ##########################################################
        # Delete only one or few attribute(s) of an entry
        ##########################################################

        mod_list = [
          (ldap.MOD_DELETE,attr_type,None)
          for attr_type in delete_attr
        ]
        ls.modifyEntry(dn,mod_list)
        w2lapp.gui.SimpleMessage(
          sid,outf,form,ls,dn,
          'Deleted Attribute(s)',
          'Deleted attribute(s) <em>%s</em> of entry %s.' % (
            '</em>, <em>'.join(delete_attr),
            w2lapp.gui.DisplayDN(sid,form,ls,dn)
          ),
          main_menu_list=w2lapp.gui.MainMenu(sid,form,ls,dn),
          context_menu_list=[]
        )

      elif scope==ldap.SCOPE_BASE:
      
        ##########################################################
        # Delete a single whole entry
        ##########################################################

        ls.deleteEntry(dn)
        old_dn = dn
        dn = ldaputil.base.ParentDN(dn)
        ls.setDN(dn)
        w2lapp.gui.SimpleMessage(
          sid,outf,form,ls,dn,
          'Deleted Entry',
          '<p>Deleted entry %s.</p></div>' % (
            w2lapp.gui.DisplayDN(sid,form,ls,old_dn)
          ),
          main_menu_list=w2lapp.gui.MainMenu(sid,form,ls,dn),
          context_menu_list=[]
        )

    else:
      raise w2lapp.core.ErrorExitClass(ls,dn,'Canceled delete.')

  else:

    ##########################################################
    # Show delete confirmation and delete mode select form
    ##########################################################

    dn_html = w2lapp.gui.DisplayDN(sid,form,ls,dn)

    if delete_attr:
      scope_input_html = 'Delete attribute(s) <em>%s</em> of entry %s.\n%s\n' % (
        '</em>, <em>'.join(delete_attr),
        dn_html,
        '\n'.join([
          '<input type="hidden" name="delete_attr" value="%s">' % (
            attr_type
          )
          for attr_type in delete_attr
        ])
      )
    elif ls.isLeafEntry(dn):
      scope_input_html = """
        <p>Delete whole entry %s.</p>
      """ % (
        dn_html
      )
    else:
      scope_input_html = """
        <p>Delete entry %s. Are you sure?</p>
        <p>Delete mode: %s</p>
        <p><strong>
            Use recursive delete with extreme care!
            Might take some time.
        </strong></p>
      """ % (
        dn_html,
        form.field['scope'].inputHTML(str(ldap.SCOPE_ONELEVEL))
      )

    # Output confirmation form
    w2lapp.gui.TopSection(
      sid,outf,form,ls,dn,
      'Delete entry?',
      w2lapp.gui.MainMenu(sid,form,ls,dn),
      context_menu_list=[]
    )

    outf.write("""<div id=MessageDiv>
<form action="%s/delete/%s"
      method="POST"
      enctype="application/x-www-form-urlencoded"
      accept-charset="%s"
>
  %s
  <p><strong>Are you sure?</strong></p>
  <input type="hidden" name="dn" value="%s">
  <input type="submit" name="delete_confirm" value="yes">
  <input type="submit" name="delete_confirm" value="no">
</form>
</div>
""" % (
      form.script_name,sid,form.accept_charset,
      scope_input_html,
      w2lapp.core.utf2display(form.accept_charset,dn),
    ))
    w2lapp.gui.PrintFooter(outf,form)

