We have
movable Parts!. That's right. With a little bit of code, a couple of unit tests, and some surprisingly simple UI work, Parts in an SCS Document can be re-arranged.
This is yielding something I've been wanting to do (and have) for years - decent through-the-web
Document editing, with drop in objects than can be reorganized easily. While I'm sure it's been done many times before, it was great seeing the results yesterday as part of my own framework and application.
The work went fairly quickly, once I moved the methods to the right class and added Unit Tests. The secret is in the
_objects attribute of Zope
ObjectManager based instances.
_objects is a
tuple of
dictionaries with the keys
'id' and
'meta_type'. ObjectManager classes use this information to know what objects in the ObjectManager are being managed as subobjects, and to define their ordering. Most people never see or know of this attributes existence as its use is well-handled by the common ObjectManager API's. But one can use this list to their advantage, such as reordering.
So, in todays code we have what I used to reorder Parts in my PartContainer class. There are two important elements - finding the index of the object in question, and then swapping its place in the list. We can't use
parts.index(part_id)
because of the complex data structure of the list - although, if we had the object in question, we could try
parts.index({'id': obj.getId(), 'meta_type': obj.meta_type})
. But the following works, and could be applied to any ObjectManager based class.
Note - there are already Products available to bring order to Folders in Zope, such as Ordered Folder. It's a feature that was Proposed for Zope 2.6, but was deferred..
class PartContainer(ObjectManager):
def _findPartIndex(self, part_id, objectList=[]):
""" Returns the index of a part in the object list """
idx = 0
for obj in objectList:
if obj['id'] == part_id:
break
idx += 1
else:
## part not found
raise IndexError(part_id)
return idx
security.declareProtected(change_scs_documents, 'movePartUp')
def movePartUp(self, part_id):
""" Move a part up. Raises IndexError if part is not found """
## make a copy of our _objects list, which is a part of ObjectManager.
## _objects is a tuple of dictionaries with keys 'meta_type' and 'id'.
parts = list(self._objects)
idx = self._findPartIndex(part_id, parts)
if idx == 0:
## Can't move past top, just exit
return 0
## swap fields, moving idx up
parts[idx], parts[idx-1] = parts[idx-1], parts[idx]
self._objects = tuple(parts)
return 1
security.declareProtected(change_scs_documents, 'movePartDown')
def movePartDown(self, part_id):
""" Move a part down. Raises IndexError if part is not found """
## make a copy of our _objects list, which is a part of ObjectManager.
## _objects is a tuple of dictionaries with keys 'meta_type' and 'id'.
parts = list(self._objects)
idx = self._findPartIndex(part_id, parts)
if idx == len(parts)-1:
## Can't move past bottom, so just exit
return 0
## swap fields, moving idx up
parts[idx], parts[idx+1] = parts[idx+1], parts[idx]
self._objects = tuple(parts)
return 1