def test_circular_reference_subfolder_deletion(self): doc1 = self.portal.doc1 doc2 = self.portal.doc2 doc4 = self.portal.folder1.doc4 # This tests the behaviour when removing three object # referencing each other in a circle. This situation cannot be # resolved completely, since the removal events are fired # separately. However, the circle gets "broken up" when # confirming the removal of the first object, and no further # confirmation form are necessary: self._set_text(doc1, '<a href="doc2">documents...</a>') self._set_text(doc2, '<a href="folder1/doc4">linking...</a>') self._set_text(doc4, '<a href="../doc1">in circles.</a>') self.assertEqual(IReferenceable(doc1).getReferences(), [doc2, ]) self.assertEqual(IReferenceable(doc2).getReferences(), [doc4, ]) self.assertEqual(IReferenceable(doc4).getReferences(), [doc1, ]) self.assertRaises(exceptions.LinkIntegrityNotificationException, self.portal.manage_delObjects, ['doc1', 'doc2', ], self.request) transaction.abort() self.portal.manage_delObjects( ['doc1', 'doc2', 'folder1', ], self.request) self.assertNotIn('doc1', self.portal) self.assertNotIn('doc2', self.portal) self.assertNotIn('folder1', self.portal)
def modifiedCoverTile(obj, event): """Ensure link integrity on Rich Text tiles. Keyword arguments: obj -- Dexterity-based object that was modified event -- event fired """ pu = api.portal.get_tool('portal_url') if pu is None: # `getObjectFromLinks` is not possible without access # to `portal_url` return rc = api.portal.get_tool('reference_catalog') if rc is None: # `updateReferences` is not possible without access # to `reference_catalog` return referenceable_parent = IReferenceable(obj.context, None) if referenceable_parent is None: # `updateReferences` is not possible # if parent object isn't referenceable return refs = set() for name, value in obj.data.items(): if isinstance(value, RichTextValue): value = value.raw if not value: continue links = extractLinks(value) refs |= getObjectsFromLinks(obj.context, links) updateReferences(IReferenceable(obj.context), referencedRelationship, refs)
def test_image_scale_reference_creation(self): doc1 = self.portal.doc1 img1 = self.portal.image1 # Linking image scales should also work: self._set_text(doc1, '<a href="image1/@@images/image_thumb">an image</a>') self.assertEqual(IReferenceable(doc1).getReferences(), [img1]) self.assertEqual(IReferenceable(img1).getBackReferences(), [doc1])
def test_image_resolveuid_reference_creation(self): doc1 = self.portal.doc1 img1 = self.portal.image1 # Linking via the "resolveuid/UID" method should also work: self._set_text( doc1, '<a href="resolveuid/{0:s}">an image</a>'.format(IUUID(img1))) self.assertEqual(IReferenceable(doc1).getReferences(), [img1]) self.assertEqual(IReferenceable(img1).getBackReferences(), [doc1])
def test_image_tag_reference_creation(self): doc1 = self.portal.doc1 img1 = self.portal.image1 # This tests the correct creation of references used for # ensuring link integrity. Any archetype-based content object # which refers to other (local) objects by `<img>` or `<a>` tags # should create references between those objects on save. self._set_text(doc1, img1.restrictedTraverse('@@images').tag()) self.assertEqual(IReferenceable(doc1).getReferences(), [img1]) self.assertEqual(IReferenceable(doc1).getBackReferences(), []) self.assertEqual(IReferenceable(img1).getReferences(), []) self.assertEqual(IReferenceable(img1).getBackReferences(), [doc1])
def test_remove_cleans_ref_catalog(self): doc1 = self.portal['doc1'] doc2 = self.portal['doc2'] doc1_refs = IReferenceable(doc1) doc2_refs = IReferenceable(doc2) doc1_refs.addReference(doc2_refs, relationship='fooRelationship') ref_catalog = self.portal.reference_catalog self.assertEquals(1, len(ref_catalog())) self.assertEquals([doc1], doc2_refs.getBackReferences()) self.portal.manage_delObjects(['doc1']) self.assertEquals(0, len(ref_catalog())) self.assertEquals([], doc2_refs.getBackReferences())
def getObjectsFromLinks(base, links): """ determine actual objects refered to by given links """ objects = set() url = base.absolute_url() scheme, host, path, query, frag = urlsplit(url) for link in links: s, h, path, q, f = urlsplit(link) # relative or local url if (not s and not h) or (s == scheme and h == host): # Paths should always be strings if isinstance(path, unicode): path = path.encode('utf-8') obj, extra = findObject(base, path) if obj: if IOFSImage.providedBy(obj): # use atimage object for scaled images obj = aq_parent(obj) if not IReferenceable.providedBy(obj): try: obj = IReferenceable(obj) except: continue objects.add(obj) return objects
def test_removal_via_zmi(self): doc1 = self.portal.doc1 doc2 = self.portal.doc2 # This tests ensuring link integrity when removing an object via # the ZMI. self._set_text(doc1, '<a href="doc2">a document</a>') self.assertEqual(IReferenceable(doc1).getReferences(), [doc2]) transaction.commit() # Then we use a browser to try to delete the referenced # document. Before we can do this we need to prevent the test # framework from choking on the exception we intentionally throw. self.browser.handleErrors = True self.browser.open('http://nohost/plone/manage_main') self.browser\ .getControl(name='ids:list')\ .getControl(value='doc2').selected = True self.browser.getControl('Delete').click() self.assertIn('Potential link breakage', self.browser.contents) self.assertIn('<a href="http://nohost/plone/doc1">Test Page 1</a>', self.browser.contents) # After we have acknowledged the breach in link integrity the # document should have been deleted: self.browser.getControl(name='delete').click() self.assertNotIn('doc2', self.portal.objectIds())
def test_files_with_spaces_removal(self): doc1 = self.portal.doc1 # This tests the behaviour when removing a referenced file that has # spaces in its id. First we need to rename the existing file: self.portal.invokeFactory('Document', id='some spaces.doc', title='A spaces doc') self.assertIn('some spaces.doc', self.portal.objectIds()) spaces1 = self.portal['some spaces.doc'] self._set_text(doc1, '<a href="some spaces.doc">a document</a>') # The document should now have a reference to the file: self.assertEqual(IReferenceable(doc1).getReferences(), [spaces1]) transaction.commit() # Then we use a browser to try to delete the referenced file. # Before we can do this we need to prevent the test framework # from choking on the exception we intentionally throw. self.browser.handleErrors = True self.browser.open('{0:s}/object_delete?_authenticator={1:s}'.format( spaces1.absolute_url(), self._get_token(spaces1))) self.assertIn('Potential link breakage', self.browser.contents) self.assertIn('<a href="http://nohost/plone/doc1">Test Page 1</a>', self.browser.contents) self.browser.getControl(name='delete').click() self.assertNotIn('some spaces.doc', self.portal.objectIds())
def modifiedDexterity(obj, event): """ a dexterity based object was modified """ pu = getToolByName(obj, 'portal_url', None) if pu is None: # `getObjectFromLinks` is not possible without access # to `portal_url` return rc = getToolByName(obj, 'reference_catalog', None) if rc is None: # `updateReferences` is not possible without access # to `reference_catalog` return fti = getUtility(IDexterityFTI, name=obj.portal_type) schema = fti.lookupSchema() additional_schema = getAdditionalSchemata(context=obj, portal_type=obj.portal_type) schemas = [i for i in additional_schema] + [schema] refs = set() for schema in schemas: for name, field in getFieldsInOrder(schema): if isinstance(field, RichText): # Only check for "RichText" ? value = getattr(schema(obj), name) if not value: continue links = extractLinks(value.raw) refs |= getObjectsFromLinks(obj, links) updateReferences(IReferenceable(obj), referencedRelationship, refs)
def test_rename_updates_ref_catalog(self): doc1 = self.portal['doc1'] doc2 = self.portal['doc2'] ref_catalog = self.portal.reference_catalog doc1.text = RichTextValue('<a href="doc2">doc2</a>') modified(doc1) self.assertEquals(1, len(ref_catalog())) self.assertEquals([doc2], IReferenceable(doc1).getReferences()) ref_brain = ref_catalog()[0] self.assertTrue(ref_brain.getPath().startswith('doc1')) self.portal.manage_renameObject(id='doc1', new_id='new_name') modified(doc1) self.assertEquals(1, len(ref_catalog())) ref_brain = ref_catalog()[0] self.assertTrue(ref_brain.getPath().startswith('new_name')) self.assertEquals([doc2], IReferenceable(doc1).getReferences())
def test_unicode_links(self): doc1 = self.portal.doc1 # This tests checks that isLinked can now be used safely as it # eventually plays well with transaction machinery. # Add bad link, should not raise exception and there should not # be any references added. self._set_text(doc1, unicode('<a href="ö?foo=bar&baz=bam">bug</a>', 'utf-8')) self.assertEqual(IReferenceable(doc1).getReferences(), [])
def working_copy(self): adapted = IReferenceable(self.context, None) if adapted is None: return None refs = adapted.getBRefs(WorkingCopyRelation.relationship) if len(refs) > 0: return refs[0] else: return None
def test_update(self): doc1 = self.portal.doc1 doc2 = self.portal.doc2 doc4 = self.portal.folder1.doc4 # This tests updating link integrity information for all site content, # i.e. after migrating from a previous version. self._set_text(doc1, '<a href="doc2">a document</a>') self._set_text(doc2, '<a href="folder1/doc4">a document</a>') IReferenceable(doc1).deleteReferences(relationship='isReferencing') IReferenceable(doc2).deleteReferences(relationship='isReferencing') # Just to make sure, we check that there are no references from or to # these documents at this point: self.assertEqual(IReferenceable(doc1).getReferences(), []) self.assertEqual(IReferenceable(doc2).getReferences(), []) # An update of link integrity information for all content is triggered # by browsing a specific url: transaction.commit() self.browser.open('{0:s}/updateLinkIntegrityInformation'.format( self.portal.absolute_url())) self.browser.getControl('Update').click() self.assertIn('Link integrity information updated for', self.browser.contents) # Now the linking documents should hold the correct link integrity # references: self.assertEqual(IReferenceable(doc1).getReferences(), [ doc2, ]) self.assertEqual(IReferenceable(doc2).getReferences(), [ doc4, ])
def test_rename_updates_ref_catalog(self): doc1 = self.portal['doc1'] doc2 = self.portal['doc2'] doc1_refs = IReferenceable(doc1) doc2_refs = IReferenceable(doc2) doc1_refs.addReference(doc2_refs, relationship='fooRelationship') ref_catalog = self.portal.reference_catalog self.assertEquals(1, len(ref_catalog())) self.assertEquals([doc2], doc1_refs.getReferences()) ref_brain = ref_catalog()[0] self.assertTrue(ref_brain.getPath().startswith('doc1')) self.portal.manage_renameObject(id='doc1', new_id='new_name') doc1 = self.portal['new_name'] doc1_refs = IReferenceable(doc1) self.assertEquals(1, len(ref_catalog())) ref_brain = ref_catalog()[0] self.assertTrue(ref_brain.getPath().startswith('new_name')) self.assertEquals([doc2], doc1_refs.getReferences())
def test_renaming_referenced_item(self): doc1 = self.portal.doc1 doc2 = self.portal.doc2 # This tests makes sure items that are linked to can still be # renamed (see the related bug report in #6608). First we need # to create the necessary links: self._set_text(doc1, '<a href="doc2">doc2</a>') self.assertEqual(IReferenceable(doc2).getBackReferences(), [doc1]) # Make changes visible to testbrowseropen transaction.commit() # Then we use a browser to rename the referenced image: self.browser.handleErrors = True self.browser.open('{0:s}/object_rename?_authenticator={1:s}'.format( doc1.absolute_url(), self._get_token(doc1))) self.browser.getControl(name='form.widgets.new_id').value = 'nuname' self.browser.getControl(name='form.buttons.Rename').click() self.assertIn("Renamed 'doc1' to 'nuname'.", self.browser.contents) transaction.commit() self.assertNotIn('doc1', self.portal.objectIds()) self.assertIn('nuname', self.portal.objectIds()) self.assertEqual(IReferenceable(doc2).getBackReferences(), [doc1]) # We simply use a browser to try to delete a content item. self.browser.open(doc2.absolute_url()) self.browser.getLink('Delete').click() self.assertIn('Do you really want to delete this item?', self.browser.contents) self.browser.getControl(name='form.buttons.Delete').click() self.assertIn('nuname', self.portal.objectIds()) # Link breakabe page should be shown self.assertIn('Potential link breakage', self.browser.contents) self.assertIn('<a href="http://nohost/plone/nuname">Test Page 1</a>', self.browser.contents)
def test_broken_references(self): # create a temporary document to test with doc1a = testing.create(self.portal, 'Document', id='doc1a') doc1 = self.portal.doc1 self.assertEqual(len(IReferenceable(doc1).getReferences()), 0) self._set_text(doc1, '<a href="doc1a">Doc 1a</a>') self.assertEqual(len(IReferenceable(doc1).getReferences()), 1) self.assertEqual(IReferenceable(doc1).getReferences()[0].id, self.portal.doc1a.id) # Now delete the target item, suppress events and test again, # the reference should be broken now. self.portal._delObject(doc1a.id, suppress_events=True) self.assertEqual(IReferenceable(doc1).getReferences(), [None]) # If we now try to update the linking document again in order to # remove the link, things used to break raising a # ``ReferenceException``. This should be handled more # gracefully now: self._set_text(doc1, 'foo!') self.assertEqual(IReferenceable(doc1).getReferences(), [])
def test_file_reference_linkintegrity_page_is_shown(self): doc1 = self.portal.doc1 file2 = testing.create(self.portal, 'File', id='file2', file=testing.GIF) self._set_text(doc1, '<a href="file2">A File</a>') self.assertEqual(IReferenceable(doc1).getReferences(), [file2]) self.assertIn('file2', self.portal.objectIds()) token = self._get_token(file2) self.request['_authenticator'] = token # Make changes visible to test browser transaction.commit() self.browser.handleErrors = True self.browser.addHeader( 'Authorization', 'Basic {0:s}:{1:s}'.format(TEST_USER_NAME, TEST_USER_PASSWORD)) delete_url = '{0:s}/object_delete?_authenticator={1:s}'.format( file2.absolute_url(), token) # Try to remove but cancel self.browser.open(delete_url) # Validate text self.assertIn('Potential link breakage', self.browser.contents) self.assertIn('removeConfirmationAction', self.browser.contents) self.assertIn('<a href="http://nohost/plone/doc1">Test Page 1</a>', self.browser.contents) self.assertIn('Would you like to delete it anyway?', self.browser.contents) # Click cancel button, item should stay in place self.browser.getControl(name='cancel').click() self.assertEqual(self.browser.url, self.portal.absolute_url()) self.assertIn('Removal cancelled.', self.browser.contents) self.assertIn('file2', self.portal.objectIds()) # Try to remove and confirm self.browser.open(delete_url) self.browser.getControl(name='delete').click() self.assertNotIn('file2', self.portal.objectIds())
def test_file_reference_throws_exception(self): """This tests the behaviour when removing a referenced file.""" doc1 = self.portal.doc1 file2 = testing.create(self.portal, 'File', id='file2', file=testing.GIF) self._set_text(doc1, '<a href="file2">A File</a>') self.assertEqual(IReferenceable(doc1).getReferences(), [file2]) self.assertIn('file2', self.portal.objectIds()) token = self._get_token(file2) self.request['_authenticator'] = token # Throws exception which actually should abort transaction view = file2.restrictedTraverse('@@object_delete') self.assertRaises(exceptions.LinkIntegrityNotificationException, view)
def update_link_integrity(obj, event): """Update link integrity information on modification/removal of tiles. :param obj: cover object that was modified :type obj: Dexterity-based content type :param event: event fired :type event: """ refs = obj.get_referenced_objects() if IS_PLONE_5: updateReferences(obj, refs) else: # needed by plone.app.linkintegrity under Plone 4.x adapted = IReferenceable(obj, None) if adapted is None: return updateReferences(adapted, referencedRelationship, refs)
def test_reference_orthogonality(self): doc = self.portal.doc1 img = self.portal.image1 tag = img.restrictedTraverse('@@images').tag() # This tests the behavior when other references already exist. self.assertEqual(IReferenceable(doc).getReferences(), []) self.assertEqual(IReferenceable(doc).getBackReferences(), []) self.assertEqual(IReferenceable(img).getReferences(), []) self.assertEqual(IReferenceable(img).getBackReferences(), []) # Then establish a reference between the document and image as # a related item: self._set_related_items(doc, [img, ]) self.assertEqual(self._get_related_items(doc), [img, ]) # Next edit the document body and insert a link to the image, # which should trigger the creation of a link integrity reference: self._set_text(doc, tag) from plone.app.linkintegrity.handlers import referencedRelationship self.assertEqual(IReferenceable(doc).getReferences( relationship=referencedRelationship), [img, ]) # And the related item reference remains in place: self.assertEqual(self._get_related_items(doc), [img, ]) # Finally, edit the document body again, this time removing the # link to the image, which should trigger the removal of the # link integrity reference: self._set_text(doc, 'where did my link go?') self.assertEqual(IReferenceable(doc).getReferences( relationship=referencedRelationship), []) # And again the related item reference remains in place: self.assertEqual(self._get_related_items(doc), [img, ])
def test_relative_upwards_link_generates_matching_reference(self): doc1 = self.portal.doc1 doc3 = self.portal.folder1.doc3 self._set_text(doc3, '<a href="../doc1">go!</a>') self.assertEqual(IReferenceable(doc3).getReferences(), [doc1])
def deleteReferences(context, event): """ """ IReferenceable(context).deleteReferences()