diff --git a/hshetl/loaders.py b/hshetl/loaders.py
index bbf7ad7fa81c9ab92bbd683fdddd36c70bf02476..0668aeb8720da0c962ee722852f5db2a0f63c1d4 100644
--- a/hshetl/loaders.py
+++ b/hshetl/loaders.py
@@ -562,3 +562,96 @@ class LdapLoader(AbstractLoader):
for attribute in changed_attributes:
output += attribute + ': ' + old[attribute] + ' -> ' + new[attribute] + ', '
return output[:-2]
+
+ def modifyModlist(self, old_entry,new_entry,ignore_attr_types=None,ignore_oldexistent=0,case_ignore_attr_types=None):
+ """
+ Build differential modify list for calling LDAPObject.modify()/modify_s()
+
+ old_entry
+ Dictionary holding the old entry
+ new_entry
+ Dictionary holding what the new entry should be
+ ignore_attr_types
+ List of attribute type names to be ignored completely
+ ignore_oldexistent
+ If non-zero attribute type names which are in old_entry
+ but are not found in new_entry at all are not deleted.
+ This is handy for situations where your application
+ sets attribute value to '' for deleting an attribute.
+ In most cases leave zero.
+ case_ignore_attr_types
+ List of attribute type names for which comparison will be made
+ case-insensitive
+ """
+ import string, ldap
+ ignore_attr_types = list_dict(map(string.lower,(ignore_attr_types or [])))
+ case_ignore_attr_types = list_dict(map(string.lower,(case_ignore_attr_types or [])))
+ modlist = []
+ attrtype_lower_map = {}
+ for a in old_entry.keys():
+ attrtype_lower_map[string.lower(a)]=a
+ for attrtype in new_entry.keys():
+ attrtype_lower = string.lower(attrtype)
+ if ignore_attr_types.has_key(attrtype_lower):
+ # This attribute type is ignored
+ continue
+ # Filter away null-strings
+ new_value = filter(lambda x:x!=None,new_entry[attrtype])
+ if attrtype_lower_map.has_key(attrtype_lower):
+ old_value = old_entry.get(attrtype_lower_map[attrtype_lower],[])
+ old_value = filter(lambda x:x!=None,old_value)
+ del attrtype_lower_map[attrtype_lower]
+ else:
+ old_value = []
+ if not old_value and new_value:
+ # Add a new attribute to entry
+ modlist.append((ldap.MOD_ADD,attrtype,new_value))
+ elif old_value and new_value:
+ # Replace existing attribute
+ replace_attr_value = old_value!=new_value
+ if not replace_attr_value:
+ case_insensitive = case_ignore_attr_types.has_key(attrtype_lower)
+ old_value_dict=list_dict(old_value,case_insensitive)
+ new_value_dict=list_dict(new_value,case_insensitive)
+ delete_values = []
+ for v in old_value:
+ if not new_value_dict.has_key(v):
+ replace_attr_value = 1
+ break
+ add_values = []
+ if not replace_attr_value:
+ for v in new_value:
+ if not old_value_dict.has_key(v):
+ replace_attr_value = 1
+ break
+ if replace_attr_value:
+ modlist.append((ldap.MOD_DELETE,attrtype,None))
+ modlist.append((ldap.MOD_ADD,attrtype,new_value))
+ elif old_value and not new_value:
+ # Completely delete an existing attribute
+ modlist.append((ldap.MOD_DELETE,attrtype,None))
+ if not ignore_oldexistent:
+ # Remove all attributes of old_entry which are not present
+ # in new_entry at all
+ for a in attrtype_lower_map.keys():
+ if ignore_attr_types.has_key(a):
+ # This attribute type is ignored
+ continue
+ attrtype = attrtype_lower_map[a]
+ modlist.append((ldap.MOD_DELETE,attrtype,None))
+ return modlist # modifyModlist()
+
+def list_dict(l,case_insensitive=0):
+ """
+ return a dictionary with all items of l being the keys of the dictionary
+
+ If argument case_insensitive is non-zero ldap.cidict.cidict will be
+ used for case-insensitive string keys
+ """
+ if case_insensitive:
+ d = ldap.cidict.cidict()
+ else:
+ d = {}
+ for i in l:
+ d[i]=None
+ return d