Updating a published resource from a draft copy

[[UPDATE:  A slightly updated version of the plugin code, with Template Variable syncing, has been posted here]]

I know, I know: long-time-no-blog.

I had told myself I wasn’t going to do any more blogging until I had my new and improved blog site set up on a MODx Revolution install.  But I finally broke down today because

  1. Setting up the new website is taking a lot longer than expected (mostly due to a lack of spare time)
  2. Today I did something so cool I can’t help but write about it.

Today I made major inroads toward solving one of my longest-standing issues with MODx publishing:  the need to work on a “draft” copy of an already-published document, and publish from the “draft” copy to the published version.  In other words:

  • A user creates and publishes a resource.
  • Later, the user needs to make “work-in-progress” revisions to the resource.  They are going to completely overhaul the original content, but it is going to take time, and they don’t want to publish the changes until they are ready.

MODx (and most other CMSes, as far as I know) have a simple published/unpublished model.  If you publish a document, there is no easy way to “work on” that document without affecting the published version. (Because there is only one ‘version’).  As Shaun McCormick recently wrote to me,

There’s a lot [involved in] workflow in CMSes, and to be frank, there’s a reason most CMSes don’t have it. It’s complex, and requires a lot of work.

Fair enough.  As I’ve thought about this problem over the last six months or so, it seems that every potential solution turned out to be much more complicated than I thought it would be, to the point that it exceeded my abilities and grasp of the concepts.

I don’t know if its because I’ve learned a lot more about MODx, or if its because I’ve reduced the scope of what I’m trying to do.  (Probably a combination of both).  But over the weekend, a light went on in my head.  I realized that the only “workflow” concepts I really needed were:

  • The ability to mark a resource as a “draft”
  • The ability to specify which resource it is a “draft” of. (To link it to a different, published resource).
  • The ability to (when desired) update the published resource with the revisions in the draft resource.

Here’s what I came up with.  Its by no means perfect, but it does exactly what I need it to:

  1. Create a “draft” copy of a published resource
    Start by just creating a copy, period. Click the “duplicate” button, and give it a title.

    Here I’ll use the same title as the original, but with “(draft)” appended.

    That works well in the resource tree – you can pretty clearly see that resource #4 is a draft copy of resource #2.

  2. Unpublish the draft copy.
    This is a draft, after all – the whole point is that it isn’t published. You don’t want it showing up in your menus and such.

    There – that’s better.

  3. Specify which published resource this is a draft of
    To do this, I’m just going to add the resource ID of the published resource to the title of the draft resource.  The new title looks like this:

    Note the dashes before and after the id: “(draft-2-)”.  In the next step I’m going to be parsing out the ID number from the title of the draft resource, so it is important to have a consistent & recognizable pattern to search against. You can use any pattern you like, so long as you know how to write a regular expression to identify it and parse out the number.
  4. Write the plugin that does all the magic.What we need now is to capture the user’s intent to “promote” the draft copy to published status.  While working on the draft, the user can save over and over again to their liking, while the original resource remains unchanged. But when the user checks the “Published” box on the draft resource, that’s where we need some magic to happen.A MODx Plugin is a PHP script that is tied to a certain System Event. Here we want to tie in to the action of saving of a resource, and potentially (based on certain criteria) make changes to another resource (the production resource).In human terms, we want our Plugin to do this:
    • Run each time a resource is saved (“OnDocFormSave” is the name of the System Event).
    • Test for whether the resource is a “draft” resource AND whether the “Published” box was checked
    • If yes to both, then:
      • parse out the ID of the production resource from the title of the draft resource
      • update the production resource with all the content from the draft resource
      • save the production resource
      • “un-publish” the draft resource
      • save the draft resource

    (Note: we need to “un-publish” the draft resource because we don’t really want the draft resource published; we are repurposing the “Published” checkbox to really mean “copy this in to production”).

    Here’s what the plugin code looks like:
    <?php
    $pagetitle = $resource->get(‘pagetitle’);
    //if the “draft” flag is in the title AND the user “published” the draft
    if (
    (preg_match(‘/\(draft-(?P<id>\d+)-\)/’, $pagetitle, $results ))&&
    ($resource->get(‘published’))
    ) {

    // the ID of the resource  to publish to
    $prodID = $results[‘id’];

    // if this represents a real & published resource
    if ($prodResource = $modx->getObject(‘modResource’,
    array(‘id’=>$prodID,’published’=>1))) {

    // update the production resource with the values from the draft resource
    $prodResource->fromArray($resource->toArray());

    //take out the “draft” token before setting pagetitle
    $prodResource->set(‘pagetitle’, str_replace($results[0], ”, $pagetitle));

    //save changes to the production resource
    $prodResource->save();
    }

    //unpublish the draft resource
    $resource->set(‘published’, 0);

    //save changes to the draft resource
    $resource->save();
    }
    return;
    ?>

    That “preg_match” business is what looks for my particular draft flag pattern “(draft-99-)”.
    That just about does it. One more change to the code:  we can’t overwrite the ‘alias’ of the production resource with the alias of the draft resource.  Since alias dictates the URL when friendly URLs are used, we probably don’t want to be messing with the alias of the production resource at all.  So we’ll have to modify the code to preserve the original alias:

    <?php
    $pagetitle = $resource->get(‘pagetitle’);
    //if the “draft” flag is in the title AND the user “published” the draft
    if (
    (preg_match(‘/\(draft-(?P<id>\d+)-\)/’, $pagetitle, $results ))&&
    ($resource->get(‘published’))
    ) {
    // the ID of the resource  to publish to
    $prodID = $results[‘id’];
    // if $prodID represents a real & published resource
    if ($prodResource = $modx->getObject(‘modResource’,
    array(‘id’=>$prodID,’published’=>1))) {
    $prodAlias = $prodResource->get(‘alias’); // we’re gonna need this
    // update the production resource with the values from the draft resource
    $prodResource->fromArray($resource->toArray());
    $prodResource->set(‘alias’, $prodAlias); // set alias back to original value;
    //take out the “draft” token before setting pagetitle
    $prodResource->set(‘pagetitle’, str_replace($results[0], ”, $pagetitle));
    $prodResource->save();
    }

    //unpublish the draft resource
    $resource->set(‘published’, 0);
    $resource->save();
    }

    return;
    ?>

  5. Tie the Plugin to the OnDocFormSave System Event
    The plugin will only work if it is tied to the proper system event.  Here we need to tie it to “OnDocFormSave” which fires just after a resource is saved through the manager form:

    [Side note: The “OnDocFormSave” event has a parameter called “resource”, which refers to the resource being saved. That’s where $resource gets its value in the first line of the plugin code.]

