Panelizer and Workbench Moderation Can Get Along
Panelizer and Workbench Moderation Can Get Along
Tobby Hagler | Director, Engineering
July 31, 2013
As frequent users of Workbench Moderation are aware working with other contrib modules and node revisions can be troublesome (to say the least). Many modules were never intended to have revisioning play any sort of role in their functionality, and few things expose that fact more than Workbench Moderation. However, the inherent benefits of using Workbench Moderation as a mainstay of workflow and editorial process far out weigh the work needed to make other modules cooperate. Another useful and ubiquitous module is Panelizer. Panelizer lets you attach panels to any content type in your content management system (CMS) (it doesn't have to be exclusively the Panel content type anymore), and you can even go so far as setting default layouts and configurations per content type (or vocabulary or term or entity...). However, frequent users of Panelizer know that revisioning and moderation is not this module's strong suit. What Panelizer offers in the way of customization without code changes is so useful, that most site developers are willing to overcome that downside for the benefits Panelizer brings to the table.
It's not going to work out of the box
For starters, these two modules are at odds in terms of editorial and site management workflow. To quote Earl Miles, "I'm not really sure there's a clean way to make these two modules work together."[1] There has been some work done in the past few months to help, including:
- Panelizer is incompatible with moderation -- an issue with loads of discussion and patches, but no complete resolution yet
- Workbench Moderation Panelizer -- a sandbox module to act as a bridge, but not a full-fledged contrib release yet
Here's what we did to make it work
To create an effective workflow and content management experience for the recently launched Pac-12 Conference Drupal CMS platform, we needed to make these modules work together. To get close to a resolution, we applied a number of patches to Panelizer and Workbench Moderation. These include:
- Panelizer: Workbench Moderation creates a new revision, which immediately makes the panelizer layout outdated - Patch
- Workbench Moderation: Panelizer is incompatible with moderation - Patch
However, there was still one more problem. When using Panelizer to create default layouts, and then edting a given node's layout with the Inline Panel Editor, then editing the node iteself (as in, creating a new draft), the layout changes previously made via IPE will vanish. I could write a whole blog post surrounding the fact that in your editorial workflow for a given Node, you may have the current revision, the published revision, and the revision you're working from (and starting a draft from), and these may not all be the same thing. I don't want to go into the nightmare of describing all the possible scenarios and edge-cases, but I wanted to at least mention that said nightmares exist.
Where's my layout changes?
Internally, each Panelizer layout change is associated with a revision (in part) by it's Display ID (DID). When using the IPE to change the Node's layout, a new DID is assigned in the {panelizer_defaults} table. When creating a new draft from an unpublished Node revision, the DID value was getting reset back to 0, which represents the default layout state. This is what causes the Panelizer customizations to disappear. The trick becomes making sure the value of DID is the same as the DID of the revision from which you are creating the new draft. Luckily, in hook_node_update(), the revision ID of the revision your new draft is created from is available as $node->old_vid. This makes it very easy to find the Panel's DID from the previous revision so that it can be applied to the new draft revision. This can be done with the following code:
<?php
function MYMODULE_node_update($node) {
if (!empty($node->old_vid)) {
// fetch the did from the old revision
$old_did = db_query("SELECT did FROM {panelizer_entity} WHERE entity_id = :nid AND revision_id = :oldvid ORDER BY revision_id DESC",
array(":nid" => $node->nid, ":oldvid" => $node->old_vid))
->fetchField();
if (!empty($old_did) && !empty($node->panelizer['page_manager']) && empty($node->panelizer['page_manager']->did)) {
$node->panelizer['page_manager']->did = $old_did;
}
}
}
?>
An interesting side-note about this is that the new draft can be created from any previous revision, and the layout changes as of that particular revision will be used, instead of the latest revision's layout or the currently published revision's layout.
Footnotes.
1. https://drupal.org/node/1402860#comment-5546622 - He actually says a lot more than that, but what's a blog post without a little hyperbole?