def test_renamecontainerKeepsReferences(self): # test for #956677: renaming the container causes contained objects # to lose their refs container = makeContent(self.folder, portal_type=self.FOLDER_TYPE, title='Spam', id='container') obj1 = makeContent(container, portal_type='SimpleType', title='Eggs', id='obj1') obj2 = makeContent(self.folder, portal_type='SimpleType', title='Foo', id='obj2') obj1.addReference(obj2) a, b = self.verifyBrains() transaction.savepoint(optimistic=True) self.assertEqual(obj2.getBRefs(), [obj1]) self.assertEqual(obj1.getRefs(), [obj2]) self.folder.manage_renameObject(id='container', new_id='cont4iner') c, d = self.verifyBrains() obj1 = self.folder.cont4iner.obj1 obj2 = self.folder.cont4iner.obj2 self.assertEqual(obj2.getBRefs(), [obj1]) self.assertEqual(obj1.getRefs(), [obj2])
def optimize_rangeindex_floor_ceiling(index): # respect the new ceiling and floor values logger.info("Optimizing range index `%s` to respect floor and ceiling " "dates" % index.getId()) ceiling_value = index.ceiling_value floor_value = index.floor_value _insertForwardIndexEntry = index._insertForwardIndexEntry _removeForwardIndexEntry = index._removeForwardIndexEntry _unindex = index._unindex i = 0 for docid, datum in _unindex.iteritems(): if datum == (None, None): continue since, until = datum changed = False if since is not None and since < floor_value: since = None changed = True if until is not None and until > ceiling_value: until = None changed = True if changed: _removeForwardIndexEntry(datum[0], datum[1], docid) _insertForwardIndexEntry(since, until, docid) # we only change the value and not the keys of the btree, so we # safely iterate over it while modifying it _unindex[docid] = (since, until) i += 1 if i % 10000 == 0: logger.info("Processed %s items." % i) transaction.savepoint(optimistic=True) transaction.savepoint(optimistic=True) logger.info("Finished range index optimization.")
def convert_to_uuidindex(catalog, index): if isinstance(index, UUIDIndex): return logger.info('Converting index `%s` to UUIDIndex.' % index.getId()) index.__class__ = UUIDIndex index._p_changed = True catalog._catalog._p_changed = True # convert from OOBTree to OIBTree old_index = index._index if not isinstance(old_index, OIBTree): index._index = _index = OIBTree() for k, v in old_index.items(): if isinstance(v, int): _index[k] = v else: if isinstance(v, (IISet, IITreeSet)): # inconsistent data, one uid with multiple docids paths = dict((tuple(catalog.getpath(k).split('/')), k) for k in v.keys()) shortest = min(paths) for path, key in paths.iteritems(): if path[:len(shortest)] != shortest: raise ValueError( 'Inconsistent UID index, UID %s is associated ' 'with multiple docids: %r' % (k, paths)) # All other docids are sub-paths of another # indicating the UID was just acquired, # choose the shortest _index[k] = paths[shortest] del old_index transaction.savepoint(optimistic=True) logger.info('Finished conversion.')
def install(self, reinstall=False): out = StringIO() portal_quickinstaller = getToolByName(self, 'portal_quickinstaller') portal_setup = getToolByName(self, 'portal_setup') install_subskin(self, out, GLOBALS) # The skins need to be sorted differently depending on whether Azax # is available or not. sort_skins(self) if not hasattr(self, TOOL_ID): from Products.CompositePack.tool import manage_addCompositeTool manage_addCompositeTool(self) for extension_id in EXTENSION_PROFILES: portal_setup.runAllImportStepsFromProfile( 'profile-%s' % extension_id, purge_old=False) product_name = extension_id.split(':')[0] portal_quickinstaller.notifyInstalled(product_name) transaction.savepoint() install_tool(self, out) install_customisation(self, out) install_fixuids(self, out) install_kupu_resource(self, out) out.write("Successfully installed %s.\n" % PROJECTNAME) return out.getvalue()
def test_UIDclash(self): catalog = getattr(self.portal, UID_CATALOG) obj_id = 'demodoc' new_id = 'new_demodoc' doc = makeContent(self.folder, portal_type='DDocument', title='Foo', id=obj_id) UID = doc.UID() # ensure object has a _p_jar transaction.savepoint(optimistic=True) self.folder.manage_renameObject(id=obj_id, new_id=new_id) # now, make a new one with the same ID and check it gets a different # UID doc2 = makeContent(self.folder, portal_type='DDocument', title='Foo', id=obj_id) UID2 = doc2.UID() self.assertFalse(UID == UID2) uniq = catalog.uniqueValuesFor('UID') self.assertTrue(UID in uniq, (UID, uniq)) self.assertTrue(UID2 in uniq, (UID, uniq))
def _renameAfterCreation(self, check_auto_id=False): """Renames an object like its normalized title. """ old_id = self.getId() if check_auto_id and not self._isIDAutoGenerated(old_id): # No auto generated id return False new_id = self.generateNewId() if new_id is None: return False invalid_id = True check_id = getattr(self, 'check_id', None) if check_id is not None: invalid_id = check_id(new_id, required=1) # If check_id told us no, or if it was not found, make sure we have an # id unique in the parent folder. if invalid_id: unique_id = self._findUniqueId(new_id) if unique_id is not None: if check_id is None or check_id(new_id, required=1): new_id = unique_id invalid_id = False if not invalid_id: # Can't rename without a subtransaction commit when using # portal_factory! transaction.savepoint(optimistic=True) self.setId(new_id) return new_id return False
def __iter__(self): count = 0 for item in self.previous: count = (count + 1) % self.every if count == 0: transaction.savepoint(optimistic=True) yield item
def test_move_comments_when_content_object_is_moved(self): # Create two folders and a content object with a comment self.portal.invokeFactory(id="folder1", title="Folder 1", type_name="Folder") self.portal.invokeFactory(id="folder2", title="Folder 2", type_name="Folder") self.portal.folder1.invokeFactory(id="moveme", title="Move Me", type_name="Document") conversation = IConversation(self.portal.folder1.moveme) comment = createObject("plone.Comment") comment_id = conversation.addComment(comment) # We need to commit here so that _p_jar isn't None and move will work transaction.savepoint(optimistic=True) # Move moveme from folder1 to folder2 cp = self.portal.folder1.manage_cutObjects(ids=("moveme",)) self.portal.folder2.manage_pasteObjects(cp) # Make sure no old comment brains are brains = self.catalog.searchResults( dict(portal_type="Discussion Item", path={"query": "/".join(self.portal.folder1.getPhysicalPath())}) ) self.assertEquals(len(brains), 0) brains = self.catalog.searchResults( dict(portal_type="Discussion Item", path={"query": "/".join(self.portal.folder2.getPhysicalPath())}) ) self.assertEquals(len(brains), 1) self.assertEquals(brains[0].getPath(), "/plone/folder2/moveme/++conversation++default/" + str(comment_id))
def test_index_html_with_304_and_caching(self): # See collector #355 cpm = DummyCachingManager() getSiteManager().registerUtility(cpm, ICachingPolicyManager) original_len = len(self.RESPONSE.headers) _path, ref = self._extractFile() self.app.image = self._makeOne('test_image', 'test_image.gif', file=ref) image = self.app.image transaction.savepoint(optimistic=True) mod_time = image.modified() self.REQUEST.environ['IF_MODIFIED_SINCE' ] = '%s;' % rfc1123_date(mod_time + 1) data = image.index_html(self.REQUEST, self.RESPONSE) self.assertEqual(data, '') self.assertEqual(self.RESPONSE.getStatus(), 304) headers = self.RESPONSE.headers self.failUnless(len(headers) >= original_len + 3) self.failUnless('foo' in headers.keys()) self.failUnless('bar' in headers.keys()) self.assertEqual(headers['test_path'], '/test_image')
def migrateFolders(context): from plone.app.folder.migration import BTreeMigrationView class MigrationView(BTreeMigrationView): def mklog(self): msgs = [] def log(msg, timestamp=True, cr=True): msgs.append(msg) if cr: logger.info(''.join(msgs)) msgs[:] = [] return log def postprocess(self, obj): # Recompile any Python script with stale code meta_type = getattr(aq_base(obj), 'meta_type', None) if meta_type == 'Script (Python)': if obj._v_change: obj._compile() obj._p_changed = 1 # Abuse this step to conveniently get rid of old persistent # uppercase Interface records if '__implements__' in obj.__dict__: del obj.__dict__['__implements__'] obj._p_changed = 1 portal = getToolByName(context, 'portal_url').getPortalObject() transaction.savepoint(optimistic=True) MigrationView(portal, None)()
def _initFolders( self ): from Products.CMFCore.PortalFolder import PortalFolder self.connection = makeConnection() try: r = self.connection.root() a = Application() r['Application'] = a self.root = a responseOut = self.responseOut = cStringIO.StringIO() self.app = makerequest( self.root, stdout=responseOut ) self.app._setObject( 'folder1', PortalFolder( 'folder1' ) ) self.app._setObject( 'folder2', PortalFolder( 'folder2' ) ) folder1 = getattr( self.app, 'folder1' ) folder2 = getattr( self.app, 'folder2' ) manage_addFile( folder1, 'file' , file='', content_type='text/plain') # Hack, we need a _p_mtime for the file, so we make sure that it # has one. We use a subtransaction, which means we can rollback # later and pretend we didn't touch the ZODB. transaction.savepoint(optimistic=True) except: self.connection.close() raise return self.app._getOb( 'folder1' ), self.app._getOb( 'folder2' )
def _install_zope(self, db): """Install a fresh Zope inside the new test DB. Eventually install an application afterwards. """ # Create the "application" newSecurityManager(None, AccessControl.User.system) connection = db.open() root = connection.root() root['Application'] = OFS.Application.Application() app = root['Application'] # Do a savepoint to get a _p_jar on the application transaction.savepoint() # Initialize the "application" try: TestAppInitializer( app, self.products, self.packages, self.users).initialize() self._install_application(makerequest( app, environ={'SERVER_NAME': 'localhost'})) except Exception as error: # There was an error during the application 'setUp'. Abort # the transaction and continue, otherwise test in other # layers might fail because of this failure. transaction.abort() raise error else: # Close transaction.commit() finally: # In any case, close the connection and continue connection.close() noSecurityManager()
def test_rename_updates_storage(self): self.folder.invokeFactory('Document', 'p1') transaction.savepoint(1) self.folder.manage_renameObject('p1', 'p2') fp = '/'.join(self.folder.getPhysicalPath()) self.assertEquals(self.storage.get(fp + '/p1'), fp + '/p2')
def removeBrokenCacheFu(context): portal = getToolByName(context, 'portal_url').getPortalObject() cpm = getattr(portal, 'caching_policy_manager', None) if cpm and cpm.getId() == 'broken': # If we detect a broken CacheFu install, remove it CACHEFU_IDS = [ 'CacheSetup_OFSCache', 'CacheSetup_PageCache', 'caching_policy_manager', 'HTTPCache', 'portal_cache_settings', 'portal_squid', ] cpm = aq_base(cpm) for i in CACHEFU_IDS: portal._delOb(i) objects = portal._objects portal._objects = tuple( [i for i in objects if getattr(portal, i['id'], None)]) sm = getSiteManager(context=portal) sm.unregisterUtility(component=cpm, provided=ICachingPolicyManager) del cpm transaction.savepoint(optimistic=True) manage_addCachingPolicyManager(portal) addCacheHandlers(portal) addCacheForResourceRegistry(portal) logger.info('Removed CacheSetup tools.')
def testRenameObject(self): # Renaming should not change position transaction.savepoint(optimistic=True) # make rename work self.portal.manage_renameObjects(['bar'], ['barney']) self.assertEqual(self.portal.getObjectPosition('foo'), 0) self.assertEqual(self.portal.getObjectPosition('barney'), 1) self.assertEqual(self.portal.getObjectPosition('baz'), 2)
def test_move_cant_delete_source( self ): # # This test fails on Zope's earlier than 2.7.3 because of the # changes required to 'OFS.CopytSupport.manage_pasteObjects' # which must pass 'validate_src' of 2 to '_verifyObjectPaste' # to indicate that the object is being moved, rather than # simply copied. # # If you are running with such a Zope, this test will fail, # because the move (which should raise Unauthorized) will be # allowed. # from AccessControl.Permissions import delete_objects as DeleteObjects from Products.CMFCore.PortalFolder import PortalFolder folder1, folder2 = self._initFolders() folder1.manage_permission( DeleteObjects, roles=(), acquire=0 ) folder1._setObject( 'sub', PortalFolder( 'sub' ) ) transaction.savepoint(optimistic=True) # get a _p_jar for 'sub' self.app.portal_types = DummyTypesTool() def _no_delete_objects(permission, object, context): return permission != DeleteObjects self._initPolicyAndUser( c_lambda=_no_delete_objects ) cookie = folder1.manage_cutObjects( ids=( 'sub', ) ) self._assertCopyErrorUnauth( folder2.manage_pasteObjects , cookie , ce_regex='Insufficient Privileges' + '.*%s' % DeleteObjects )
def updateKupu(context): # Ordinarily, GenericSetup handlers check for the existence of XML files. # Here, we are not parsing an XML file, but we use this text file as a # flag to check that we actually meant for this import step to be run. # The file is found in profiles/default. if context.readDataFile('collective.imagetags_kupu.txt') is None: return # Add additional setup code here out = StringIO() portal = getSite() # Get kupu tool and update its paragraph_styles property kt = getToolByName(portal, 'kupu_library_tool', None) if kt: new_style = 'Show tags|img|imagetags-show' styles = kt.getParagraphStyles() if not new_style in styles: styles.append(new_style) kt.configure_kupu(parastyles=styles) transaction.savepoint() print >> out, "Updated paragraph_styles in kupu: %s" % new_style else: print >> out, "kupu already has %s in paragraph_styles" % new_style context.getLogger("collective.imagetags").info(out.getvalue()) return out.getvalue()
def test_index_html_with_304_and_caching( self ): # See collector #355 self._setupCachingPolicyManager(DummyCachingManager()) original_len = len(self.RESPONSE.headers) path, ref = self._extractFile() from webdav.common import rfc1123_date self.root.image = self._makeOne( 'test_image', 'test_image.gif' ) image = self.root.image transaction.savepoint(optimistic=True) mod_time = image.modified() self.REQUEST.environ[ 'IF_MODIFIED_SINCE' ] = '%s;' % rfc1123_date( mod_time+1 ) data = image.index_html( self.REQUEST, self.RESPONSE ) self.assertEqual( data, '' ) self.assertEqual( self.RESPONSE.getStatus(), 304 ) headers = self.RESPONSE.headers self.failUnless(len(headers) >= original_len + 3) self.failUnless('foo' in headers.keys()) self.failUnless('bar' in headers.keys()) self.assertEqual(headers['test_path'], '/test_image')
def testOnItemRenamed(self): """Test notification when an item is renamed.""" ## Since there is currently no special rule for this event, we ## should not send anything. portal = self.portal ntool = getToolByName(portal, NTOOL_ID) changeProperty = lambda key, value: \ ntool.manage_changeProperties(**{key: value}) self.login('manager') ## Enable some rules just to make sure that none of them ## match. changeProperty('item_creation_notification_enabled', True) changeProperty('on_item_creation_users', ['* :: *']) changeProperty('on_item_creation_mail_template', ['* :: string:creation_mail_notification']) changeProperty('item_modification_notification_enabled', True) changeProperty('on_item_modification_users', ['* :: *']) changeProperty('on_item_modification_mail_template', ['* :: string:modification_mail_notification']) changeProperty('item_wf_transition_notification_enabled', True) changeProperty('on_wf_transition_modification_users', ['* :: *']) changeProperty('on_wf_transition_modification_mail_template', ['* :: string:workflow_mail_notification']) transaction.savepoint() ## We need this to cut/paste objects. portal.folder.manage_renameObjects(('document1', ), ('renamed-document', )) self.failUnlessSent(0)
def testImageRenameKeepsMimeType(self): self.assertEqual(self.folder.image.Format(), 'image/gif') self.assertEqual(self.folder.image.content_type, 'image/gif') transaction.savepoint(optimistic=True) # make rename work self.folder.image.image_edit(id='foo') self.assertEqual(self.folder.foo.Format(), 'image/gif') self.assertEqual(self.folder.foo.content_type, 'image/gif')
def test23_RegistryBasesNotVersionedOrRestored(self): portal_repo = self.portal.portal_repository fol = self.portal.fol fol.setTitle("v1") # Make it a component registry with bases base = aq_base(self.portal.getSiteManager()) components = PersistentComponents() components.__bases__ = (base,) fol.setSiteManager(components) portal_repo.applyVersionControl(fol) broken_iface = broken.find_global( 'never_gonna_be_real', 'IMissing', Broken=ZODB.interfaces.IBroken, type=InterfaceClass) sys.modules[broken_iface.__module__] = module = imp.new_module( broken_iface.__module__) module.IMissing = broken_iface # add a broken registrsation but do a savepoint before # breaking the interfaces to simulate a broken registrion from # a previous commit base.registerUtility(component=None, provided=broken_iface) transaction.savepoint(optimistic=True) del sys.modules[broken_iface.__module__] fol.setTitle("v2") # If an attempt was made to pickle the parent registry's # broken registration we would see an error here portal_repo.save(fol) self.assertEqual(self.portal.fol.Title(), "v2") self.assertTrue( self.portal.fol.getSiteManager().__bases__[0] is base)
def testFileRenameKeepsMimeType(self): self.assertEqual(self.folder.file.Format(), "application/pdf") self.assertEqual(self.folder.file.getFile().content_type, "application/pdf") transaction.savepoint(optimistic=True) # make rename work self.folder.file.file_edit(id="foo") self.assertEqual(self.folder.foo.Format(), "application/pdf") self.assertEqual(self.folder.foo.getFile().content_type, "application/pdf")
def testRenameLastObject(self): # Renaming should not change position transaction.savepoint(optimistic=True) # make rename work self.folder.manage_renameObjects(['baz'], ['bedrock']) self.assertEqual(self.folder.getObjectPosition('foo'), 0) self.assertEqual(self.folder.getObjectPosition('bar'), 1) self.assertEqual(self.folder.getObjectPosition('bedrock'), 2)
def cleanUpSkinsTool(context): skins = getToolByName(context, 'portal_skins') # Remove directory views for directories missing on the filesystem for name in skins.keys(): directory_view = skins.get(name) reg_key = getattr(directory_view, '_dirpath', None) if not reg_key: # not a directory view, but a persistent folder continue try: reg_key = _dirreg.getCurrentKeyFormat(reg_key) _dirreg.getDirectoryInfo(reg_key) except ValueError: skins._delObject(name) transaction.savepoint(optimistic=True) existing = skins.keys() # Remove no longer existing entries from skin selections for layer, paths in skins.selections.items(): new_paths = [] for name in paths.split(','): if name == 'plone_styles': # plone_styles has been moved and renamed new_paths.append('classic_styles') elif name in existing: new_paths.append(name) skins.selections[layer] = ','.join(new_paths)
def removeOldUIDs(portal, out): # remove temporary needed index uc = getToolByName(portal, UID_CATALOG) print >>out, 'Removing old uids\n' if olduididx in uc.indexes(): uc.delIndex(olduididx) if olduididx in uc.schema(): uc.delColumn(olduididx) count = 0 allbrains = uc() for brain in allbrains: # Get a uid for each thingie obj = brain.getObject() objUID = getattr(aq_base(obj), olduididx, None) if objUID is None: continue # not an old style AT delattr(obj, olduididx) obj._updateCatalog(portal) count += 1 if not count % 10: print >>out, '.', # avoid eating up all RAM if not count % 250: print >>out, '*', transaction.savepoint(optimistic=True) if USE_FULL_TRANSACTIONS: transaction.commit() else: transaction.savepoint(optimistic=True) print >>out, "\n%s old UID attributes removed." % count print >>out, 'Done\n'
def testRenameMember(self): id = 'newmember' password = '******' new_id = 'newmember_renamed' uf = self.portal.acl_users mdata = self.portal.portal_memberdata self.setupDummyUser() # need manager to use rename self.loginAsPortalOwner() self.failUnless(id in mdata.objectIds()) # for some reason renaming fails unless the transaction has been committed # so we use a subtransaciton that we can rollback to prevent causing problems # to other tests transaction.savepoint() mdata.manage_renameObject(id, new_id) self.failUnless(new_id in mdata.objectIds()) #import pdb; pdb.set_trace() user = uf.authenticate(new_id, password, self.portal.REQUEST) self.failIf(user is None)
def test18_retrieveObjectWhichHasBeenReplaced(self): portal_repo = self.portal.portal_repository fol = self.portal.fol doc1 = fol.doc1 doc2 = fol.doc2 # save change no 1 fol.setTitle('v1 of fol') doc1.setTitle("v1 of doc1") doc2.setTitle("v1 of doc2") portal_repo.applyVersionControl(doc1, comment='first save') portal_repo.applyVersionControl(doc2, comment='first save') transaction.savepoint(optimistic=True) fol.manage_renameObjects(['doc1','doc2'],['doc1_renamed', 'doc1']) doc1 = fol.doc1_renamed doc2 = fol.doc1 doc1.setTitle('v2 of doc1_renamed') doc2.setTitle('v2 of doc1 (was doc2)') portal_repo.save(doc1, comment='second save') portal_repo.save(doc2, comment='second save') retrieved_data = portal_repo.retrieve(doc1, 0) ret_doc = retrieved_data.object self.assertEqual(ret_doc.getId(), 'doc1') self.assertEqual(ret_doc.Title(), 'v1 of doc1') portal_repo.revert(doc1, 0) rev_doc = fol.doc1_renamed self.assertEqual(rev_doc.getId(), 'doc1_renamed') self.assertEqual(rev_doc.Title(), 'v1 of doc1')
def test21_RevertObjectWithChangedIdMaintainsConsistentCatalog(self): portal_repo = self.portal.portal_repository catalog = self.portal.portal_catalog fol = self.portal.fol doc1 = fol.doc1 # save change no 1 doc1.setTitle("v1 of doc1") portal_repo.applyVersionControl(doc1, comment='first save') self.assertEqual(len(catalog(getId='doc1')), 1) doc1.setTitle("v2 of doc1") transaction.savepoint() fol.manage_renameObject('doc1', 'doc1_changed') doc1 = fol.doc1_changed doc1.reindexObject() self.assertEqual(len(catalog(getId='doc1')), 0) self.assertEqual(len(catalog(getId='doc1_changed')), 1) portal_repo.save(doc1, comment='second save') portal_repo.revert(doc1, 0) rev_doc = fol.doc1_changed self.assertEqual(rev_doc.Title(), "v1 of doc1") self.assertEqual(len(catalog(getId='doc1')), 0) self.assertEqual(len(catalog(getId='doc1_changed')), 1) self.assertEqual(len(catalog(Title='v1 of doc1')), 1)
def renameAfterCreation(obj): # Can't rename without a subtransaction commit when using portal_factory transaction.savepoint(optimistic=True) # The id returned should be normalized already new_id = generateUniqueId(obj) obj.aq_inner.aq_parent.manage_renameObject(obj.id, new_id) return new_id
def handle_rename(self, action): data, errors = self.extractData() if errors: return parent = aq_parent(aq_inner(self.context)) sm = getSecurityManager() if not sm.checkPermission('Copy or Move', parent): raise Unauthorized(_(u'Permission denied to rename ${title}.', mapping={u'title': self.context.title})) oldid = self.context.getId() newid = data['new_id'] newid = INameChooser(parent).chooseName(newid, self.context) context_state = getMultiAdapter( (self.context, self.request), name='plone_context_state') if context_state.is_default_page(): parent.setDefaultPage(newid) # Requires cmf.ModifyPortalContent permission self.context.title = data['new_title'] # Requires zope2.CopyOrMove permission parent.manage_renameObjects([oldid, ], [str(newid), ]) transaction.savepoint(optimistic=True) notify(ObjectModifiedEvent(self.context)) IStatusMessage(self.request).add( _(u"Renamed '${oldid}' to '${newid}'.", mapping={ u'oldid': oldid, u'newid': newid})) self.request.response.redirect(self.context.absolute_url())
def safe_update_metadata(obj): """Execute asset update metadata method using transaction savepoint. :param obj: Asset model instance. """ savepoint = transaction.savepoint() try: obj.update_metadata() except ConnectionError: savepoint.rollback() msg = 'Failure updating metadata for asset: {id} title: {title}.' logger.info(msg.format(id=obj.id, title=obj.title))
def create_initial_version(self): """Creates an initial version for the document. Copied from `Products.CMFEditions.CopyModifyMergeRepositoryTool.save` only the timestamp is changed to the creation timestamp. """ if self.has_initial_version(): return if self.get_custom_initial_version_comment(): comment = self.get_custom_initial_version_comment() else: comment = _(u'initial_document_version_change_note', default=u'Initial version') comment = translate(comment, context=getRequest()) self.repository._assertAuthorized(self.document, SaveNewVersion, 'save') sp = transaction.savepoint(optimistic=True) sys_metadata = self.repository._prepareSysMetadata(comment) # Set creation datetime as initial version timestamp, # cmfeditions stores unix timestamps without any timezone information # therefore we have to do the same. created = self.document.created().asdatetime().replace(tzinfo=None) sys_metadata['timestamp'] = time.mktime(created.timetuple()) metadata = {} creator = self.document.Creator() try: # Create the initial version using a temporary security manager # that claims a user ID of the user that was the creator of the # document. # # This will execute the version creation with Manager privileges, # but the given user_id. # # We need to do this this way because setting # sys_metadata['principal'] to the current user ID is hardcoded # in Products.CMFEditions.ArchivistTool.PreparedObject.__init__ with elevated_privileges(user_id=creator): self.repository._recursiveSave( self.document, metadata, sys_metadata, autoapply=self.repository.autoapply) except ModifierException: # modifiers can abort save operations under certain conditions sp.rollback() raise self.remove_custom_initial_version_comment()
def setupQi(context): """Miscellanous steps import handle. """ # Ordinarily, GenericSetup handlers check for the existence of XML files. # Here, we are not parsing an XML file, but we use this text file as a # flag to check that we actually meant for this import step to be run. # The file is found in profiles/default. if context.readDataFile('test.cgwb_qi.txt') is None: return portal = context.getSite() portal_quickinstaller = getToolByName(portal, 'portal_quickinstaller') #portal_setup = getToolByName(portal, 'portal_setup') logger = logging.getLogger('test.cgwb.Install') for product in PRODUCT_DEPENDENCIES: logger.info('(RE)Installing %s.' % product) if not portal_quickinstaller.isProductInstalled(product): portal_quickinstaller.installProduct(product) transaction.savepoint()
def __call__(self, name=None, include_collector_items=False, override_vars=None): if override_vars is None: override_vars = '{}' try: include_collector_items = int(include_collector_items) except ValueError: include_collector_items = False if IChannel.providedBy(self.context): channel = self.context items = () else: assert name is not None channel = lookup(name) items = (FullFormatWrapper(self.context), ) sub = PreviewSubscription(channel) # We don't want to commit a transaction just for the preview; # this would happen if we were to simply add and remove from # the queue. sp = transaction.savepoint() message = IMessageAssemble(channel).render_message( self.request, sub, items, bool(include_collector_items), eval(override_vars)) if message is None: IStatusMessage(self.request).addStatusMessage( _(u"No items found.")) return self.request.response.redirect(self.context.absolute_url()) # pull message out of hat channel.queue[message.status].pull(-1) # rollback savepoint sp.rollback() # walk message, decoding HTML payload for part in message.payload.walk(): if part.get_content_type() == 'text/html': html = part.get_payload(decode=True) break else: raise ValueError("Message does not contain a 'text/html' part.") return self.template(content=html, title=channel.title)
def testSetUp(self): super(GEVERIntegrationTesting, self).testSetUp() # In order to let the SQL transaction manager make a savepoint of no # changes we need to mark the session as changed first. mark_changed(create_session()) self.savepoint = transaction.savepoint() self.interceptor.intercept(self.interceptor.BEGIN | self.interceptor.COMMIT | self.interceptor.ABORT) self.interceptor.begin_savepoint_simulation() self.interceptor.begin() logout()
def install(portal, reinstall=False): portal_quickinstaller = getToolByName(portal, 'portal_quickinstaller') portal_setup = getToolByName(portal, 'portal_setup') if not reinstall: leftColumn = getUtility(IPortletManager, name=u'plone.leftcolumn', context=portal) rightColumn = getUtility(IPortletManager, name=u'plone.rightcolumn', context=portal) leftColumn.listAllManagedPortlets = [] rightColumn.listAllManagedPortlets = [] for extension_id in EXTENSION_PROFILES: portal_setup.runAllImportStepsFromProfile('profile-%s' % extension_id, purge_old=False) product_name = extension_id.split(':')[0] portal_quickinstaller.notifyInstalled(product_name) transaction.savepoint()
def test_100_or_more_unique_ids(self): # add the same item 110 times. the first 100 items should be numbered. # after that it should use datetime to generate the id holder = self.portal title = "A Small Document" # create the first object, which will have no suffix holder.invokeFactory("Document", id='a-small-document') chooser = INameChooser(holder) for i in range(1, ATTEMPTS + 1): id = chooser.chooseName(title, holder) if i <= ATTEMPTS: # first addition has no suffix self.assertEqual("a-small-document-%s" % i, id) else: self.assertNotEqual("a-small-document-%s" % i, id) holder.invokeFactory("Document", id) transaction.savepoint(optimistic=True) holder.get(id)
def _getCopy(self, container): # Commit a subtransaction to: # 1) Make sure the data about to be exported is current # 2) Ensure self._p_jar and container._p_jar are set even if # either one is a new object transaction.savepoint(optimistic=True) if self._p_jar is None: raise CopyError( 'Object "%r" needs to be in the database to be copied' % self) if container._p_jar is None: raise CopyError('Container "%r" needs to be in the database' % container) # Ask an object for a new copy of itself. f = tempfile.TemporaryFile() self._p_jar.exportFile(self._p_oid, f) f.seek(0) ob = container._p_jar.importFile(f) f.close() return ob
def optimize_rangeindex_int_iiset(index): # migrate internal int and IISet to IITreeSet logger.info('Converting to IITreeSet for index `%s`.' % index.getId()) for name in ('_since', '_since_only', '_until', '_until_only'): tree = getattr(index, name, None) if tree is not None: logger.info('Converting tree `%s`.' % name) i = 0 for k, v in tree.items(): if isinstance(v, IISet): tree[k] = IITreeSet(v) i += 1 elif isinstance(v, int): tree[k] = IITreeSet((v, )) i += 1 if i and i % 10000 == 0: transaction.savepoint(optimistic=True) logger.info('Processed %s items.' % i) transaction.savepoint(optimistic=True) logger.info('Finished conversion.')
def reindex_catalog(args, root, registry, **kw): root.catalog.clear() root.document_map.docid_to_address.clear() root.document_map.address_to_docid.clear() root.document_map.docid_to_metadata.clear() i = 0 limit = 500 total = 0 for obj in find_all_db_objects(root): try: cataloger = ICataloger(obj) except TypeError: continue cataloger.index_object() i += 1 total += 1 if i > limit: i = 0 transaction.savepoint() print total print "-- Process complete. Reindexed %s objects" % total
def _delete_users_and_votes(filename: str, root: IPool, registry: Registry): print('Reading emails from file...') users_emails = _get_emails(filename) print('Getting users from the database...') users = _get_users(root, registry, users_emails) catalogs = find_service(root, 'catalogs') index = 1 for user in users: print('Deleting user {} ({}) and its rates'.format( user.__name__, user.email)) _delete_rate_items(catalogs, user) _delete(user) if index % 100 == 0: print('Creating savepoint...') transaction.savepoint() index = index + 1 print('Reindexing...') _reindex_proposals(catalogs, root) print('Committing changes...') transaction.commit() print('Users deleted successfully.')
def rename_object(self, obj): # Archetypes objects may get renamed during deserialization. # Do not rename again. if (base_hasattr(obj, '_isIDAutoGenerated') and not obj._isIDAutoGenerated(obj.getId())): return chooser = INameChooser(self.context) # INameFromTitle adaptable objects should not get a name # suggestion. NameChooser would prefer the given name instead of # the one provided by the INameFromTitle adapter. suggestion = None name_from_title = INameFromTitle(obj, None) if name_from_title is None: if base_hasattr(obj, 'generateNewId'): suggestion = obj.generateNewId() else: suggestion = obj.Title() name = chooser.chooseName(suggestion, obj) transaction.savepoint(optimistic=True) self.context.manage_renameObject(obj.getId(), name)
def _renameAfterCreation(self, check_auto_id=False): """ The ID (short name) for Policy and Procedure types is a concatenated string comprised of four distinct elements separated by dashes: {Theatre of Ops}-{Function Area}-{Content Type}-{Camelized Title} """ ops = self.getTheatreOfOpsCode() func = self.getFunctionalAreaCode() ptype = self.portal_type title = self.Title() if not isinstance(title, unicode): charset = self.getCharset() title = unicode(title, charset) cleaner = IDNormalizer().normalize(title) title = camelize(cleaner.replace('-', ' ')) new_id = "%s-%s-%s-%s" % (ops, func, ptype, title) # Can't rename without a subtransaction commit when using # portal_factory. Yes it's true! transaction.savepoint(optimistic=True) self.setId(new_id)
def test_move_cant_delete_source(self): from AccessControl.Permissions import delete_objects as DeleteObjects from ..PortalFolder import PortalFolder folder1, folder2 = self._initFolders() folder1.manage_permission(DeleteObjects, roles=(), acquire=0) folder1._setObject('sub', PortalFolder('sub')) transaction.savepoint(optimistic=True) # get a _p_jar for 'sub' def _no_delete_objects(permission, object, context): return permission != DeleteObjects self._initPolicyAndUser(c_lambda=_no_delete_objects) cookie = folder1.manage_cutObjects(ids=('sub', )) self._assertCopyErrorUnauth(folder2.manage_pasteObjects, cookie, ce_regex='Insufficient Privileges' + '.*%s' % DeleteObjects)
def retrieveSite(self): """Retrieves the links from all objects in the site.""" database = self.getParentNode().database objects = self.portal_catalog(Language='all') os_ = len(objects) i = 0 for ob in objects: try: obj = ob.getObject() except Exception, e: # Maybe the catalog isn't up to date log.logger.debug("Site crawl raised an error for %s: %s" % (ob.getPath(), str(e))) continue i += 1 log.logger.debug("Site Crawl Status %s of %s (%s)" % (i, os_, ob.getPath())) self.retrieveObject(obj, online=False) if not i % 100: # Memory optimization transaction.savepoint()
def test_object_reindexed_after_cut_and_paste(self): self._initPolicyAndUser() # allow copy/paste operations site = self._makeSite() site.folder1 = SimpleFolder('folder1') folder1 = site.folder1 site.folder2 = SimpleFolder('folder2') folder2 = site.folder2 bar = TheClass('bar') folder1._setObject('bar', bar) cat = site.portal_catalog cat.log = [] transaction.savepoint(optimistic=True) cookie = folder1.manage_cutObjects(ids=['bar']) folder2.manage_pasteObjects(cookie) self.assertEquals(cat.log, ["unindex /site/folder1/bar", "index /site/folder2/bar"])
def send_form(self, payment_code): data = self.get_payment_data(payment_code) form = aq_parent(self.context) if data is not None: field_data = data.get("fields", []) fields = [] form_data = {} for field in field_data: form_data[field["id"]] = field["value"] fields.append(form.get(field["id"])) self.request.form = form_data body_post = self.context.getBody_post() options = { "datetime": self.datetime(), "reference_number": data.get("reference_number"), } body_post_extra_info = self.extra_template(request=self.request, **options) post_message = body_post + body_post_extra_info self.context.setBody_post(post_message) transaction.savepoint(1) # Mail to form owner self.context.send_form(fields, self.request, to_addr=self.context.getRecipient_email()) to_field = self.context.getTo_field() if to_field != "#NONE#": # Mail to form filler self.context.send_form(fields, self.request, to_addr=form_data.get(to_field, "")) self.context.setBody_post(body_post) transaction.savepoint(1)
def handle_rename(self, action): data, errors = self.extractData() if errors: return parent = aq_parent(aq_inner(self.context)) sm = getSecurityManager() if not sm.checkPermission('Copy or Move', parent): raise Unauthorized( _(u'Permission denied to rename ${title}.', mapping={u'title': self.context.title})) oldid = self.context.getId() newid = data['new_id'] newid = INameChooser(parent).chooseName(newid, self.context) context_state = getMultiAdapter((self.context, self.request), name='plone_context_state') if context_state.is_default_page(): parent.setDefaultPage(newid) # Requires cmf.ModifyPortalContent permission self.context.title = data['new_title'] # Requires zope2.CopyOrMove permission parent.manage_renameObjects([ oldid, ], [ str(newid), ]) transaction.savepoint(optimistic=True) notify(ObjectModifiedEvent(self.context)) IStatusMessage(self.request).add( _(u"Renamed '${oldid}' to '${newid}'.", mapping={ u'oldid': oldid, u'newid': newid })) self.request.response.redirect(self.context.absolute_url())
def install(self, reinstall=False): """Install a set of products (which themselves may either use Install.py or GenericSetup extension profiles for their configuration) and then install our extension profile. We do this because it is not possible to install other products during the execution of an extension profile (i.e. we cannot do this during the importVarious step for this profile). """ portal_quickinstaller = getToolByName(self, 'portal_quickinstaller') portal_setup = getToolByName(self, 'portal_setup') for product in PRODUCT_DEPENDENCIES: if reinstall and portal_quickinstaller.isProductInstalled(product): portal_quickinstaller.reinstallProducts([product]) transaction.savepoint() elif not portal_quickinstaller.isProductInstalled(product): portal_quickinstaller.installProduct(product) transaction.savepoint() portal_setup.runAllImportStepsFromProfile( 'profile-Products.eXtremeManagement:default', purge_old=False) # No need to notify the quick installer that our own profile has # been installed. transaction.savepoint()
def cleanUpSkinsTool(context): """Cleanup the portal_skins tool. Initially this was created for Plone 4.0 alpha, but was factored out later. - Remove directory views for directories missing on the filesystem. - Remove invalid skin layers from all skin selections. """ skins = getToolByName(context, 'portal_skins') # Remove directory views for directories missing on the filesystem for name in skins.keys(): directory_view = skins.get(name) reg_key = getattr(directory_view, '_dirpath', None) if not reg_key: # not a directory view, but a persistent folder continue try: # Removed in CMF 2.3 if getattr(_dirreg, 'getCurrentKeyFormat', None): reg_key = _dirreg.getCurrentKeyFormat(reg_key) _dirreg.getDirectoryInfo(reg_key) except ValueError: skins._delObject(name) transaction.savepoint(optimistic=True) existing = skins.keys() # Remove no longer existing entries from skin selections for layer, paths in skins.selections.items(): new_paths = [] for name in paths.split(','): if name in existing: new_paths.append(name) elif '/' in name and testSkinLayer(skins, name): new_paths.append(name) else: logger.info( 'Removed no longer existing path %s ' 'from skin selection %s.', name, layer) skins.selections[layer] = ','.join(new_paths)
def convertObject(o, oldtype, newtype, ignore=False): if callable(o.id): o_id = o.id() else: o_id = o.id parent = o.getParentNode() if ignore == False: if newtype.portal_type not in parent.immediatelyAddableTypes: return if parent.meta_type == oldtype: convertObject(parent, oldtype, newtype) portaltypes = parent.portal_types portaltypes.constructContent(newtype.portal_type, parent, o_id + '_new', None) new_container = parent[o_id + '_new'] new_container.update(title=o.title) cb = o.manage_cutObjects(ids=o.objectIds()) new_container.manage_pasteObjects(cb) transaction.savepoint() parent.manage_delObjects(ids=[o_id]) transaction.savepoint() ids = [] new_ids = [] ids.append(o_id + '_new') new_ids.append(o_id) parent.manage_renameObjects(ids, new_ids) transaction.savepoint()
def after_retract(analysis): """Function triggered after a 'retract' transition for the analysis passed in is performed. The analysis transitions to "retracted" state and a new copy of the analysis is created. The copy initial state is "unassigned", unless the the retracted analysis was assigned to a worksheet. In such case, the copy is transitioned to 'assigned' state too """ # Rename the analysis to make way for it's successor. # Support multiple retractions by renaming to *-0, *-1, etc parent = analysis.aq_parent keyword = analysis.getKeyword() analyses = filter(lambda an: an.getKeyword() == keyword, parent.objectValues("Analysis")) # Rename the retracted analysis # https://docs.plone.org/develop/plone/content/rename.html # _verifyObjectPaste permission check must be cancelled parent._verifyObjectPaste = str retracted_id = '{}-{}'.format(keyword, len(analyses)) # Make sure all persistent objects have _p_jar attribute transaction.savepoint(optimistic=True) parent.manage_renameObject(analysis.getId(), retracted_id) delattr(parent, '_verifyObjectPaste') # Create a copy of the retracted analysis analysis_uid = api.get_uid(analysis) new_analysis = create_analysis(parent, analysis, RetestOf=analysis_uid) # Assign the new analysis to this same worksheet, if any. worksheet = analysis.getWorksheet() if worksheet: worksheet.addAnalysis(new_analysis) # Retract our dependents (analyses that depend on this analysis) cascade_to_dependents(analysis, "retract") # Try to rollback the Analysis Request if IRequestAnalysis.providedBy(analysis): doActionFor(analysis.getRequest(), "rollback_to_receive") reindex_request(analysis)
def test_savepoints_earlier_rollback(self): from transaction.interfaces import InvalidSavepointRollbackError # First of all, make sure we are in a clean transaction transaction.begin() self.assertEquals(len(_listSpools()), 0) email1 = self._makeAndSend() # Now that the email has been sent, there should be two files: The # lock file and the actual email. The lockfile stays until the # transaction commits. self.assertEquals(len(_listSpools()), 2) # create a savepoint savepoint1 = transaction.savepoint() # send a second mail email2 = self._makeAndSend() self.assertEquals(len(_listSpools()), 4) # create another savepoint savepoint2 = transaction.savepoint() # send a third mail email3 = self._makeAndSend() self.assertEquals(len(_listSpools()), 6) # rollback should remove email2 and email3 savepoint1.rollback() self.assertEquals(len(_listSpools()), 2) # out of order rollback, should raise an exception self.assertRaises(InvalidSavepointRollbackError, savepoint2.rollback) # Aborting a transaction should remove the email file and the # lockfile. transaction.abort() self.assertEquals(len(_listSpools()), 0)
def test_custome_metadata(self): # create two objects # make ref from one object to the other # place a custom attribute on the reference # update schemas # test if attribute still exists on the reference object rc = self.portal.reference_catalog obj1 = makeContent(self.folder, portal_type='Refnode', id='one') uid = obj1.UID() obj2 = makeContent(self.folder, portal_type='Refnode', id='two') uid2 = obj2.UID() # create reference obj1.update(link=[obj2.UID()]) ref = rc.getReferences(obj1)[0] ref.attribute1 = "some_value" ruid = ref.UID() self.assertTrue(ref.attribute1 == 'some_value') transaction.savepoint(optimistic=True) # update schema self.app.REQUEST.form['Archetypes.Refnode'] = 1 self.app.REQUEST.form['update_all'] = 1 self.portal.archetype_tool.manage_updateSchema( REQUEST=self.app.REQUEST) del obj1 # get the reference for obj1 obj1 = rc.lookupObject(uid) refs = rc.getReferences( obj1, relationship=obj1.Schema()['link'].relationship) ref = refs[0] ruid2 = ref.UID() assert ruid == ruid2, """ref uid got reassigned""" #check for the attribute self.assertTrue( hasattr(ref, 'attribute1'), 'Custom attribute on reference object is lost during schema update' ) self.assertEqual(ref.attribute1, 'some_value')
def upgrade(context): """Migrate all tool examples that were created as Pages to ToolExample objects. """ portal = getSite() catalog = getToolByName(portal, 'portal_catalog') path = portal.getPhysicalPath() path = "/".join(path) + '/tools' pages = catalog(portal_type='Document', path=path) logger.info('Starting migration of pages to ToolExample objects...') for page in pages: page = aq_inner(page.getObject()) text = page.getText() container = aq_parent(page) obj_id = page.getId() tmp_id = obj_id + '.tmp' rich_text = RichTextValue(text.decode('utf-8'), 'text/html', 'text/x-html-safe', 'utf-8') logger.info('Migrating page at %s' % page.absolute_url()) # Create a new toolexample with a temp id container.invokeFactory(type_name='osha.campaigntoolkit.toolexample', title=page.Title(), text=rich_text, id=tmp_id) # Delete the page del container[obj_id] # This is needed otherwise we get an error when renaming # See http://plone.293351.n2.nabble.com/File-object-migration-how-to-change-id-of-new-object-programatically-2-5-5-td4174543.html transaction.savepoint(1) # Rename the toolexample object container.manage_renameObject(tmp_id, obj_id) logger.info('Migration done.')
def add_data(self, line): year = datetime.datetime.now().year if line[1].value == 'Hauptzähler': EnergyPrice.find_or_create( year=year).value = line[self.cell_index].value transaction.savepoint() return if line[1].value == 'Endabrechnung': EnergyPrice.find_or_create( year=year).bill = line[self.cell_index].value transaction.savepoint() return if line[0].value is None and line[1].value is None: return meter = ElectricMeter.by_number(line[4].value) if meter is None: meter = ElectricMeter.get(line[0].value) if line[4].value: meter.number = line[4].value if not line[self.cell_index].value: value = int(line[self.cell_index - 1].value) estimated = True else: value = int(line[self.cell_index].value) estimated = not line[self.cell_index + 2].value energy_value = EnergyValue.create(electric_meter=meter, year=year, value=value, estimated_value=estimated) if energy_value not in meter.energy_values: meter.energy_values.append(energy_value) energy_value.update_member() energy_value.update_usage() # verbrauch berechnen transaction.savepoint()
def install(self, reinstall=False): """Install a set of products (which themselves may either use Install.py or GenericSetup extension profiles for their configuration) and then install a set of extension profiles. One of the extension profiles we install is that of this product. This works because an Install.py installation script (such as this one) takes precedence over extension profiles for the same product in portal_quickinstaller. We do this because it is not possible to install other products during the execution of an extension profile (i.e. we cannot do this during the importVarious step for this profile). """ portal_quickinstaller = getToolByName(self, 'portal_quickinstaller') portal_setup = getToolByName(self, 'portal_setup') for product in config.DEPENDENCIES: if reinstall and portal_quickinstaller.isProductInstalled(product): portal_quickinstaller.reinstallProducts([product]) transaction.savepoint() elif not portal_quickinstaller.isProductInstalled(product): portal_quickinstaller.installProduct(product) transaction.savepoint() for extension_id in config.EXTENSION_PROFILES: portal_setup.runAllImportStepsFromProfile('profile-%s' % extension_id, purge_old=False) product_name = extension_id.split(':')[0] portal_quickinstaller.notifyInstalled(product_name) transaction.savepoint()
def __call__(self): self.errors = [] self.protect() context = aq_inner(self.context) torename = json.loads(self.request.form['torename']) catalog = getToolByName(context, 'portal_catalog') mtool = getToolByName(context, 'portal_membership') missing = [] for data in torename: uid = data['UID'] brains = catalog(UID=uid) if len(brains) == 0: missing.append(uid) continue obj = brains[0].getObject() title = self.objectTitle(obj) if not mtool.checkPermission('Copy or Move', obj): self.errors( _(u'Permission denied to rename ${title}.', mapping={u'title': title})) continue sp = transaction.savepoint(optimistic=True) newid = data['newid'].encode('utf8') newtitle = data['newtitle'] try: obid = obj.getId() title = obj.Title() change_title = newtitle and title != newtitle if change_title: getSecurityManager().validate(obj, obj, 'setTitle', obj.setTitle) obj.setTitle(newtitle) notify(ObjectModifiedEvent(obj)) if newid and obid != newid: parent = aq_parent(aq_inner(obj)) parent.manage_renameObjects((obid, ), (newid, )) elif change_title: # the rename will have already triggered a reindex obj.reindexObject() except ConflictError: raise except Exception: sp.rollback() self.errors.append( _('Error renaming ${title}', mapping={'title': title})) return self.message(missing)
def _traverseToMountedRoot(self, root, mount_parent): """Hook for getting the object to be mounted. """ params = self._v_mount_params if params is None: params = self._loadMountParams() real_root, real_path, container_class = params if real_root is None: real_root = 'Application' try: obj = root[real_root] except KeyError: # DM 2005-05-17: why should we require 'container_class'? #if container_class or self._create_mount_points: if self._create_mount_points: # Create a database automatically. from OFS.Application import Application obj = Application() root[real_root] = obj # Get it into the database transaction.savepoint(optimistic=True) else: raise if real_path is None: real_path = self._path if real_path and real_path != '/': try: obj = obj.unrestrictedTraverse(real_path) except (KeyError, AttributeError): # DM 2005-05-13: obviously, we do not want automatic # construction when "_create_mount_points" is false #if container_class or self._create_mount_points: if container_class and self._create_mount_points: blazer = CustomTrailblazer(obj, container_class) obj = blazer.traverseOrConstruct(real_path) else: raise return obj
def test_cutPasteSupport(self): # cut/paste behaviour test # in another folder, pasted object should keep the references # added by GL (for bug #985393) org_folder = makeContent(self.folder, portal_type=self.FOLDER_TYPE, title='Origin folder', id='org_folder') dst_folder = makeContent(self.folder, portal_type=self.FOLDER_TYPE, title='Destination folder', id='dst_folder') a = makeContent(org_folder, portal_type='DDocument', id='a') b = makeContent(org_folder, portal_type='DDocument', id='b') a.addReference(b) transaction.savepoint(optimistic=True) cb = org_folder.manage_cutObjects(ids=['a']) dst_folder.manage_pasteObjects(cb_copy_data=cb) copy_a = getattr(dst_folder, 'a') self.assertEqual(copy_a.getRefs(), [b]) self.assertEqual(b.getBRefs(), [copy_a])