There you have it.  Now whenever the draft copy is saved with the “Published” checkbox checked, the “production” copy will be synched with all the changes.

One thing this plugin won’t do (yet):  update Template Variables. I think I know to make that enhancement, but wanted to get this preliminary version out as soon as possible to solicit feedback.

So… let me know what you think.  Does anyone other than me see this as potentially useful?  Or see a better way of doing it?  Lets talk.

Advertisements

14 responses to this post.

  1. Posted by rthrash on August 2, 2010 at 3:10 pm

    Very cool James. Can’t wait for TVs to be handled with this. Keep up the great work!

    Reply

  2. This is indeed very cool James. We build & manage restaurant websites mostly, and it’s rare to get a new (food) menu updated to the client’s satisfaction in one go. This could be hugely useful. Shout when you’ve got the TVs working too. Congrats & thanks!

    Reply

  3. What a great little trick! Looking forward to seeing your TV enhancement.

    Reply

  4. Great approach! 🙂

    Reply

  5. Thanks for the kind words everyone.

    I hope to get template variables working later this week. I’d also eventually like to automate the “create a draft copy” process so it is simpler for a novice end user. I see this eventually as a complete package- you configure your own draft token in the system settings so it looks the way you want it to.

    I’ll also need to figure out permissions issues – I’m guessing that user permissions don’t effect what a plugin does. Right now there’s nothing that would prevent a user from overwriting any resource ID # they want to using this method. I’ll have to test that out and get some insight from the permissions wizards…

    Reply

  6. One idea:

    Instead of defining in the title that the resource is a draft of another resource (draft-ID-), you could create a TV for the resource, like “Draft of” where you can input the ID of the resource. Your plugin would check the TV value, instead of the title.

    (albeit, that’s a bit of abusing of the concept of TVs…)

    Anyhow, it’s cool what you did! 😉

    Reply

    • Thanks Mihai. I did think a lot about using a TV-based approach. …Believe me, I’m not at all opposed to abusing concepts to get things done! The reason I decided against it ultimately though is that TVs are tied to templates – I’d have to make sure to add that TV to every single template.

      I like that the token appears on the page title in the resource tree, for readability. But I don’t like that it is editable – ideally I think it should be set once upon creation of the draft doc, and then not editable after that… it doesn’t really make sense to decide suddenly that it should be a draft of some other document. I’ve thought about using plugins to append it to the title post-save and take it off upon form edit.

      Lots of little details to iron out… but I’m very encouraged by the initial response. Glad to see this was worth doing.

      Reply

      • Yeah, I know what you mean. It’s also something I’d ponder about.

        We should have Resource Variables as well. Look through the API – maybe you can add a new field to the resource.

      • I know this is old & there’s an updated version – but you can set hidden TVs as well, which stores the value but is not visible in the TVs tab. There’s no visual queue (minus the same name) – but it is stored.

  7. Posted by Wendy on August 3, 2010 at 6:55 am

    Phew! Very interesting!!

    For Revolution only, or Evo, too??

    Thanks for your hard work, and for sharing it 🙂

    Reply

    • I wrote it for Revolution… I don’t know enough about how Plugins work in Evo to know whether the same thing could be done on that platform. Perhaps others can chime in on that(?)

      Reply

  8. Just in case anyone missed it, I posted a revised copy of the plugin today that includes setting TVs.
    https://ichosemodx.wordpress.com/2010/08/03/publishing-a-resource-from-a-draft-copy-revised-now-with-tvs/

    Reply

  9. What version of MODx was this created to work on? Just tried this on REVO 2.2.7 and it does not work… published the draft resource does not unpublish the other resource…?

    Reply

  10. This is a wonderful article. I would like to suggest you that please keep sharing such type of info. I really found this article very informative. It is what i was searching for. 
Thanks
    dini islami sohbet

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: