def test_adding(self): contact = create(Builder('contact')) self.assertTrue(IContact.providedBy(contact))
def sync_contacts(context, ldap_records, set_owner=False): """Synchronize the given ldap results """ # Statistics created = 0 modified = 0 skipped = 0 failed = 0 deleted = 0 dn_contact_id_mapping = {} ct = getToolByName(context, 'portal_catalog') mapper = get_ldap_attribute_mapper() dummy_contact = createContent('ftw.contacts.Contact') dummy_contact_folder = createContent('ftw.contacts.ContactFolder') # 1st pass: create or update profiles for dn, entry in ldap_records: if not dn: continue dn = dn.decode('unicode_escape').encode('iso8859-1').decode('utf-8') # Only entries with a contact id contact_id = entry.get(mapper.id(), [None, ])[0] if not contact_id: skipped += 1 logger.debug("Skipping entry '%s'. No contact id." % dn) continue # Get the normalzied name for the contact with the given id. This has # to be done on a dummy folder because existing content would influence # the resulting id. contact_id = INameChooser(dummy_contact_folder).chooseName( contact_id, dummy_contact) dn_contact_id_mapping[dn] = contact_id contact = context.unrestrictedTraverse(contact_id, None) changed = False is_new_object = False # Check if we really got the wanted object. if not IContact.providedBy(contact): contact = None # Create contact if contact is None: try: contact = createContent('ftw.contacts.Contact') contact.id = contact_id addContentToContainer(context, contact) is_new_object = True # invalid id except BadRequest: failed += 1 logger.warn("Could not create contact '%s' (invalid id)." % contact_id) continue # Update/set field values IContactSchema(contact).ldap_dn = dn field_mapping = dict(getFields(IContactSchema)) for ldap_name, field_name in mapper.mapping().items(): field = field_mapping.get(field_name, None) if field is None: raise NotImplementedError() value = entry.get(ldap_name, [''])[0] current_value = field.get(contact) if IBlobWrapper.providedBy(current_value): current_value = current_value.data if current_value != value: # Handle images if INamedImageField.providedBy(field) and value: infile = StringIO(value) filename = '%s.jpg' % contact_id value = File(filename, filename, infile, 'image/jpeg') value.filename = filename field.set(contact, value) changed = True # Update/set fields with custom updaters custom_updaters = getAdapters((contact, entry), provided=ILDAPCustomUpdater) for name, updater in custom_updaters: changed = updater.update() if is_new_object: if set_owner: # Grant owner role to contact contact.__ac_local_roles__ = None contact.manage_setLocalRoles(contact_id, ['Owner']) contact.reindexObjectSecurity() notify(ObjectCreatedEvent(contact)) aq_contact = context.get(contact_id) ct.catalog_object(aq_contact, '/'.join(aq_contact.getPhysicalPath())) created += 1 logger.debug("Created new contact '%s (%s)'." % (contact_id, dn)) elif changed: contact.reindexObject() notify(ObjectModifiedEvent(contact)) modified += 1 logger.debug("Modified contact '%s' (%s)." % (contact_id, dn)) total = len(ldap_records) unchanged = total - skipped - modified - created - failed # 2nd pass: set references # TODO # 3rd pass: delete contacts which have an ldap_id but are not in LDAP. all_contacts = ct.unrestrictedSearchResults( portal_type='ftw.contacts.Contact', path=dict(query='/'.join(context.getPhysicalPath()), depth=1)) to_be_deleted = {} for contact in all_contacts: obj = contact.getObject() ldap_dn = IContactSchema(obj).ldap_dn if ldap_dn and ldap_dn not in dn_contact_id_mapping: parent_path = '/'.join(obj.getPhysicalPath()[:-1]) id_ = obj.getPhysicalPath()[-1] if parent_path not in to_be_deleted: to_be_deleted[parent_path] = [] to_be_deleted[parent_path].append(id_) logger.debug("Deleting contact '%s'" % id_) # Disable link integrity check while deleting contacts ptool = getToolByName(context, 'portal_properties') props = getattr(ptool, 'site_properties') old_check = props.getProperty('enable_link_integrity_checks', False) props.enable_link_integrity_checks = False for parent_path, ids in to_be_deleted.items(): parent = context.unrestrictedTraverse(parent_path) deleted += len(ids) parent.manage_delObjects(ids) # Reenable previous link integrity setting props.enable_link_integrity_checks = old_check return dict( created=created, modified=modified, unchanged=unchanged, total=total, skipped=skipped, failed=failed, deleted=deleted, )