Updating Manager Pages in a Plugin II

Adding buttons to the Create/Edit Resource Manager page with JavaScript in a plugin attached to OnDocFormRender


This is the second in a series of articles on dealing with issues created by operations performed in plugins that work on the Create/Edit Resource Panel in the MODX Manager. In this one, we'll look at how to insert buttons into the Create/Edit Resource Panel. Will also look at using heredoc syntax for quoted strings in PHP.

MODX logo

Getting a Handle on the Location

The first step in adding buttons to the page is getting the location of the existing buttons in JavaScript ("Save," "View," etc.). The best place for the new buttons was to the left of the existing ones.

Here is the JS code to find the div tag containing the buttons:

var hostdiv = document.getElementById('modx-action-buttons');
div = hostdiv.getElementsByClassName("x-toolbar-left-row");

Because your can chain functions in JavaScript, this code could also be written as:

div = document.getElementById('modx-action-buttons').getElementsByClassName("x-toolbar-left-row");

The second version is more efficient, but arguably more difficult to understand.


Button Code

Before we can insert the buttons, we have to create code for them. We'll designate them as editDraftButton, deleteDraftButton, and editOriginalButton, and use those for the variable names in both PHP and JavaScript, and a lowercase version with underscores between the words for the IDs of the buttons in HTML. You could put the button code in a chunk. It would slow things down some, but it would let users edit the button content more easily. Because our buttons are fairly simple, we'll just put their HTML content directly in the plugin's JS code. Here's the HTML code for the buttons:

<span id="stagecoach_edit_draft_button" class="x-btn x-btn-small stagecoach-link"><button>Edit Draft</button></span>

<span id="stagecoach_delete_draft_button" class="x-btn x-btn-small stagecoach-link"><button>Delete Draft</button></span>

<span id="stagecoach_edit_original_button" class="x-btn x-btn-small stagecoach-link">\
                <button onClick="window.location.href=\'' + '$siteUrl' + 'manager/?a=resource/update&id=' + $liveId + '\'">Edit Original</button>\
            </span>

The button code below is designed as a first step to make the buttons functional on their own. When we put everything together in later articles, we'll modify the code to make things more efficient. For example, we only need to get the buttonDiv, buttonRows, and buttonRow elements once.


Inserting the Buttons

Now that we have the buttons defined, here's the full JavaScript code for adding them to the left of the existing buttons:

    var buttonDiv = document.getElementById('modx-action-buttons');
    var buttonRows = buttonDiv.getElementsByClassName("x-toolbar-left-row");

    /* Inject Edit Draft button */
    var row = buttonRows[0];
    var editDraftButton = row.insertCell(0);

    editDraftButton.innerHtml = '
        <span id="stagecoach_edit_draft_button" class="x-btn x-btn-small stagecoach-link">
            <button>Edit Draft</button>
        </span>';

    /* Inject Delete Draft button */
    row = buttonRows[0];
    var deleteDraftButton = row.insertCell(0);
    deleteDraftButton.innerHtml =
        '<span id="stagecoach_delete_draft_button" class="x-btn x-btn-small stagecoach-link">
            <button>Delete Draft</button>
        </span>';

Using heredoc for Quoted Strings

If you have a complicated quoted string with variables inside it, it's often easier to use PHP's heredoc syntax. Basically you add a variable name at the top of the string, followed by <<< and a token, and put the same token at the end. By convention, the token is in uppercase letters, like this:

$name = 'MyName';
$text = 'SomeText';

/* heredoc quoted string */
$str = <<<TOKEN
My name is "$name".
Now, I am printing some '$text'.
TOKEN;

echo $str;

The heredoc syntax looks strange at first, but all that's happening in the heredoc section of the code above is assigning a string to the $str variable. Think of the << at the top and the TOKEN at the bottom as super quotation marks.

The nice thing about heredoc syntax is that you can mix in single and double quotes and variable with much less worry. The quotes will always be rendered as written and the variables will be translated into their values. The echo $str code above will display this:

My name is "MyName".
Now, I am printing some 'SomeText'.

Notice that the quotes (both kinds) become part of the string just as written.

If a variable in a heredoc section is complicated, it should be enclosed in curly brackets, like this: {$myArray[0]} or this: {$someFunction->myArray[1]}. When in doubt, you can use the curly brackets, since they will work with any variable.

Using the heredoc syntax makes things a lot easier for our example because it contains both single and double quotes, making it difficult to quote without escaping many of the quotes. Placing the code in a chunk would accomplish the same thing (with slower execution). Any variables in the code would have to be replaced with placeholders and the placeholder values would have to go in an array sent as a second argument to $modx->getChunk(). In StageCoach, most of the heredoc sections contain JavaScript code that no end user would ever edit. If you expect users to edit the string (e.g., the text of a Tpl chunk), you should definitely use a chunk.

Unfortunately, our heredoc code is more complicated than usual because we're creating JavaScript code, especially the part where we construct the links for the editDraft and editOriginal buttons, but we won't worry about that until later articles in the series. For now, we'll just create the buttons.


The Full Code (using heredoc)

Now that we have the JavaScript code for the buttons, we need to get it inserted into the page. We'll only do the two buttons that go on the Original Page — later, we'll introduce the code that determines which pages get which buttons. We'll use the $modx->regClientStartupScript() method, which will put the code in the head section of the page's HTML code. We also need to wrap the code in an Ext.onReady function so it won't execute until the page has been rendered and we can be sure that the HTML code the JavaScript is referencing exists on the page. Since extJS/modExt is loaded along with every Manager page, we might as well use it here. If the MODX Manager were ever re-written without extJS/modExt, we'd have to rewrite our code.

Making sure the page is fully rendered is particularly important with ExtJS/modExt, which creates much of each manager page on the fly. You can see this yourself if you do a "View Source" on a Manager page. You'll find that most of the page code is missing. If you examine the page in Firefox of Chrome Dev. Tools, you'll see a ton of stuff that isn't there in "View Source." The extra HTML code is rendered on the fly by the Manager JavaScript.

$button_script = <<<SCSCRIPT
Ext.onReady(function () {
    var buttonDiv = document.getElementById('modx-action-buttons');
    var buttonRows = hostdiv.getElementsByClassName("x-toolbar-left-row");

    /* Inject Edit Draft button */
    var row = buttonRows[0];
    var editDraftButton = row.insertCell(0);
    editDraftButton.innerHtml =
        '<span id="stagecoach_edit_draft_button" class="x-btn x-btn-small stagecoach-link">
            <button>Edit Draft</button>
        </span>';

    /* Inject Delete Draft button */
    row = buttonRows[0];
    var deleteDraftButton = row.insertCell(0);
    deleteDraftButton.innerHtml =
        '<span id="stagecoach_delete_draft_button" class="x-btn x-btn-small stagecoach-link">
            <button>Delete Draft</button>
        </span>';
});
SCSCRIPT;

$modx->regClientStartupScript($button_script);


How it Works

The first step in adding buttons to the page is getting the location of the existing buttons in JavaScript ("Save," "View," etc.). The new buttons will be inserted to the left of the existing ones.

Here is the JS code to find the div tag containing the buttons:

var hostdiv = document.getElementById('modx-action-buttons');
buttonRows = hostdiv.getElementsByClassName("x-toolbar-left-row");

modx-action-buttons is the outer div containing the buttons. We get that with the first line. In order to insert the buttons, we need to get the table collection of buttons inside that div. Because the buttons are in a table row with the class x-toolbar-left-row, we can get a collection of rows with that class by calling getElementsByClassName on the buttonDiv element we got in the first line.

There's only one element returned, but getElementsByClassName returns a collection, so we need to refer to it as buttonRows[0]. We want to insert the new buttons to the left of the existing ones so we insert our button with row.insertCell(0).

Notice that we've used the same code for both buttons. Once our first button is inserted, it becomes the leftmost button, so inserting the second button in front of it is done the same way.

Because you can chain functions in JavaScript, the code to get the buttonDiv could also be written as:

// buttonDiv var is no longer necessary
buttonRows = document.getElementById('modx-action-buttons').getElementsByClassName("x-toolbar-left-row");

The second version is more efficient, but arguably more difficult to understand and to debug.


Coming Up

The code above placed in a plugin attached to OnDocFormRender will add the buttons to the Create/Edit Resource panel, but there are a couple of serious problems. First, the buttons won't do anything. Second, the buttons will appear on every resource of the site. We'll deal with those problems in upcoming articles.


Looking for high-quality, MODX-friendly hosting? As of May 2016, Bob's Guides is hosted at A2 hosting. (More information in the box below.)



Comments (0)


Please login to comment.

  (Login)