From 7b257deaf370e2a1ae00cc215d93b7720808c6b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Perrin?= Date: Tue, 14 Jan 2025 23:16:16 +0900 Subject: [PATCH] Enable ZMI History tab for ``OFS.Image.File`` Modernize a bit the condition to display online text editor: support editing json inline and tolerate larger file content. --- CHANGES.rst | 3 +++ src/OFS/Image.py | 31 ++++++++++++++++++++++++- src/OFS/dtml/fileEdit.dtml | 38 +++++++++++++++---------------- src/OFS/tests/testFileAndImage.py | 13 +++++++++++ 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 566858299f..340267f540 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,9 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst 5.11.2 (unreleased) ------------------- +- Enable ZMI History tab for ``OFS.Image.File``. + (`#396 `_) + - Fix error messages from spam/pen test requests. - Fix a ``ResourceWarning`` emitted when uploading large files. diff --git a/src/OFS/Image.py b/src/OFS/Image.py index 4cec563ab5..b950a1d3ca 100644 --- a/src/OFS/Image.py +++ b/src/OFS/Image.py @@ -33,6 +33,8 @@ from App.special_dtml import DTMLFile from DateTime.DateTime import DateTime from OFS.Cache import Cacheable +from OFS.History import Historical +from OFS.History import html_diff from OFS.interfaces import IWriteLock from OFS.PropertyManager import PropertyManager from OFS.role import RoleManager @@ -167,6 +169,7 @@ class File( PropertyManager, RoleManager, Item_w__name__, + Historical, Cacheable ): """A File object is a content object for arbitrary files.""" @@ -201,7 +204,9 @@ class File( + PropertyManager.manage_options + RoleManager.manage_options + Item_w__name__.manage_options - + Cacheable.manage_options) + + Cacheable.manage_options + + Historical.manage_options + ) _properties = ( {'id': 'title', 'type': 'string'}, @@ -657,6 +662,30 @@ def manage_upload(self, file='', REQUEST=None): return self.manage_main( self, REQUEST, manage_tabs_message=msg) + @security.protected(change_images_and_files) + def manage_is_editable_inline(self): + return ( + self.content_type + and ( + self.content_type.startswith('text') + or self.content_type.endswith('javascript') + or self.content_type == 'application/json' + ) + and self.get_size() < 2**17 + ) + + def manage_historyCompare(self, rev1, rev2, REQUEST, + historyComparisonResults=''): + if self.manage_is_editable_inline(): + return File.inheritedAttribute('manage_historyCompare')( + self, rev1, rev2, REQUEST, + historyComparisonResults=html_diff( + str(rev1), str(rev2))) + return File.inheritedAttribute('manage_historyCompare')( + self, rev1, rev2, REQUEST, + historyComparisonResults=historyComparisonResults + ) + def _get_content_type(self, file, body, id, content_type=None): headers = getattr(file, 'headers', None) if headers and 'content-type' in headers: diff --git a/src/OFS/dtml/fileEdit.dtml b/src/OFS/dtml/fileEdit.dtml index 034da539b6..a64c0d5990 100644 --- a/src/OFS/dtml/fileEdit.dtml +++ b/src/OFS/dtml/fileEdit.dtml @@ -38,28 +38,26 @@ - - -
- - - - - -
- -
- -
- bytes + +
+ + + + + +
+ +
+ +
+ bytes
- - +
+
diff --git a/src/OFS/tests/testFileAndImage.py b/src/OFS/tests/testFileAndImage.py index 45ec9a21e4..183e8b2e4c 100644 --- a/src/OFS/tests/testFileAndImage.py +++ b/src/OFS/tests/testFileAndImage.py @@ -517,3 +517,16 @@ def test_Image__manage_edit__1(self): self.assertIn('Saved changes', self.browser.contents) text_2 = self.browser.getControl(name='filedata:text').value self.assertEqual(text_2, 'hällo') + + def test_File__manage_history(self): + self.app.file.update_data('beföre'.encode()) + transaction.commit() + self.app.file.update_data('àftér'.encode()) + transaction.commit() + self.browser.open('http://localhost/file/manage_main') + self.browser.getLink('History').click() + keys = self.browser.getControl(name='keys:list') + keys.value = [keys.options[0], keys.options[1]] + self.browser.getControl('Compare').click() + self.assertIn('beföre', self.browser.contents) + self.assertIn('àftér', self.browser.contents)