Warning

This document hasn't been checked for compatibility with current versions of Plone. Use at your own risk.

Moving Objects Using Workflow

by Russell Hires last modified Dec 06, 2009 09:27 PM
This recipe shows how to automatically move objects out of the member's home directory and into a named subdirectory of the portal root. It also emails the content owner, notifying him or her of the status change.

This howto was originally written by bobvin, and is originally found on the zopelabs.com website. He in turn credits runyaga for some of this code. Any changes from what is on the zopelabs.com website found here are mine, and mine alone. Hopefully this will be an improvement. I've found that people on the #plone chat channel ask about how to do this. Here we go:

First, here's the script. You'll want to put this in your plone_workflow/scripts tab. (Add Script (Python), then paste this over the default script that's there, and save):

    ## Script (Python) "move_published_content"
    ##bind container=container
    ##bind context=context
    ##bind namespace=
    ##bind script=script
    ##bind subpath=traverse_subpath
    ##parameters=state_change
    ##title=Move published content to subfolder of portal root
    ##
    contentObject = state_change.object
    objectOwner = contentObject.Creator()
    ownerEmail = context.portal_membership.getMemberById(objectOwner).email
    objectType = contentObject.getTypeInfo().getId()
    objectTitle = contentObject.TitleOrId()
    actorId = context.portal_membership.getAuthenticatedMember().getId()
    actorEmail = context.portal_membership.getMemberById(actorId).email
    try:
        mailhost=getattr(context, context.portal_url.superValues('Mail Host')[0].id)
    except:
        raise AttributeError, "Can't find a Mail Host object"
    mSubj = objectType+': "'+objectTitle+'" published'
    mMsg = 'From: "'+actorId+'" <'+actorEmail+'>\n'
    mMsg = mMsg+'To: "'+objectOwner+'" <'+ownerEmail+'>\n'
    mMsg = mMsg+'Subject: '+mSubj+'\n\n'
    mMsg = mMsg+'Your '+objectType+' entitled "'+objectTitle+'" has been published.\n\n'
    mMsg = mMsg+'It has been moved out of your personal folder and into the main website.\n\n'
    mMsg = mMsg+'The revision history follows:\n\nCreated'
    for revision in state_change.getHistory():
      if revision.get('action'):
        mMsg = mMsg+revision.get('action','')
      if revision.get('actor'):
        mMsg = mMsg+' by '+revision.get('actor','')
      if revision.get('Date'):
        mMsg = mMsg+' on '+container.toPortalTime(revision.get('Date'))
      if revision.get('comments'):
        mMsg = mMsg+': '+revision.get('comments','No Comment')
      mMsg = mMsg+'\n'
    mMsg = mMsg+'\nThank you.\n'
    mailhost.send(mMsg, ownerEmail, actorEmail, mSubj)

    # The email goes out first (above) and then the object gets moved (below)

    dest_folder = context.portal_url.getPortalObject()
    mappings={'Document':'docs',
              'Event':'events',
              'File':'files',
              'Image':'pics',
              'Link':'links',
              'News Item':'news'}
    if objectType in mappings.keys():
        dest_folder = dest_folder[mappings[objectType]]
    content_object = state_change.object
    content_folder = content_object.aq_parent
    content_id = content_object.getId()
    dest_folder.manage_pasteObjects(content_folder.manage_cutObjects(content_id))

Explanation

The workflow script automatically sorts the published objects by type.

Sub-folders were created under the portal root named "events", "files", "pics", "links", and "news."

The code is fairly simple; it's mostly cut-and-pasted from examples by Runyaga and others.

First, it just emails the content owner notifying him or her of the status change.

Then it cuts the published object out of its present location (usually the Member's home folder) and pastes it into a subfolder of the portal_root.

This will result in an object-not-found error when publishing the object, because the standard portal_navigation redirects to a view of the affected object (in its original location) after a status change.

To get around this, I used plone's content_status_modify script to do the redirection. You have to do this for each type you will be moving. It's important to note that content_status_modify is controller page template (.cpt). (For more information on controller page templates, see the excellent Form Controller Tutorial)

  • Go to portal_skins/plone_form_scripts/content_status_modify
  • Select the "Actions" tab. You should see something that says "Add a New Form Action Override."
  • In the "status" field you want to put "success"
  • In the "context" field, you want to put the type of object to be moved. For each content type, select it from the dropdown list. As in the example, you would select "News" from the dropdown list.
  • The "action" will be "traverse_to" and finally the "argument" will be "string:publish_redirect" Then click "Add"
  • In your "custom" folder, create a new Controller Python Script. Give it the name "publish_redirect" Click the "Add and Edit" button.
  • Change the script by replacing what's there with this:
            portal_workflow = context.REQUEST.get('portal_workflow')
    
            if portal_workflow == 'publish':
                state.set(status='redirect')
            else:
                state.set(status='success')
    
            # Always make sure to return the ControllerState object
            return state
    

And Save Changes.

  • As before, select the "Actions" tab. You should see something that says "Add a New Form Action Override."
  • In the "Status" field you want to put "success", "Context" is going to be "News", the "Action" will be "traverse_to", and "Argument" will be "string:view". (This is what keeps you in the same place if you change the workflow state to something other than "Publish".) Now you need to add another override, everything being the same except that "Action" will be "redirect_to" and "Argument" will be "string:news". (If "news" is not the name of the folder, just put the name of the folder there).
  • For each type you want to move to a folder with the name of the type, lather, rinse, repeat.

To install this script, add it to the "scripts" folder of the appropriate workflow under your portal_workflow object.

If you want Reviewers to be able to publish items with this script, you need to give it the Manager Proxy Role. Just go to the [Proxy] tab of the script, click on the "Manager" role, and then click [Save].

Click on the "transitions" tab of your workflow, select the appropriate transition (the default is "publish") and select this script in the "Script (after)" drop-down box.

If everything is working properly, your newly published object will be moved to its new home, and you'll be redirected to the folder where the newly published object is now living, so you can verify that it's there.

In a future version of this script, we'll deal with checking for items in the destination folder that have the same name.


Contribute

Something wrong or out of date? Anybody can edit or create a new article in the knowledge base. Simply create an account on this site, log in, and click the Edit button to contribute.