Mar 31, 2015

Why patch?

Unlike WordPress, Drupal, and Joomla!, TERMINALFOUR is very particular about where content appears in a template. The concept of loops is still present, but handled in an unusual manner. You get one main loop, which pulls in all page content marked as the text/html format, and then a predefined set of additional loops called “navigation tags” that can be inserted into templates or content types. The placement of the main loop is determined by where a template’s header ends and its footer begins, so you simply slice your HTML in half at the main content div, paste each half into the appropriate box, and the implied main loop runs invisibly between them. Content pulled using the main loop can be edited through the Direct Edit interface; however, the same does not hold true for navigation tags.

Immediately a serious flaw presents itself: If you get to choose only one insertion point in the HTML for editable content, how could you make two editable columns?

You could pull in #column2’s content with a navigation tag, but then when the page loads in Direct Edit no editing boxes will show up over any content in #column2.

You could drop all content into #column1 and then use a script on the page to identify blocks of content that should appear in #column2 and pull them over; however, Direct Edit has no way of knowing that this has been done, so again the content in #column2 ends up uneditable.

Close all the tags!

This.

You could, if you were feeling a particular disregard for future-proofing, start all #column2 content types with a </div>, fail to close them out, specify a fixed width and float right, then use a navigation tag to apply a class to the first column that restricts its width if #column2 content is detected. This would create a second editable column, but any #column1 content appearing after #column2 content in the editor would be thrown off the side of the page. Incidentally, this was the practice we’ve used until very recently, and it proved a monumental task to eradicate.

The first solution isn’t viable because Direct Edit simply doesn’t know about any #column2 content, and the third solution is just insanity. And we can’t just not have a second column, because we already have about ten thousand of them created using the third solution. That leaves solution 2, which incidentally turns out to be a very simple and future-proof method if you’re willing to write a small patch that either:

  1. Repositions Direct Edit’s editing boxes to the content’s new location (Site Manager 7)
  2. Clones the page content to its correct location when the user hits a “preview” button (Site Manager 8).

Moving the Direct Edit boxes in Site Manager 7 is a pretty simple exercise. You just put a script in the template that checks if you’re editing the page (match the current URL against a simple regular expression), and if true then load the patch. At this point, solution 2’s script has already run, so #column2 content is now in the second column, but its editing boxes have gone missing. The patch looks at the contentID of each content item on the page, measures its width, height, distance from left, and distance from top, and then applies those values to each corresponding editing box—immediately all is well again. If the window is resized, the patch (which has a window.resize listener) runs the process again, so the boxes always stay right on top of their content. It works great.

Unfortunately for our simple patch, Site Manager 8’s Direct Edit is all-new. Totally different code, totally different UI, totally different (and better) approach to just about everything.

How TERMINALFOUR Direct Edit works

A page being edited in Site Manager 8 consists of a toolbar, an iframe containing the page to be edited, and a Backbone JavaScript application that monitors the controls on the toolbar and injects editable boxes into the iframe. Unlike the previous Direct Edit which overlaid a square around editable content items and then popped out a modal to do the actual editing, the new Direct Edit wraps editable content items in additional HTML to form them into truly inline-editable items.

While this results in an extremely desirable user experience, it does come with a few drawbacks. The editable boxes that are wrapped onto the content items are in block format, so one comes after the other and they never overlap. This is a huge change from our current setup in which floating items like slideshows and videos can be placed onto a page and have written content wrap around them. In the new direct edit, a slideshow will appear as it would normally appear, but the content that usually wraps around it will be pushed down below it in a separate editable box.

The real challenge this introduces is that the editable boxes are no longer on the page independently of the content, so moving a piece of content to a new location and then using the patch to have the editable box chase after it is no longer a viable option. You’d need to move the editable box itself, but then the Backbone application would lose its references to it, and it would no longer be editable.

Also, patching Site Manager 8 is not so easy as patching Site Manager 7. For one, any scripts loaded are inside an iframe, and second, Direct Edit now strips all script elements out of the page being edited–an understandable move to some extent, but what modern framework doesn’t need to be initialized with a script?

Reaching out

That said, we work on templates that don’t properly close out basic HTML elements, so we’re perfectly equipped to handle a script disappearing here and there. To run the patch, it needs to be injected into the parent window of the iframe—but how do you run a script when all the scripts have been stripped from the template?

The answer, it turns out, is to call it after the stripping has happened. I just piggybacked it onto the site’s logo, like so:

<img id="ribbon" src="http://107.170.46.11/SR/edge/images/mst_ribbon.png" onload="if(window.parent.sm_config){window.parent.$.getScript('http://107.170.46.11/SR/edge/js/st-de-patch.js');}" />

Bit of a hacky solution, but it does the trick.

The migratory column

Still, we have the problem that our second solution (moving the content for #column2 into position with a script) will no longer work in Direct Edit since all scripts have been stripped (it still works fine in preview and when live).

To solve this problem, a “Preview” button is added to T4’s interface. When users click it, the page auto-magically transforms into what it will look like when live. Items in the second column move over where they should be, wrapping text wraps around floating items, etc.

Really what’s happening is the patch is scanning through the content area, copying out any content, hiding the Direct Edit area entirely, and placing this content into empty containers arranged how the page will actually look. A few CSS animations make the whole thing look pretty believable.

Source available to other TERMINALFOUR admins on request.

Fork me on GitHub