def update_copied_objects_list(object, event):
    """Update the list of which objects have been copied.

    Note that the new object does not yet have an acquisition chain,
    so we cannot really do much here yet.  We are only interested in
    the old object now.

    When copying a single item:

    - object is copy of item

    - event.object is copy of item

    - event.original is original item

    Both copies have not been added to an acquisition context yet.

    When copying a folder that has sub folders with content, like
    folder/sub/doc, and pasting it to the same location so the origal
    and pasted folders are at the same level, this event is also fired
    with:

    - object is copy of doc, with physical path copy_of_folder/sub/doc

    - event.object is copy of folder, with physical path copy_of_folder

    - event.original is the original folder

    - sub is nowhere to be seen...

    Luckily we can use physical paths in that case.
    """
    request = event.original.REQUEST
    if not ISpecialPasteInProgress.providedBy(request):
        return
    annotations = IAnnotations(object, None)
    if annotations is None:
        # Annotations on this object are not supported.  This happens
        # e.g. for SyndicationInformation, ATSimpleStringCriterion,
        # and WorkflowPolicyConfig, so it is quite normal.
        return
    if object is event.object:
        original = event.original
    else:
        # Use the path minus the top level folder, as that may be
        # copy_of_folder.
        path = '/'.join(object.getPhysicalPath()[1:])
        try:
            original = event.original.restrictedTraverse(path)
        except:
            logger.error("Could not get original %s from parent %r", path,
                event.original)
            raise
    annotations[ANNO_KEY] = original.getPhysicalPath()
    logger.debug("Annotation set: %r", '/'.join(original.getPhysicalPath()))
def update_cloned_object(object, event):
    """Update the cloned object.

    Now the new (cloned) object has an acquisition chain and we can
    start doing interesting things to it, based on the info of the old
    object.
    """
    request = object.REQUEST
    if not ISpecialPasteInProgress.providedBy(request):
        return
    annotations = IAnnotations(object, None)
    if annotations is None:
        logger.debug("No annotations.")
        return
    original_path = annotations.get(ANNO_KEY, None)
    if not original_path:
        logger.debug("No original found.")
        return
    logger.debug("Original found: %r", original_path)
    # We could delete our annotation, but it does not hurt to keep it
    # and it may hurt to remove it when others write subscribers that
    # depend on it.
    #
    # del annotations[ANNO_KEY]
    original_object = object.restrictedTraverse('/'.join(original_path))
    wf_tool = getToolByName(object, 'portal_workflow')
    wfs = wf_tool.getWorkflowsFor(original_object)
    if wfs is None:
        return
    for wf in wfs:
        if not wf.isInfoSupported(original_object, 'review_state'):
            continue
        original_state = wf.getInfoFor(original_object, 'review_state',
                                       _marker)
        if original_state is _marker:
            continue

        # We need to store a real status on the new object.
        former_status = wf_tool.getStatusOf(wf.id, original_object)
        if former_status is None:
            former_status = {}
        # Use a copy for good measure
        status = former_status.copy()

        # We could fire a BeforeTransitionEvent and an
        # AfterTransitionEvent, but that does not seem wise, as we do
        # not want to treat this as a transition at all.
        try:
            wf_tool.setStatusOf(wf.id, object, status)
        except WorkflowException:
            logger.warn("WorkflowException when setting review state of "
                        "cloned object %r to %s.", object, original_state)
        else:
            logger.debug("Setting review state of cloned "
                         "object %r to %s.", object, original_state)
            # Update role to permission assignments.
            wf.updateRoleMappingsFor(object)

    # Update the catalog, especially the review_state.
    # object.reindexObjectSecurity() does not help though.
    object.reindexObject(idxs='review_state')