def test07_getModificationDate(self): portal_storage = self.portal.portal_historiesstorage obj = Dummy() v1_modified = obj.modified() v1 = portal_storage.register(history_id=1, object=ObjectData(obj), metadata=self.buildMetadata('saved v1')) self.assertEqual(v1_modified, portal_storage.getModificationDate(history_id=1)) self.assertEqual( v1_modified, portal_storage.getModificationDate(history_id=1, selector=v1)) #storage never gets the same object twice, because the archivist always generates another copy on save, #which then have a diffrent python id. #simulate object copy notifyModified(obj) obj = Dummy() v2_modified = obj.modified() v2 = portal_storage.save(history_id=1, object=ObjectData(obj), metadata=self.buildMetadata('saved v2')) self.assertNotEquals(v1, v2) self.assertEqual(v2_modified, portal_storage.getModificationDate(history_id=1)) self.assertEqual( v2_modified, portal_storage.getModificationDate(history_id=1, selector=v2)) self.assertEqual( v1_modified, portal_storage.getModificationDate(history_id=1, selector=v1))
def test12_retrieveNonExistentVersion(self): portal_storage = self.portal.portal_historiesstorage obj1 = Dummy() obj1.text = 'v1 of text' portal_storage.register(1, ObjectData(obj1), metadata=self.buildMetadata('saved v1')) obj2 = Dummy() obj2.text = 'v2 of text' portal_storage.save(1, ObjectData(obj2), metadata=self.buildMetadata('saved v2')) # purge portal_storage.purge(1, 0, metadata=self.buildMetadata("purged v1")) # retrieve non existing version self.assertRaises(StorageRetrieveError, portal_storage.retrieve, history_id=1, selector=2, countPurged=True, substitute=True) self.assertRaises(StorageRetrieveError, portal_storage.retrieve, history_id=1, selector=1, countPurged=False)
def _setupMinimalHistory(self): portal_storage = self.portal.portal_historiesstorage obj1 = Dummy() obj1.text = 'v1 of text' portal_storage.register(1, ObjectData(obj1), metadata=self.buildMetadata('saved v1')) obj2 = Dummy() obj2.text = 'v2 of text' portal_storage.save(1, ObjectData(obj2), metadata=self.buildMetadata('saved v2')) obj3 = Dummy() obj3.text = 'v3 of text' portal_storage.save(1, ObjectData(obj3), metadata=self.buildMetadata('saved v3')) obj4 = Dummy() obj4.text = 'v4 of text' portal_storage.save(1, ObjectData(obj4), metadata=self.buildMetadata('saved v4'))
def test13_saveWithUnicodeComment(self): portal_storage = self.portal.portal_historiesstorage obj1 = Dummy() obj1.text = 'v1 of text' portal_storage.register(1, ObjectData(obj1), metadata=self.buildMetadata('saved v1')) portal_storage.save(1, ObjectData(obj1), metadata=self.buildMetadata(u'saved v1\xc3\xa1'))
def test01_saveAfterRegisteringDoesNotRaiseException(self): portal_storage = self.portal.portal_historiesstorage obj = Dummy() sel = portal_storage.register(1, ObjectData(obj), metadata=self.buildMetadata('saved')) self.assertEqual(sel, 0) sel = portal_storage.save(1, ObjectData(obj), metadata=self.buildMetadata('saved')) self.assertEqual(sel, 1)
def test02_saveUnregisteredObjectRaisesException(self): portal_storage = self.portal.portal_historiesstorage obj = Dummy() self.assertRaises(StorageUnregisteredError, portal_storage.save, 1, ObjectData(obj), metadata=self.buildMetadata('saved'))
def test16_delete_history_on_content_deletion(self): """ If a content item gets deleted, delete it's history as well """ portal_hidhandler = self.portal.portal_historyidhandler portal_storage = self.portal.portal_historiesstorage self.portal.invokeFactory('Document', 'doc') self.portal.invokeFactory('Link', 'link') self.portal.invokeFactory('Folder', 'folder') # the event subscriber should be able to handle unversioned content self.portal.invokeFactory('Document', 'unversioned_doc') doc = self.portal.doc doc_histid = portal_hidhandler.register(doc) portal_storage.register( doc_histid, ObjectData(aq_base(doc)), metadata=self.buildMetadata('initial')) portal_storage.save( doc_histid, ObjectData(aq_base(doc)), metadata=self.buildMetadata('v2')) link = self.portal.link link_histid = portal_hidhandler.register(link) portal_storage.register( link_histid, ObjectData(aq_base(link)), metadata=self.buildMetadata('initial')) folder = self.portal.folder folder_histid = portal_hidhandler.register(folder) portal_storage.register( folder_histid, ObjectData(aq_base(folder)), metadata=self.buildMetadata('first draft')) dochist = portal_storage.retrieve(doc_histid).object doctype = dochist.object.portal_type self.assertEqual('Document', doctype) linkhist = portal_storage.retrieve(link_histid).object linktype = linkhist.object.portal_type self.assertEqual('Link', linktype) folderhist = portal_storage.retrieve(folder_histid).object foldertype = folderhist.object.portal_type self.assertEqual('Folder', foldertype) self.portal.manage_delObjects( ids=['doc', 'link', 'folder', 'unversioned_doc']) removed_doc = portal_storage.retrieve(history_id=doc_histid) self.assertTrue(type(removed_doc.object) == Removed) removed_link = portal_storage.retrieve(history_id=link_histid) self.assertTrue(type(removed_link.object) == Removed) removed_folder = portal_storage.retrieve(history_id=folder_histid) self.assertTrue(type(removed_folder.object) == Removed)
def test05_getHistory(self): portal_storage = self.portal.portal_historiesstorage obj1 = Dummy() obj1.text = 'v1 of text' portal_storage.register(1, ObjectData(obj1), metadata=self.buildMetadata('saved v1')) obj2 = Dummy() obj2.text = 'v2 of text' portal_storage.save(1, ObjectData(obj2), metadata=self.buildMetadata('saved v2')) obj3 = Dummy() obj3.text = 'v3 of text' portal_storage.save(1, ObjectData(obj3), metadata=self.buildMetadata('saved v3')) history = portal_storage.getHistory(history_id=1) length = len(history) # check length self.assertEquals(length, 3) # iterating over the history for i, vdata in enumerate(history): expected_test = 'v%s of text' % (i + 1) self.assertEquals(vdata.object.object.text, expected_test) self.assertEquals(history[i].object.object.text, expected_test) expected_comment = 'saved v%s' % (i + 1) self.assertEqual(self.getComment(vdata), expected_comment) self.assertEqual(self.getComment(history[i]), expected_comment) # accessing the versions self.assertEquals(history[0].object.object.text, "v1 of text") self.assertEqual(self.getComment(history[0]), "saved v1") self.assertEquals(history[1].object.object.text, "v2 of text") self.assertEqual(self.getComment(history[1]), "saved v2") self.assertEquals(history[2].object.object.text, "v3 of text") self.assertEqual(self.getComment(history[2]), "saved v3")
def test03_saveAndRetrieve(self): portal_storage = self.portal.portal_historiesstorage obj1 = Dummy() obj1.text = 'v1 of text' portal_storage.register(1, ObjectData(obj1), metadata=self.buildMetadata('saved v1')) obj2 = Dummy() obj2.text = 'v2 of text' portal_storage.save(1, ObjectData(obj2), metadata=self.buildMetadata('saved v2')) # retrieve the state at registration time retrieved_obj = portal_storage.retrieve(history_id=1, selector=0) self.assertEqual(retrieved_obj.object.object.text, 'v1 of text') self.assertEqual(self.getComment(retrieved_obj), 'saved v1') # just check if first save wasn't a double save retrieved_obj = portal_storage.retrieve(history_id=1, selector=1) self.assertEqual(retrieved_obj.object.object.text, 'v2 of text') self.assertEqual(self.getComment(retrieved_obj), 'saved v2')
def test06_checkObjectManagerIntegrity(self): portal_storage = self.portal.portal_historiesstorage om = DummyOM() sub1 = Dummy() sub2 = Dummy() om._setObject('sub1', sub1) om._setObject('sub2', sub2) self.assertEqual(len(om.objectIds()), 2) portal_storage.register(1, ObjectData(om), metadata=self.buildMetadata('saved v1')) vdata = portal_storage.retrieve(history_id=1, selector=0) retrieved_om = vdata.object self.assertEqual(len(retrieved_om.object.objectIds()), 2)
def test15_storageStatistics(self): self.maxDiff = None portal_storage = self.portal.portal_historiesstorage cmf_uid = 1 obj1 = CMFDummy('obj', cmf_uid) obj1.text = 'v1 of text' portal_storage.register(cmf_uid, ObjectData(obj1), metadata=self.buildMetadata('saved v1')) obj2 = CMFDummy('obj', cmf_uid) obj2.text = 'v2 of text' portal_storage.save(cmf_uid, ObjectData(obj2), metadata=self.buildMetadata('saved v2')) obj3 = CMFDummy('obj', cmf_uid) obj3.text = 'v3 of text' portal_storage.save(cmf_uid, ObjectData(obj3), metadata=self.buildMetadata('saved v3')) obj4 = CMFDummy('obj', cmf_uid) obj4.text = 'v4 of text' self.portal._setObject('obj', obj4) self.portal.portal_catalog.indexObject(self.portal.obj) portal_storage.save(cmf_uid, ObjectData(obj4), metadata=self.buildMetadata('saved v4')) cmf_uid = 2 tomorrow = DateTime() + 1 obj5 = CMFDummy('tomorrow', cmf_uid, effective=tomorrow) obj5.allowedRolesAndUsers = ['Anonymous'] self.portal._setObject('tomorrow', obj5) self.portal.portal_catalog.indexObject(self.portal.tomorrow) portal_storage.register( cmf_uid, ObjectData(obj5), metadata=self.buildMetadata('effective tomorrow')) cmf_uid = 3 yesterday = DateTime() - 1 obj6 = CMFDummy('yesterday', cmf_uid, expires=yesterday) obj6.allowedRolesAndUsers = ['Anonymous'] self.portal._setObject('yesterday', obj6) self.portal.portal_catalog.indexObject(self.portal.yesterday) portal_storage.register( cmf_uid, ObjectData(obj6), metadata=self.buildMetadata('expired yesterday')) cmf_uid = 4 obj7 = CMFDummy('public', cmf_uid) obj7.text = 'visible for everyone' obj7.allowedRolesAndUsers = ['Anonymous'] self.portal._setObject('public', obj7) self.portal.portal_catalog.indexObject(self.portal.public) portal_storage.register(cmf_uid, ObjectData(obj7), metadata=self.buildMetadata('saved public')) got = portal_storage.zmi_getStorageStatistics() expected = { 'deleted': [], 'summaries': { 'totalHistories': 4, 'deletedVersions': 0, 'existingVersions': 7, 'deletedHistories': 0, # time may easily be different # 'time': '0.00', 'totalVersions': 7, 'existingAverage': '1.8', 'existingHistories': 4, 'deletedAverage': 'n/a', 'totalAverage': '1.8' }, 'existing': [{ 'url': 'http://nohost/plone/obj', 'history_id': 1, 'length': 4, 'path': '/obj', 'sizeState': 'approximate', 'portal_type': 'Dummy', }, { 'url': 'http://nohost/plone/tomorrow', 'history_id': 2, 'length': 1, 'path': '/tomorrow', 'sizeState': 'approximate', 'portal_type': 'Dummy', }, { 'url': 'http://nohost/plone/yesterday', 'history_id': 3, 'length': 1, 'path': '/yesterday', 'sizeState': 'approximate', 'portal_type': 'Dummy', }, { 'url': 'http://nohost/plone/public', 'history_id': 4, 'length': 1, 'path': '/public', 'sizeState': 'approximate', 'portal_type': 'Dummy', }] } self.assertEqual(expected['deleted'], got['deleted']) self.assertTrue('summaries' in got) self.assertTrue('time' in got['summaries']) for key, value in expected['summaries'].items(): self.assertEqual(value, got['summaries'][key]) self.assertEqual(len(expected['existing']), len(got['existing'])) for idx in range(len(expected['existing'])): exp = expected['existing'][idx] actual = got['existing'][idx] for key, value in exp.items(): self.assertEqual(actual[key], value) # The actual size is not important and we want robust tests, # s. https://github.com/plone/Products.CMFEditions/issues/31 self.failUnless(actual['size'] > 0)
def test11_purgeOnSave(self): # install the purge policy that removes all except the current and # previous objects self.installPurgePolicyTool() portal_storage = self.portal.portal_historiesstorage # save no 1 obj1 = Dummy() obj1.text = 'v1 of text' sel = portal_storage.register(1, ObjectData(obj1), metadata=self.buildMetadata('saved v1')) history = portal_storage.getHistory(1, countPurged=False) self.assertEquals(sel, 0) self.assertEquals(len(history), 1) self.assertEqual(history[0].object.object.text, 'v1 of text') self.assertEqual(self.getComment(history[0]), 'saved v1') # save no 2 obj2 = Dummy() obj2.text = 'v2 of text' sel = portal_storage.save(1, ObjectData(obj2), metadata=self.buildMetadata('saved v2')) history = portal_storage.getHistory(1, countPurged=False) self.assertEquals(sel, 1) self.assertEquals(len(history), 2) self.assertEqual(history[0].object.object.text, 'v1 of text') self.assertEqual(self.getComment(history[0]), 'saved v1') self.assertEqual(history[1].object.object.text, 'v2 of text') self.assertEqual(self.getComment(history[1]), 'saved v2') # save no 3: purged oldest version obj3 = Dummy() obj3.text = 'v3 of text' sel = portal_storage.save(1, ObjectData(obj3), metadata=self.buildMetadata('saved v3')) history = portal_storage.getHistory(1, countPurged=False) length = len(history) # iterating over the history for i, vdata in enumerate(history): self.assertEquals(vdata.object.object.text, 'v%s of text' % (i + 2)) self.assertEqual(self.getComment(vdata), 'saved v%s' % (i + 2)) self.assertEquals(sel, 2) self.assertEquals(length, 2) self.assertEqual(history[0].object.object.text, 'v2 of text') self.assertEqual(self.getComment(history[0]), 'saved v2') self.assertEqual(history[1].object.object.text, 'v3 of text') self.assertEqual(self.getComment(history[1]), 'saved v3') # save no 4: purged oldest version obj4 = Dummy() obj4.text = 'v4 of text' sel = portal_storage.save(1, ObjectData(obj4), metadata=self.buildMetadata('saved v4')) history = portal_storage.getHistory(1, countPurged=False) length = len(history) # iterating over the history for i, vdata in enumerate(history): self.assertEquals(vdata.object.object.text, 'v%s of text' % (i + 3)) self.assertEqual(self.getComment(vdata), 'saved v%s' % (i + 3)) self.assertEquals(sel, 3) self.assertEquals(length, 2) self.assertEqual(history[0].object.object.text, 'v3 of text') self.assertEqual(self.getComment(history[0]), 'saved v3') self.assertEqual(history[1].object.object.text, 'v4 of text') self.assertEqual(self.getComment(history[1]), 'saved v4')
def prepare(self, obj, app_metadata=None, sys_metadata={}): obj, history_id = dereference(obj) if history_id is None: # object isn't under version control yet # An working copy beeing under version control needs to have # a history_id, version_id (starts with 0) and a location_id # (the current implementation isn't able yet to handle multiple # locations. Nevertheless lets set the location id to a well # known default value) portal_hidhandler = getToolByName(obj, 'portal_historyidhandler') history_id = portal_hidhandler.register(obj) version_id = obj.version_id = 0 obj.location_id = 0 is_registered = False else: version_id = len(self.queryHistory(obj)) is_registered = True base_obj = aq_base(obj) doc1_inside = getattr(base_obj, 'doc1_inside', None) doc2_inside = getattr(base_obj, 'doc2_inside', None) doc3_outside = getattr(base_obj, 'doc3_outside', None) # simulate clone modifiers icrefs = [] ocrefs = [] clone = deepCopy(base_obj) if doc1_inside is not None: icrefs.append(ObjectManagerStorageAdapter(clone, 'doc1_inside')) if doc2_inside is not None: icrefs.append(ObjectManagerStorageAdapter(clone, 'doc2_inside')) if doc3_outside is not None: ocrefs.append(ObjectManagerStorageAdapter(clone, 'doc3_outside')) crefs = icrefs + ocrefs # simulate before save modifier iorefs = [] oorefs = [] if doc1_inside is not None: iorefs.append(getattr(obj, 'doc1_inside')) if doc2_inside is not None: iorefs.append(getattr(obj, 'doc2_inside')) if doc3_outside is not None: oorefs.append(getattr(obj, 'doc3_outside')) orefs = iorefs + oorefs for cref in crefs: cref.setAttribute(VersionAwareReference()) # log if sys_metadata['originator'] is None: self.log("") if orefs: self.log("%sprepare %s: hid=%s, refs=(%s)" % (self.alog_indent, obj.getId(), history_id, ', '.join([ref.getId() for ref in orefs]))) else: self.log("%sprepare %s: hid=%s" % (self.alog_indent, obj.getId(), history_id)) self.alog_indent += ' ' # prepare object structure original_info = ObjectData(obj, iorefs, oorefs) clone_info = ObjectData(clone, icrefs, ocrefs) approxSize = None return PreparedObject(history_id, original_info, clone_info, (), app_metadata, sys_metadata, is_registered, approxSize)
def test15_storageStatistics(self): self.maxDiff = None portal_storage = self.portal.portal_historiesstorage cmf_uid = 1 obj1 = CMFDummy('obj', cmf_uid) obj1.text = 'v1 of text' portal_storage.register(cmf_uid, ObjectData(obj1), metadata=self.buildMetadata('saved v1')) obj2 = CMFDummy('obj', cmf_uid) obj2.text = 'v2 of text' portal_storage.save(cmf_uid, ObjectData(obj2), metadata=self.buildMetadata('saved v2')) obj3 = CMFDummy('obj', cmf_uid) obj3.text = 'v3 of text' portal_storage.save(cmf_uid, ObjectData(obj3), metadata=self.buildMetadata('saved v3')) obj4 = CMFDummy('obj', cmf_uid) obj4.text = 'v4 of text' self.portal._setObject('obj', obj4) self.portal.portal_catalog.indexObject(self.portal.obj) portal_storage.save(cmf_uid, ObjectData(obj4), metadata=self.buildMetadata('saved v4')) cmf_uid = 2 tomorrow = DateTime() + 1 obj5 = CMFDummy('tomorrow', cmf_uid, effective=tomorrow) obj5.allowedRolesAndUsers = ['Anonymous'] self.portal._setObject('tomorrow', obj5) self.portal.portal_catalog.indexObject(self.portal.tomorrow) portal_storage.register( cmf_uid, ObjectData(obj5), metadata=self.buildMetadata('effective tomorrow')) cmf_uid = 3 yesterday = DateTime() - 1 obj6 = CMFDummy('yesterday', cmf_uid, expires=yesterday) obj6.allowedRolesAndUsers = ['Anonymous'] self.portal._setObject('yesterday', obj6) self.portal.portal_catalog.indexObject(self.portal.yesterday) portal_storage.register( cmf_uid, ObjectData(obj6), metadata=self.buildMetadata('expired yesterday')) cmf_uid = 4 obj7 = CMFDummy('public', cmf_uid) obj7.text = 'visible for everyone' obj7.allowedRolesAndUsers = ['Anonymous'] self.portal._setObject('public', obj7) self.portal.portal_catalog.indexObject(self.portal.public) portal_storage.register(cmf_uid, ObjectData(obj7), metadata=self.buildMetadata('saved public')) got = portal_storage.zmi_getStorageStatistics() expected = { 'deleted': [], 'summaries': { 'totalHistories': 4, 'deletedVersions': 0, 'existingVersions': 7, 'deletedHistories': 0, 'time': '0.00', 'totalVersions': 7, 'existingAverage': '1.8', 'existingHistories': 4, 'deletedAverage': 'n/a', 'totalAverage': '1.8' }, 'existing': [{ 'url': 'http://nohost/plone/obj', 'history_id': 1, 'length': 4, 'path': '/obj', 'sizeState': 'approximate', 'portal_type': 'Dummy', 'size': 1718 }, { 'url': 'http://nohost/plone/tomorrow', 'history_id': 2, 'length': 1, 'path': '/tomorrow', 'sizeState': 'approximate', 'portal_type': 'Dummy', 'size': 555 }, { 'url': 'http://nohost/plone/yesterday', 'history_id': 3, 'length': 1, 'path': '/yesterday', 'sizeState': 'approximate', 'portal_type': 'Dummy', 'size': 557 }, { 'url': 'http://nohost/plone/public', 'history_id': 4, 'length': 1, 'path': '/public', 'sizeState': 'approximate', 'portal_type': 'Dummy', 'size': 557 }] } self.assertEqual(expected, got)