def test_adding(self):
     contact = create(Builder('contact'))
     self.assertTrue(IContact.providedBy(contact))
Esempio n. 2
0
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,
    )