def addRelations(obj, event): """Register relations. Any relation object on the object will be added. """ for name, relation in _relations(obj): rel_event._setRelation(obj, name, relation)
def migrate_linkintegrity_relations(context): """Migrate linkintegrity-relation from reference_catalog to zc.relation. """ reference_catalog = getToolByName(context, REFERENCE_CATALOG, None) intids = getUtility(IIntIds) if reference_catalog is not None: # Only handle linkintegrity-relations ('isReferencing'). # [:] copies the full result list to make sure # it won't change while we delete references below for brain in reference_catalog(relationship=referencedRelationship)[:]: try: source_obj = uuidToObject(brain.sourceUID) target_obj = uuidToObject(brain.targetUID) except AttributeError: source_obj = target_obj = None if source_obj is None or target_obj is None: # reference_catalog may be inconsistent log.info( 'Cannot delete relation since the relation_catalog is inconsistent.' ) # noqa: E501 continue # Delete old reference reference_catalog.deleteReference( source_obj, target_obj, relationship=referencedRelationship) # Trigger the recreation of linkintegrity-relation in # the relation_catalog (zc.relation) target_id = ensure_intid(target_obj, intids) if target_id is None: continue rel = RelationValue(target_id) _setRelation(source_obj, referencedRelationship, rel)
def updateReferences(obj, refs): """Renew all linkintegritry-references. Search the zc.relation catalog for linkintegritry-references for this obj. Drop them all and set the new ones. TODO: Might be improved by not changing anything if the links are the same. """ intids = getUtility(IIntIds) try: int_id = intids.getId(obj) except KeyError: # In some cases a object is not yet registered by the intid catalog try: int_id = intids.register(obj) except NotYet: return catalog = getUtility(ICatalog) # unpack the rels before deleting old_rels = [ i for i in catalog.findRelations({ 'from_id': int_id, 'from_attribute': referencedRelationship }) ] for old_rel in old_rels: catalog.unindex(old_rel) for ref in refs: _setRelation(obj, referencedRelationship, ref)
def updateReferences(obj, refs): """Renew all linkintegritry-references. Search the zc.relation catalog for linkintegritry-references for this obj. Drop them all and set the new ones. TODO: Might be improved by not changing anything if the links are the same. """ intids = getUtility(IIntIds) try: int_id = intids.getId(obj) except KeyError: # In some cases a object is not yet registered by the intid catalog try: int_id = intids.register(obj) except NotYet: return catalog = getUtility(ICatalog) # unpack the rels before deleting old_rels = [i for i in catalog.findRelations( {'from_id': int_id, 'from_attribute': referencedRelationship})] for old_rel in old_rels: catalog.unindex(old_rel) for ref in refs: _setRelation(obj, referencedRelationship, ref)
def add_behavior_relations(obj, event): """Register relations in behaviors. This event handler fixes a bug in plone.app.relationfield, which only updates the zc.catalog when an object gets modified, but not when it gets added. """ for behavior_interface, name, relation in extract_relations(obj): _setRelation(obj, name, relation)
def copyTo(self, container): context = aq_inner(self.context) wc = self._copyBaseline(container) # get id of objects intids = component.getUtility(IIntIds) wc_id = intids.getId(wc) # create a relation relation = StagingRelationValue(wc_id) event._setRelation(context, ITERATE_RELATION_NAME, relation) return wc, relation
def add_relations_to_relation_catalog(self, old_object, new_object): for behavior_interface, name, relation in extract_relations(new_object): if isinstance(relation, (str, unicode)): # We probably got a UID, but we are working with intids # and can not do anything with it, so we skip it. LOG.warning('Got a invalid relation ({!r}), which is not ' 'z3c.relationfield compatible.'.format(relation)) continue _setRelation(new_object, name, relation)
def test_references_dx_to_at(self): at = create(Builder('page').titled('AT')) dx = create(Builder('example dx type').titled(u'DX')) at_relation = create_relation('/'.join(at.getPhysicalPath())) IRelatedItems(dx).relatedItems = [at_relation] _setRelation(dx, 'relatedItems', at_relation) self.assertEquals( [at], list(get_state(dx).get_references()) )
def test_references_dx_to_dx(self): foo = create(Builder('example dx type').titled(u'Foo')) bar = create(Builder('example dx type').titled(u'Bar')) foo_relation = create_relation('/'.join(foo.getPhysicalPath())) IRelatedItems(bar).relatedItems = [foo_relation] _setRelation(bar, 'relatedItems', foo_relation) self.assertEquals( [foo], list(get_state(bar).get_references()) )
def add_relations_to_relation_catalog(self, old_object, new_object): for behavior_interface, name, relation in extract_relations( new_object): if isinstance(relation, (str, six.text_type)): # We probably got a UID, but we are working with intids # and can not do anything with it, so we skip it. LOG.warning('Got a invalid relation ({!r}), which is not ' 'z3c.relationfield compatible.'.format(relation)) continue _setRelation(new_object, name, relation)
def copyTo( self, container ): context = aq_inner( self.context ) wc = self._copyBaseline( container ) # get id of objects intids = component.getUtility( IIntIds ) wc_id = intids.getId( wc ) # create a relation relation = StagingRelationValue( wc_id ) event._setRelation( context, STAGING_RELATION_NAME, relation ) # self._handleReferences( self.context, wc, 'checkout', relation ) return wc, relation
def toFieldValue(self, value): if not value: return value intids = component.queryUtility(IIntIds) for row in value: for key in row: if row[key] and IRelationChoice.providedBy(self.field.value_type.schema[key]): to_id = intids.queryId(row[key]) if to_id: relation = RelationValue(to_id) _setRelation(row[key], key, relation) row[key] = relation return value
def updateReferences(obj, refs): """Renew all linkintegritry-references. Search the zc.relation catalog for linkintegritry-references for this obj. Drop them all and set the new ones. TODO: Might be improved by not changing anything if the links are the same. """ int_id = ensure_intid(obj) if int_id is None: return catalog = getUtility(ICatalog) # unpack the rels before deleting old_rels = [i for i in catalog.findRelations( {'from_id': int_id, 'from_attribute': referencedRelationship})] for old_rel in old_rels: catalog.unindex(old_rel) for ref in refs: _setRelation(obj, referencedRelationship, ref)
def create_media_base_path(obj, event): """automatically create related base path""" create = api.portal.get_registry_record( "collective.behavior.relatedmedia.create_media_container_base_paths", default=False, ) if (not create or obj.related_media_base_path or getattr(obj.REQUEST, "translation_info", {})): # do not create or we already have a value or we create a translation just return return media_root = get_media_root(obj) if media_root is None: # do nothing ... no media root path is defined return # we use UID for media container id to avoid duplicate ids in media root media_base_id = obj.UID() if media_base_id not in media_root: # create base path media_base = createContentInContainer( media_root, "Folder", id=media_base_id, title=obj.Title(), ) else: # XXX: this should never happen? media_base = media_root[media_base_id] _rel = create_relation("/".join(media_base.getPhysicalPath())) # fix RelationValue properties _setRelation(obj, "related_media_base_path", _rel) obj.related_media_base_path = _rel
def test_delete_confirmation_for_any_reference(self): """Test, if delete confirmation shows also a warning if items are deleted, which are referenced by other items via a reference field. """ img1 = self.portal['image1'] doc1 = self.portal['doc1'] intids_tool = getUtility(IIntIds) to_id = intids_tool.getId(img1) rel = RelationValue(to_id) _setRelation(doc1, 'related_image', rel) # Test, if relation is present in the relation catalog catalog = getUtility(ICatalog) rels = list(catalog.findRelations({'to_id': to_id})) self.assertEqual(len(rels), 1) # Test, if delete_confirmation_info shows also other relations than # ``isReferencing``. info = img1.restrictedTraverse('@@delete_confirmation_info') breaches = info.get_breaches() self.assertEqual(len(breaches), 1) self.assertEqual(len(info.get_breaches()[0]['sources']), 1)
def link_objects(source, target, relationship): """Create a relation from source to target using zc.relation For RelationChoice or RelationList it will add the relation as attribute. Other relations they will only be added to the relation-catalog. """ if not IDexterityContent.providedBy(source): logger.info(u'{} is no dexterity content'.format(source.portal_type)) return if not IDexterityContent.providedBy(target): logger.info(u'{} is no dexterity content'.format(target.portal_type)) return relation_catalog = getUtility(ICatalog) intids = getUtility(IIntIds) to_id = intids.getId(target) from_id = intids.getId(source) from_attribute = relationship # Check if there is exactly this relation. # If so remove it and create a fresh one. query = { 'from_attribute': from_attribute, 'from_id': from_id, 'to_id': to_id, } for rel in relation_catalog.findRelations(query): relation_catalog.unindex(rel) if from_attribute == referencedRelationship: # Don't mess with linkintegrity-relations! # Refresh them by triggering this subscriber. modifiedContent(source, None) return if from_attribute == ITERATE_RELATION_NAME: # Iterate relations use a subclass of RelationValue relation = StagingRelationValue(to_id) event._setRelation(source, ITERATE_RELATION_NAME, relation) return field_and_schema = get_field_and_schema_for_fieldname( from_attribute, source.portal_type) if field_and_schema is None: # The relationship is not the name of a field. Only create a relation. logger.info(u'No field. Setting relation {} from {} to {}'.format( source.absolute_url(), target.absolute_url(), relationship)) event._setRelation(source, from_attribute, RelationValue(to_id)) return field, schema = field_and_schema if isinstance(field, RelationList): logger.info('Add relation to relationlist {} from {} to {}'.format( from_attribute, source.absolute_url(), target.absolute_url())) existing_relations = getattr(source, from_attribute, []) existing_relations.append(RelationValue(to_id)) setattr(source, from_attribute, existing_relations) modified(source) return elif isinstance(field, (Relation, RelationChoice)): logger.info('Add relation {} from {} to {}'.format( from_attribute, source.absolute_url(), target.absolute_url())) setattr(source, from_attribute, RelationValue(to_id)) modified(source) return # We should never end up here! logger.info('Warning: Unexpected relation {} from {} to {}'.format( from_attribute, source.absolute_url(), target.absolute_url()))
def restore_relations(context=None, all_relations=None): """Restore relations from a annotation on the portal. """ portal = api.portal.get() if all_relations is None: all_relations = IAnnotations(portal)[RELATIONS_KEY] logger.info('Loaded {0} relations to restore'.format(len(all_relations))) update_linkintegrity = set() modified_items = set() modified_relation_lists = defaultdict(list) # remove duplicates but keep original order unique_relations = [] seen = set() seen_add = seen.add for i in all_relations: hashable = tuple(i.items()) if hashable not in seen: unique_relations.append(i) seen_add(hashable) else: logger.info(u'Dropping duplicate: {}'.format(hashable)) if len(unique_relations) < len(all_relations): logger.info('Dropping {0} duplicates'.format( len(all_relations) - len(unique_relations))) all_relations = unique_relations intids = getUtility(IIntIds) for index, item in enumerate(all_relations, start=1): if not index % 500: logger.info(u'Restored {} of {} relations...'.format( index, len(all_relations))) source_obj = uuidToObject(item['from_uuid']) target_obj = uuidToObject(item['to_uuid']) if not source_obj: logger.info(u'{} is missing'.format(item['from_uuid'])) continue if not target_obj: logger.info(u'{} is missing'.format(item['to_uuid'])) continue if not IDexterityContent.providedBy(source_obj): logger.info(u'{} is no dexterity content'.format( source_obj.portal_type)) continue if not IDexterityContent.providedBy(target_obj): logger.info(u'{} is no dexterity content'.format( target_obj.portal_type)) continue from_attribute = item['from_attribute'] to_id = intids.getId(target_obj) if from_attribute == referencedRelationship: # Ignore linkintegrity for now. We'll rebuilt it at the end! update_linkintegrity.add(item['from_uuid']) continue if from_attribute == ITERATE_RELATION_NAME: # Iterate relations are not set as values of fields relation = StagingRelationValue(to_id) event._setRelation(source_obj, ITERATE_RELATION_NAME, relation) continue field_and_schema = get_field_and_schema_for_fieldname( from_attribute, source_obj.portal_type) if field_and_schema is None: # the from_attribute is no field # we could either create a fresh relation or log the case logger.info(u'No field. Setting relation: {}'.format(item)) event._setRelation(source_obj, from_attribute, RelationValue(to_id)) continue field, schema = field_and_schema relation = RelationValue(to_id) if isinstance(field, RelationList): logger.info( 'Add relation to relationslist {} from {} to {}'.format( from_attribute, source_obj.absolute_url(), target_obj.absolute_url())) if item['from_uuid'] in modified_relation_lists.get( from_attribute, []): # Do not purge relations existing_relations = getattr(source_obj, from_attribute, []) else: # First touch. Make sure we purge! existing_relations = [] existing_relations.append(relation) setattr(source_obj, from_attribute, existing_relations) modified_items.add(item['from_uuid']) modified_relation_lists[from_attribute].append(item['from_uuid']) continue elif isinstance(field, (Relation, RelationChoice)): logger.info('Add relation {} from {} to {}'.format( from_attribute, source_obj.absolute_url(), target_obj.absolute_url())) setattr(source_obj, from_attribute, relation) modified_items.add(item['from_uuid']) continue else: # we should never end up here! logger.info('Warning: Unexpected relation {} from {} to {}'.format( from_attribute, source_obj.absolute_url(), target_obj.absolute_url())) update_linkintegrity = set(update_linkintegrity) logger.info('Updating linkintegrity for {} items'.format( len(update_linkintegrity))) for uuid in sorted(update_linkintegrity): modifiedContent(uuidToObject(uuid), None) logger.info('Updating relations for {} items'.format(len(modified_items))) for uuid in sorted(modified_items): obj = uuidToObject(uuid) # updateRelations from z3c.relationfield does not properly update relations in behaviors # that are registered with a marker-interface. # update_behavior_relations (from plone.app.relationfield) does that but does not update # those in the main schema. Duh! updateRelations(obj, None) update_behavior_relations(obj, None) # purge annotation from portal if they exist if RELATIONS_KEY in IAnnotations(portal): del IAnnotations(portal)[RELATIONS_KEY] logger.info('Done!')
def update_behavior_relations(obj, event): """Re-register relations in behaviors """ for behavior_interface, name, relation in extract_relations(obj): _setRelation(obj, name, relation)
def add_relations_to_relation_catalog(self, old_object, new_object): for behavior_interface, name, relation in extract_relations(new_object): _setRelation(new_object, name, relation)
def updateRelations(obj, event): storage = IWCAnnotator(obj) if storage: _setRelation(obj, STAGING_RELATION_NAME, storage.get_relation())
def restore_relations(context=None, all_relations=None): """Restore relations from a annotation on the portal. """ portal = api.portal.get() if all_relations is None: all_relations = IAnnotations(portal)[RELATIONS_KEY] logger.info('Loaded {0} relations to restore'.format(len(all_relations))) update_linkintegrity = [] modified_items = [] modified_relation_lists = defaultdict(list) # remove duplicates but keep original order seen = set() seen_add = seen.add unique_relations = [ i for i in all_relations if not (tuple(i.items()) in seen or seen_add(tuple(i.items()))) ] if len(unique_relations) < len(all_relations): logger.info('Dropping {0} duplicates'.format( len(all_relations) - len(unique_relations))) all_relations = unique_relations intids = getUtility(IIntIds) for item in all_relations: source_obj = uuidToObject(item['from_uuid']) target_obj = uuidToObject(item['to_uuid']) if not source_obj: logger.info(u'{} is missing'.format(item['from_uuid'])) continue if not target_obj: logger.info(u'{} is missing'.format(item['to_uuid'])) continue if not IDexterityContent.providedBy(source_obj): logger.info(u'{} is no dexterity content'.format( source_obj.portal_type)) continue if not IDexterityContent.providedBy(target_obj): logger.info(u'{} is no dexterity content'.format( target_obj.portal_type)) continue from_attribute = item['from_attribute'] to_id = intids.getId(target_obj) if from_attribute == referencedRelationship: # Ignore linkintegrity for now. We'll rebuilt it at the end! update_linkintegrity.append(item['from_uuid']) continue if from_attribute == ITERATE_RELATION_NAME: # Iterate relations are not set as values of fields relation = StagingRelationValue(to_id) event._setRelation(source_obj, ITERATE_RELATION_NAME, relation) continue fti = getUtility(IDexterityFTI, name=source_obj.portal_type) field_and_schema = get_field_and_schema_for_fieldname( from_attribute, fti) if field_and_schema is None: # the from_attribute is no field # we could either create a fresh relation or log the case logger.info(u'No field. Setting relation: {}'.format(item)) event._setRelation(source_obj, from_attribute, RelationValue(to_id)) continue field, schema = field_and_schema relation = RelationValue(to_id) if isinstance(field, RelationList): logger.info( 'Add relation to relationslist {} from {} to {}'.format( from_attribute, source_obj.absolute_url(), target_obj.absolute_url())) if item['from_uuid'] in modified_relation_lists.get( from_attribute, []): # Do not purge relations existing_relations = getattr(source_obj, from_attribute, []) else: # First touch. Make sure we purge! existing_relations = [] existing_relations.append(relation) setattr(source_obj, from_attribute, existing_relations) modified_items.append(item['from_uuid']) modified_relation_lists[from_attribute].append(item['from_uuid']) continue elif isinstance(field, (Relation, RelationChoice)): logger.info('Add relation {} from {} to {}'.format( from_attribute, source_obj.absolute_url(), target_obj.absolute_url())) setattr(source_obj, from_attribute, relation) modified_items.append(item['from_uuid']) continue else: # we should never end up here! logger.info('Warning: Unexpected relation {} from {} to {}'.format( from_attribute, source_obj.absolute_url(), target_obj.absolute_url())) to_update = set(update_linkintegrity + modified_items) if to_update: logger.info('Reindexing {} items'.format(len(to_update))) for uuid in sorted(to_update): # call modified for all changed items modifiedContent(uuidToObject(uuid), None) # purge annotation from portal if they exist if RELATIONS_KEY in IAnnotations(portal): del IAnnotations(portal)[RELATIONS_KEY]