def _getTeaserMedia(self, high, scale): """ teaser media utility method """ obj = high.getObject() media = obj.getMedia() media_url = media_type = media_title = media_copy = media_note = '' if media: if IBlobWrapper.providedBy(media): media_url = obj.absolute_url() + '/image' else: media_url = media.absolute_url() if obj.absolute_url() in media_url: # image in image field media_type = 'Image' media_title = obj.getImageCaption() media_copy = obj.getImageCopyright() media_note = obj.getImageNote() else: # reference to an Image or FlashFile media_type = media.portal_type media_title = media.Title() media_copy = media.Rights() media_note = media.Description() adapter = queryMultiAdapter((obj, self.request), name=u'themes-object', default=None) themes = [] if adapter is not None: themes = adapter.short_items() result = { 'id': high['id'], 'getUrl': high.get('getUrl', high.getURL()), 'getNewsTitle': high['getNewsTitle'], 'getTeaser': high['getTeaser'], 'effective': high['effective'], 'expires': high['expires'], 'getVisibilityLevel': high['getVisibilityLevel'], 'themes': themes, } if media is not None: result['media'] = { 'absolute_url': media_url, 'portal_type': media_type, 'Title': media_title, 'Rights': media_copy, 'Description': media_note, 'getScale': '' } if IBlobWrapper.providedBy(media): result['media']['getScale'] = obj.getField('image').tag( obj, scale=scale) return result
def _getTeaserMedia(self, high, scale): """ teaser media utility method """ obj = high.getObject() media = obj.getMedia() media_url = media_type = media_title = media_copy = media_note = '' if media: if IBlobWrapper.providedBy(media): media_url = obj.absolute_url() + '/image' else: media_url = media.absolute_url() if obj.absolute_url() in media_url: # image in image field media_type = 'Image' media_title = obj.getImageCaption() media_copy = obj.getImageCopyright() media_note = obj.getImageNote() else: # reference to an Image or FlashFile media_type = media.portal_type media_title = media.Title() media_copy = media.Rights() media_note = media.Description() adapter = queryMultiAdapter((obj, self.request), name=u'themes-object', default=None) themes = [] if adapter is not None: themes = adapter.short_items() result = { 'id': high['id'], 'getUrl': high.get('getUrl', high.getURL()), 'getNewsTitle': high['getNewsTitle'], 'getTeaser': high['getTeaser'], 'effective': high['effective'], 'expires': high['expires'], 'getVisibilityLevel': high['getVisibilityLevel'], 'themes': themes, } if media is not None: result['media'] = { 'absolute_url' : media_url, 'portal_type' : media_type, 'Title' : media_title, 'Rights' : media_copy, 'Description' : media_note, 'getScale' : '' } if IBlobWrapper.providedBy(media): result['media']['getScale'] = obj.getField('image').tag( obj, scale=scale) return result
def migrateToBlobs(context): from plone.app.blob.interfaces import IBlobWrapper portal = context.portal_url.getPortalObject() ctool = getToolByName(portal, 'portal_catalog') items = ctool(object_provides=ILeadImageable.__identifier__) cnt = len(items) logger.info('Migrating %d items' % cnt) for item in items: obj = item.getObject() field = obj.getField(IMAGE_FIELD_NAME) if (field is not None) and not IBlobWrapper.providedBy(field.getUnwrapped(obj)): # this is apparently old content. AttributeError is raised # when old site is upgraded to BLOB aware one # Let's migrate logger.info('Migrating item %s' % '/'.join(obj.getPhysicalPath())) extender = LeadImageExtender(obj) if extender.fields: extfield = extender.fields[0] if extfield.getName() == IMAGE_FIELD_NAME: # ok, retrieve the value value = extfield.get(obj) if hasattr(extfield, 'removeScales'): extfield.removeScales(obj) field.getMutator(obj)(value) logger.info('Migration finished')
def convert(self, value): """ Convert value to more JSON friendly format. """ if isinstance(value, DateTime): # Zope DateTime # http://pypi.python.org/pypi/DateTime/3.0.2 return value.ISO8601() elif isinstance(value, FormattableName): return dict(value.items()) elif IBlobWrapper.providedBy(value): # TODO: equivalent handling of binary data as in isBinary special case return None elif hasattr(value, "isBinary") and value.isBinary(): if not EXPORT_BINARY: return None # Archetypes FileField and ImageField payloads # are binary as OFS.Image.File object data = getattr(value.data, "data", None) if not data: return None return base64.b64encode(data) else: # Passthrough return value
def fieldSerialization(self, field, value): """ Custom serialization for fields which provide field values that are incompatible with json / JSON-standard. @param field: Field-Object from Schema @type field: Field @param value: Return-Value of the Raw-Accessor of the Field on the current context @type value: string or stream @return: JSON-optimized value @rtype: string """ if isinstance(field, DateTimeField) and value: value = str(value) elif HAS_BLOBS and IBlobWrapper.providedBy(value): file_ = value.getBlob().open() value = {'filename': value.getFilename(), 'data': base64.encodestring(file_.read()), 'type': 'blob'} file_.close() elif isinstance(field, FileField) and isinstance(value, File): tmp = StringIO.StringIO(value.data) tmp.seek(0) value = {'filename': value.filename, 'data': base64.encodestring(tmp.read())} elif isinstance(field, QueryField): query = field.getRaw(self.object) # Cast "ZPublisher.HTTPRequest.record" instance to dict value = [dict(item) for item in query] return value
def _expand_binary_data(self, obj, data): max_size = es_config.max_blobsize is_archetype = False if HAS_ARCHETYPES and IBaseContent.providedBy(obj): is_archetype = True schema = obj.Schema() for fieldname in self._iterate_binary_fields(obj, data): if fieldname not in data: data[fieldname] = None continue if is_archetype: field = schema[fieldname] value = field.get(obj) if value is None: data[fieldname] = None continue data[fieldname + '_meta'] = data[fieldname] if IBlobWrapper.providedBy(value): if max_size and value.get_size() > max_size: data[fieldname] = None del data[fieldname + '_meta'] msg = 'File too big for ElasticSearch Indexing: {0}' logger.info(msg.format(obj.absolute_url(), ), ) continue with value.getBlob().open() as fh: data[fieldname] = base64.b64encode(fh.read()) elif ITextField.providedBy(field): data[fieldname] = base64.b64encode( data[fieldname + '_meta']['data'].encode('utf8')) else: field = getattr(obj, fieldname, None) if field is None: data[fieldname] = None continue data[fieldname + '_meta'] = data[fieldname] if IBlobby.providedBy(field): if max_size and field.getSize() > max_size: data[fieldname] = None del data[fieldname + '_meta'] msg = 'File too big for ElasticSearch Indexing: {0}' logger.info(msg.format(obj.absolute_url(), ), ) continue with field.open() as fh: data[fieldname] = base64.b64encode(fh.read()) elif IRichTextValue.providedBy(field): data[fieldname] = base64.b64encode( data[fieldname + '_meta']['data'].encode('utf8'), ) if max_size and len(data[fieldname]) > max_size: data[fieldname] = None del data[fieldname + '_meta'] logger.info( 'File too big for ElasticSearch Indexing: {0}'.format( obj.absolute_url(), ), )
def check_is_image(img): """ Check image class """ try: # first try blob Images from plone.app.blob.interfaces import IBlobWrapper from plone.app.imaging.interfaces import IBaseObject checked = False if IBaseObject.providedBy(img) or IBlobWrapper.providedBy(img): checked = True if not checked: raise except: # try to see if it is an old OFS.image if not isinstance(img, Image): raise ValueError('This is not a plone Image.')
def check_is_image(img): """ Check image class """ try: # first try blob Images from plone.app.blob.interfaces import IBlobWrapper from plone.app.imaging.interfaces import IBaseObject checked = False if IBaseObject.providedBy(img) or IBlobWrapper.providedBy(img): checked = True if not checked: raise except: # try to see if it is an old OFS.image if not isinstance(img, Image): raise ValueError( 'This is not a plone Image.')
def __call__(self, text, file_path=None): matches = SRC_PATTERN.findall(text) for match in set(matches): match_path = match.strip('"').strip("'").replace( '../', '').replace('%20', ' ').lstrip('/').encode('utf-8') obj = self.context.unrestrictedTraverse(match_path, None) ext = match_path.rsplit('.', 1) ext = ext in ('png', 'jpg', 'gif', 'jpeg') and ext or 'jpg' if obj and isinstance(obj, ATImage): text = text.replace(match_path, match_path + '/image.%s' % ext) if hasattr(obj, 'getBlobWrapper'): if 'image' in obj.getBlobWrapper().getContentType(): text = text.replace(match_path, match_path + '/image.%s' % ext) if not obj: try: path, filename = match_path.rsplit('/', 1) except ValueError: continue fieldname = filename.split('_', 1)[0] obj = self.context.restrictedTraverse( '/'.join((path, fieldname)), None) if PLONE_APP_BLOB_INSTALLED and IBlobWrapper.providedBy(obj): text = text.replace(match_path, match_path + '/image.jpg') if not obj: if '/@@images/' in match_path: parent_path, image_name = match_path.split( '/@@images/') spl_img_name = image_name.split('/') if len(spl_img_name) == 1: # no scalename in path fieldname = spl_img_name new_path = '/'.join((parent_path, 'image.jpg')) else: # scalename in path fieldname, scalename = spl_img_name new_path = '/'.join((parent_path, '_'.join( (fieldname, scalename)))) text = text.replace(match_path, new_path + '/image.jpg') return text
def get_object(self, attrs=[]): skeleton = self.get_skeleton(attrs, just_keys=True) for k in skeleton.keys(): if self.context[k] == None: skeleton[k] = None elif isinstance(self.context[k], BaseUnit): # -- it's worse than the x-ray shows -- # brittle_bones, tisk tisk # # Archetypes' BaseUnit subclasses OFS's File, which has the # _properties attribute, but it loses its 'id' and # 'content_type' ??? skeleton[k] = self.context[k].getRaw() elif isinstance(self.context[k], File): skeleton[k] = self._gather_file_data(k) elif has_blob_support and IBlobWrapper.providedBy(self.context[k]): skeleton[k] = self._gather_blob_data(k) else: skeleton[k] = self.context[k] return skeleton
def migrate_lead_image_into_textblock(old_page, new_page): image = old_page.getImage() if not image or not image.get_size(): # no lead image return assert IBlobWrapper.providedBy(image), \ 'Unexpectedly, the teaser image "{!r}" is not an IBlobWrapper.'.format( image) teaser_block = createContentInContainer( container=new_page, portal_type='ftw.simplelayout.TextBlock', title='Teaser' ) fields = dict(reduce(list.__add__, map(getFieldsInOrder, iterSchemata(teaser_block)))) fields['show_title'].set(teaser_block, False) fields['image_alt_text'].set( teaser_block, old_page.getImageAltText().decode('utf-8')) fields['image_caption'].set( teaser_block, old_page.getImageCaption().decode('utf-8')) fields['open_image_in_overlay'].set( teaser_block, old_page.getImageClickable()) new_image = fields['image']._type( data='', contentType=image.content_type, filename=image.filename.decode('utf-8')) new_image._blob = image.getBlob() fields['image'].set(teaser_block, new_image) # Since the teaser was stored on the *page*, the image layout of the # teaser is stored in the block-config of the page! # Therefore we must migrate the view from the page to the new # teaser block. migrate_sl_image_layout(old_page, teaser_block) alsoProvides(teaser_block, ISlotA) move_sl_block_into_slot(old_page, new_page, teaser_block, 'default')
def fieldSerialization(self, field, value): """ Custom serialization for fields which provide field values that are incompatible with json / JSON-standard. @param field: Field-Object from Schema @type field: Field @param value: Return-Value of the Raw-Accessor of the Field on the current context @type value: string or stream @return: JSON-optimized value @rtype: string """ if isinstance(field, DateTimeField) and value: value = str(value) elif HAS_BLOBS and IBlobWrapper.providedBy(value): file_ = value.getBlob().open() value = { 'filename': value.getFilename(), 'data': base64.encodestring(file_.read()), 'type': 'blob' } file_.close() elif isinstance(field, FileField) and isinstance(value, File): tmp = StringIO.StringIO(value.data) tmp.seek(0) value = { 'filename': value.filename, 'data': base64.encodestring(tmp.read()) } elif isinstance(field, QueryField): query = field.getRaw(self.object) # Cast "ZPublisher.HTTPRequest.record" instance to dict value = [dict(item) for item in query] return value
def __call__(self, text, file_path=None): matches = SRC_PATTERN.findall(text) for match in set(matches): match_path = match.strip('"').strip("'").replace('../', '').replace('%20', ' ').lstrip('/').encode('utf-8') obj = self.context.unrestrictedTraverse(match_path, None) ext = match_path.rsplit('.', 1)[-1] if ext and ext.endswith('/view'): ext = ext[:-5] ext = ext in ('png', 'jpg', 'gif', 'jpeg') and ext or 'jpg' if obj and isinstance(obj, ATImage): text = text.replace(match_path, match_path + '/image.%s' % ext) if hasattr(obj, 'getBlobWrapper'): if 'image' in obj.getBlobWrapper().getContentType(): text = text.replace(match_path, match_path + '/image.%s' % ext) if not obj: try: path, filename = match_path.rsplit('/', 1) except ValueError: continue fieldname = filename.split('_', 1)[0] obj = self.context.restrictedTraverse('/'.join((path, fieldname)), None) if PLONE_APP_BLOB_INSTALLED and IBlobWrapper.providedBy(obj): text = text.replace(match_path, match_path + '/image.jpg') if not obj: if '/@@images/' in match_path: parent_path, image_name = match_path.split('/@@images/') spl_img_name = image_name.split('/') if len(spl_img_name) == 1: # no scalename in path fieldname = spl_img_name new_path = '/'.join((parent_path, 'image.jpg')) else: # scalename in path fieldname, scalename = spl_img_name new_path = '/'.join((parent_path, '_'.join((fieldname, scalename)))) text = text.replace(match_path, new_path + '/image.jpg') return text
def _render_obj(self, obj): """ Render object to string. """ if isinstance(obj, basestring): return obj ## 'plone.global_sections' viewlet uses request['URL'] highlight ## selected tab, so it must be overridden but only for a while initial_url = self.request['URL'] try: obj_url = obj.absolute_url() except AttributeError: try: obj_url = obj.context.absolute_url() except AttributeError: obj_url = None if obj_url: self.request['URL'] = obj_url ## breadcrumb implementation in quills uses 'PARENTS', so it must ## be overriden but ony for a while initial_parents = self.request['PARENTS'] if hasattr(obj, 'aq_chain'): self.request['PARENTS'] = obj.aq_chain try: if IResource.providedBy(obj): try: f = open(obj.context.path) result = f.read() f.close() except IOError: log.error("Couldn't open '%s' file with resource" % obj.context.path) return None return result if isinstance(obj, (BrowserView, FSPageTemplate, PythonScript)): try: return obj() except NotFound: log.error("Resource '%s' not found" % repr(obj)) return None if isinstance(obj, (FSFile, FSImage)): return self._render_obj(obj._readFile(None)) if isinstance(obj, FSDTMLMethod): return self._render_obj(obj.read()) mt = None try: mt = obj.aq_base.meta_type except AttributeError: pass if mt in self.file_types or isinstance(obj, (ImageField, OFSImage, Pdata, File)): return self._render_obj(obj.data) if PLONE_RESOURCE_INSTALLED and isinstance(obj, FilesystemFile): if not obj.request: obj.request = self.request return obj().read() if PLONE_APP_BLOB_INSTALLED and IBlobWrapper.providedBy(obj): return obj.data if IBaseObject.providedBy(obj) or isinstance(obj, PloneSite): def_page_id = obj.getDefaultPage() if def_page_id: def_page = obj[def_page_id] return self._render_obj(def_page) view_name = obj.getLayout() view = queryMultiAdapter((obj, self.request), name=view_name) if view_name == 'language-switcher': lang = self.request.get('LANGUAGE') def_page = getattr(obj, lang, None) if def_page: return self._render_obj(def_page) if view: try: return view.context() except (ContentProviderLookupError, TypeError): pass view = obj.restrictedTraverse(view_name, None) if view: try: return view.context() except (AttributeError, TypeError): try: return view() except Exception, error: log.warning("Unable to render view: '%s'! Error occurred: %s" % (view, error)) pass else: try: return obj() except AttributeError: pass finally: ## back to initial url if obj_url: self.request['URL'] = initial_url ## back to initial parents self.request['PARENTS'] = initial_parents log.warning("Not recognized object '%s'!" % repr(obj)) return None
def __call__(self, text, file_path=None): dom = getDom(text) if not dom: return text for link in dom.cssselect('a[href],img[src]'): link = LinkElement(link) url = link.val.rstrip('/') match_path = url.replace('%20', ' ').lstrip('/') if type(match_path) == unicode: match_path = match_path.encode('utf-8') obj = self.context.unrestrictedTraverse(match_path, None) ext = match_path.split('.')[-1].lower() ext = ext in ('png', 'jpg', 'gif', 'jpeg') and ext or 'jpg' if obj and isinstance(obj, ATImage) or ( hasattr(obj, 'getBlobWrapper') and \ 'image' in obj.getBlobWrapper().getContentType()): link.set(url + '/image.%s' % ext) elif obj and isinstance(obj, ArchetypesImage): # it's a scale, always use image.jpg extension link.set(url + '/image.jpg') if not obj: try: path, filename = match_path.rsplit('/', 1) except ValueError: continue fieldname = filename.split('_', 1)[0] obj = self.context.restrictedTraverse('/'.join((path, fieldname)), None) if not obj: # not all fields are traversable obj = self.context.restrictedTraverse(path, None) if IImageScaling.providedBy(obj): # we can't do anything with the @@images view yet here... obj = None if obj and hasattr(obj, 'getField'): field = obj.getField(fieldname) if field: obj = field.get(obj) if PLONE_APP_BLOB_INSTALLED and IBlobWrapper.providedBy(obj): link.set(url + '/image.jpg') if not obj: if '/@@images/' in match_path: parent_path, image_name = match_path.split('/@@images/') spl_img_name = image_name.split('/') if len(spl_img_name) == 1: # no scalename in path uid = spl_img_name[0] if '-' in uid: # seems to be actual uid for a custom scale here... # it should written out as [uid].[ext] new_path = '/'.join((parent_path, uid)) else: # just use original if we can't figure this out... new_path = '/'.join((parent_path, 'image.%s' % ext)) else: # scalename in path fieldname, scalename = spl_img_name new_path = '/'.join((parent_path, '_'.join((fieldname, scalename)))) new_path = new_path + '/image.jpg' new_path = '/' + new_path.lstrip('/') link.set(new_path) return unicode(dom)
def prepare_field_value(self, new_object, field, value): recurse = partial(self.prepare_field_value, new_object, field) if isinstance(value, str): return recurse(value.decode('utf-8')) if isinstance(value, list): return map(recurse, value) if isinstance(value, tuple): return tuple(map(recurse, value)) relation_fields = filter(IRelationChoice.providedBy, (field, getattr(field, 'value_type', None))) if relation_fields and isinstance(value, unicode): target = uuidToObject(value) return create_relation('/'.join(target.getPhysicalPath())) if IRichText.providedBy(field) \ and not IRichTextValue.providedBy(value): return recurse(field.fromUnicode(value)) if INamedField.providedBy(field) and value \ and not isinstance(value, field._type): source_is_blobby = IBlobWrapper.providedBy(value) target_is_blobby = INamedBlobFileField.providedBy(field) or \ INamedBlobImageField.providedBy(field) if source_is_blobby and target_is_blobby: filename = value.filename if isinstance(filename, str): filename = filename.decode('utf-8') new_value = field._type( data='', # empty blob, will be replaced contentType=value.content_type, filename=filename) if not hasattr(new_value, '_blob'): raise ValueError( ('Unsupported file value type {!r}' ', missing _blob.').format( new_value.__class__)) # Simply copy the persistent blob object (with the file system # pointer) to the new value so that the file is not copied. # We assume that the old object is trashed and can therefore # adopt the blob file. new_value._blob = value.getBlob() return recurse(new_value) else: filename = value.filename if isinstance(filename, str): filename = filename.decode('utf-8') data = value.data data = getattr(data, 'data', data) # extract Pdata return recurse(field._type( data=data, contentType=value.content_type, filename=filename)) return value
def prepare_field_value(self, new_object, field, value): recurse = partial(self.prepare_field_value, new_object, field) if isinstance(value, str): return recurse(value.decode('utf-8')) if isinstance(value, list): return map(recurse, value) if isinstance(value, tuple): return tuple(map(recurse, value)) relation_fields = filter(IRelation.providedBy, (field, getattr(field, 'value_type', None))) if relation_fields and isinstance(value, unicode): target = uuidToObject(value) return create_relation('/'.join(target.getPhysicalPath())) if IRichText.providedBy(field) \ and not IRichTextValue.providedBy(value): return recurse(field.fromUnicode(value)) if INamedField.providedBy(field) and value is not None \ and not isinstance(value, field._type): if value == '': return None if hasattr(value, 'get_size') and value.get_size() == 0: return None source_is_blobby = IBlobWrapper.providedBy(value) target_is_blobby = INamedBlobFileField.providedBy(field) or \ INamedBlobImageField.providedBy(field) if source_is_blobby and target_is_blobby: filename = value.filename if isinstance(filename, str): filename = filename.decode('utf-8') new_value = field._type( data='', # empty blob, will be replaced contentType=value.content_type, filename=filename) if not hasattr(new_value, '_blob'): raise ValueError( ('Unsupported file value type {!r}' ', missing _blob.').format( new_value.__class__)) # Simply copy the persistent blob object (with the file system # pointer) to the new value so that the file is not copied. # We assume that the old object is trashed and can therefore # adopt the blob file. new_value._blob = value.getBlob() return recurse(new_value) else: filename = value.filename if isinstance(filename, str): filename = filename.decode('utf-8') data = value.data data = getattr(data, 'data', data) # extract Pdata return recurse(field._type( data=data, contentType=value.content_type, filename=filename)) return value
def _render_obj(self, obj, new_req=None): """ Render object to string. """ if isinstance(obj, basestring): return obj if new_req is None: new_req = self.request try: if IResource.providedBy(obj): try: f = open(obj.context.path) result = f.read() f.close() except AttributeError: result = obj.context.data except IOError: log.error("Couldn't open '%s' file with resource" % (obj.context.path)) return None return result if isinstance(obj, (BrowserView, FSPageTemplate, PythonScript)): try: return obj() except NotFound: log.error("Resource '%s' not found" % repr(obj)) return None if isinstance(obj, (FSFile, FSImage)): return self._render_obj(obj._readFile(None)) if isinstance(obj, FSDTMLMethod): return self._render_obj(obj.read()) mt = None try: mt = obj.aq_base.meta_type except AttributeError: pass if mt in self.file_types or isinstance(obj, (ImageField, OFSImage, Pdata, File)): return self._render_obj(obj.data) if PLONE_RESOURCE_INSTALLED and isinstance(obj, FilesystemFile): if not obj.request: obj.request = new_req return obj().read() if PLONE_APP_BLOB_INSTALLED and IBlobWrapper.providedBy(obj): return obj.data if IBaseObject.providedBy(obj) or isinstance(obj, PloneSite): def_page_id = obj.getDefaultPage() if def_page_id: def_page = obj[def_page_id] return self._render_obj(def_page) view_name = obj.getLayout() view = queryMultiAdapter((obj, new_req), name=view_name) if view_name == "language-switcher": lang = new_req.get("LANGUAGE") def_page = getattr(obj, lang, None) if def_page: return self._render_obj(def_page) if view: try: return view.context() except (ContentProviderLookupError, TypeError): pass view = obj.restrictedTraverse(view_name, None) if view: try: return view.context() except (AttributeError, TypeError): try: return view() except Exception, error: log.warning("Unable to render view: '%s'! Error occurred: %s" % (view, error)) else: try: return obj() except AttributeError: pass finally: pass log.warning("Not recognized object '%s'!" % repr(obj)) return None
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, )