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 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 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]