Ticket #13165 (confirmed Bug)
CloneBlobs requires user to have AccessPreviousVersions permission
Reported by: | nglogic | Owned by: | alecm |
---|---|---|---|
Priority: | minor | Milestone: | 4.x |
Component: | Versioning | Version: | 4.1 |
Keywords: | Cc: |
Description
In CloneBlobs.getReferencedAttributes, line 1080, repo.retrieve is called, which requires this permission. This prevents users without AccessPreviousVersions perm from creating a new or editing object of the type that involves CloneBlobs.
repo = getToolByName(obj, 'portal_repository') try: prior_rev = repo.retrieve(obj) except ArchivistRetrieveError: prior_rev = None for f in blob_fields:
Suggested solution: use the _retrieve method that skips checking for permission.
prior_rev = repo._retrieve(obj, selector=None, preserve=(), countPurged=True)
This affects CMFEditions 2.2.5 and older.
Change History
comment:1 Changed 4 years ago by nglogic
- Owner set to alecm
- Component changed from Unknown to Versioning
comment:2 Changed 4 years ago by nglogic
Workaround: define a new modifier and add it to portal_modifier object.
import os,sys from itertools import izip from App.class_init import InitializeClass from Acquisition import aq_base from zope.interface import implements, Interface from ZODB.blob import Blob from OFS.ObjectManager import ObjectManager from Products.BTreeFolder2.BTreeFolder2 import BTreeFolder2Base from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.CMFCore.utils import getToolByName from Products.CMFCore.permissions import ManagePortal from Products.CMFCore.Expression import Expression from Products.CMFEditions.interfaces.IArchivist import ArchivistRetrieveError from Products.CMFEditions.interfaces.IModifier import IAttributeModifier from Products.CMFEditions.interfaces.IModifier import ICloneModifier from Products.CMFEditions.interfaces.IModifier import ISaveRetrieveModifier from Products.CMFEditions.interfaces.IModifier import IConditionalTalesModifier from Products.CMFEditions.interfaces.IModifier import IReferenceAdapter from Products.CMFEditions.interfaces.IModifier import FileTooLargeToVersionError from Products.CMFEditions.Modifiers import ConditionalModifier from Products.CMFEditions.Modifiers import ConditionalTalesModifier from Products.CMFEditions.StandardModifiers import CloneBlobs, manage_CloneBlobsAddForm from Products.CMFEditions.StandardModifiers import modifiers try: from plone.app.blob.interfaces import IBlobField except ImportError: class IBlobField(Interface): pass manage_NewCloneBlobsAddForm = \ PageTemplateFile('www/NewCloneBlobs.pt', globals(), __name__='manage_NewCloneBlobsAddForm') class NewCloneBlobs(CloneBlobs): def __init__(self, obj, name): """Initialize the adapter. """ self._obj = obj self._name = name def getReferencedAttributes(self, obj): blob_fields = (f for f in obj.Schema().fields() if IBlobField.providedBy(f)) file_data = {} # try to get last revision, only store a new blob if the # contents differ from the prior one, otherwise store a # reference to the prior one # XXX: According to CopyModifyMergeRepository, retrieve and getHistory # should not be used as a part of more complex transactions # due to some resource managers not supporting savepoints. repo = getToolByName(obj, 'portal_repository') try: prior_rev = repo._retrieve(obj, selector=None, preserve=(), countPurged=True) except ArchivistRetrieveError: prior_rev = None for f in blob_fields: # XXX: should only do this if data has changed since last # version somehow Resave the blob line by line blob_file = f.get(obj, raw=True).getBlob().open('r') save_new = True if prior_rev is not None: prior_obj = prior_rev.object prior_blob = f.get(prior_obj, raw=True).getBlob() prior_file = prior_blob.open('r') # Check for file size differences if (os.fstat(prior_file.fileno()).st_size == os.fstat(blob_file.fileno()).st_size): # Files are the same size, compare line by line for line, prior_line in izip(blob_file, prior_file): if line != prior_line: break else: # The files are the same, save a reference # to the prior versions blob on this version file_data[f.getName()] = prior_blob save_new = False if save_new: new_blob = file_data[f.getName()] = Blob() new_blob_file = new_blob.open('w') try: blob_file.seek(0) new_blob_file.writelines(blob_file) finally: blob_file.close() new_blob_file.close() return file_data InitializeClass(NewCloneBlobs) def initialize(context): """Registers modifiers with zope (on zope startup). """ m = { 'id': 'NewCloneBlobs', 'title': "Store blobs and files by reference on AT content, new version", 'enabled': True, 'condition': "python: portal_type in ('Image', 'File')", 'wrapper': ConditionalTalesModifier, 'modifier': NewCloneBlobs, 'form': manage_NewCloneBlobsAddForm, 'factory': manage_addNewCloneBlobs, } context.registerClass( m['wrapper'], m['id'], permission = ManagePortal, constructors = (m['form'], m['factory']), ) def manage_addNewCloneBlobs(self, id, title=None, REQUEST=None): """Add a skip parent pointers modifier """ modifier = NewCloneBlobs(id, title) self._setObject(id, modifier) if REQUEST is not None: REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
Requires initialize method to be called from your product similar method and a www/NewCloneBlobs.pt template:
<p tal:replace="structure here/manage_page_header" omit-tag="">Header</p> <p tal:replace="structure here/manage_tabs" omit-tag="">tabs</p> <h2>Add Clone Blobs Modifier</h2> <form action="manage_addNewCloneBlobs" method="post"> <table border="0"> <tr> <th> Id </th> <td> <input type="text" name="id" value="" size="40"/> </td> </tr> <tr> <th> Title </th> <td> <input type="text" name="title" value="" size="40"/> </td> </tr> <tr> <td colspan="2"> <input type="submit" name="submit" value="Add"/> </td> </tr> </table> </form> <p tal:replace="structure here/manage_page_footer" omit-tag="">Footer</p>
Note: See
TracTickets for help on using
tickets.