7

Let's say I want to add some post_meta into the "new" revisions UI. I'm saving some meta on revisions only, and want to display it on this screen.

enter image description here

Normally, I'd open up wp-admin/revisions.php look for some hooks and code away. But the revisions page is all done with Backbone. I don't know Backbone (totally willing to learn). However, I can't find anything about how to modify existing admin Backbone views in WordPress.

Has anyone successfully modified this interface and can provide an example of how it can be done?

  • An ajax call that appends the returned data to .diff-meta-to should accomplish what you want. Which event to watch is where I seem to have hit a wall. Custom events are defined in wp-admin/js/revisions.js The template for the revision-meta section is in wp-admin/revision.php around line 163 (script id="tmpl-revisions-meta") – nvwd Jun 24 '14 at 17:38
7

Unfortunately, there is no standardized best practice instituted by WordPress for hooking into Backbone templates. There have been plans suggested to bring the familiar filters and actions API to Javascript in WordPress, but there is a lack of traction in this movement. Carl Danley created a library which does just that, which is helpful if you're trying to build/jerry-rig code in the most minute way to all you to do what you're trying to do.

Basically, you're trying to override what's going on in revisions.view.Meta's microtemplate.

There isn't a simple way to override the microtemplate markup itself. What I'd suggest is overriding the .template() method of any constructor that extends from revisions.view.Meta, those being revisions.view.MetaFrom and revisions.view.MetaTo. To do this, here's some boilerplate:

add_action( 'admin_footer-revision.php', function() {
    $post = get_post();
    ?>
    <script id="tmpl-revisions-meta-override" type="text/html">
        <# if ( ! _.isUndefined( data.attributes ) ) { #>
            <div class="diff-title">
                <# if ( 'from' === data.type ) { #>
                    <strong><?php _ex( 'From:', 'Followed by post revision info' ); ?></strong>
                <# } else if ( 'to' === data.type ) { #>
                    <strong><?php _ex( 'To:', 'Followed by post revision info' ); ?></strong>
                <# } #>
                <div class="author-card<# if ( data.attributes.autosave ) { #> autosave<# } #>">
                    {{{ data.attributes.author.avatar }}}
                    <div class="author-info">
                    <# if ( data.attributes.autosave ) { #>
                        <span class="byline"><?php printf( __( 'Autosave by %s' ),
                            '<span class="author-name">{{ data.attributes.author.name }}</span>' ); ?></span>
                    <# } else if ( data.attributes.current ) { #>
                        <span class="byline"><?php printf( __( 'Current Revision by %s' ),
                            '<span class="author-name">{{ data.attributes.author.name }}</span>' ); ?></span>
                    <# } else { #>
                        <span class="byline"><?php printf( __( 'Revision by %s' ),
                            '<span class="author-name">{{ data.attributes.author.name }}</span>' ); ?></span>
                    <# } #>
                        <span class="time-ago">{{ data.attributes.timeAgo }}</span>
                        <span class="date">({{ data.attributes.dateShort }})</span>
                    </div>
                    Your custom text here
                <# if ( 'to' === data.type && data.attributes.restoreUrl ) { #>
                    <input  <?php if ( wp_check_post_lock( $post->ID ) ) { ?>
                        disabled="disabled"
                    <?php } else { ?>
                        <# if ( data.attributes.current ) { #>
                            disabled="disabled"
                        <# } #>
                    <?php } ?>
                    <# if ( data.attributes.autosave ) { #>
                        type="button" class="restore-revision button button-primary" value="<?php esc_attr_e( 'Restore This Autosave' ); ?>" />
                    <# } else { #>
                        type="button" class="restore-revision button button-primary" value="<?php esc_attr_e( 'Restore This Revision' ); ?>" />
                    <# } #>
                <# } #>
            </div>
        <# if ( 'tooltip' === data.type ) { #>
            <div class="revisions-tooltip-arrow"><span></span></div>
        <# } #>
    <# } #>
    </script>
    <script>
    (function($) {
        wp.revisions.view.MetaFrom.prototype.template = wp.template('revisions-meta-override');
        wp.revisions.view.MetaTo.prototype.template = wp.template('revisions-meta-override');
    })(jQuery);
    </script>
    <?php
} );

And to explain that. We're hooking into the footer of the revisions admin page, so we're guaranteed that the constructors revisions.view.MetaFrom and revisions.view.MetaTo have been defined, but before the revisions module bootstrapper has been triggered. This is important, because we want to modify the constructors before they are put to use. I copied the contents of revisions.view.Meta's microtemplate verbatim into an overridden version of the template #tmpl-revisions-meta-override, but notice I've added Your custom text here in the template where it looks like you want your custom text. Then, I override both constructor's template() method, using the modified microtemplate's ID as a reference.

Voila. Now, it depends what you want to do with your custom text. If you want to plop some text in there, go for it. If you want to output text specific to that revision, you'll want to check out Carl Danley's aforementioned library, and trigger a filter hook passing along data so that you can output something specific to the data at hand.

I know, this is insane. But this is where we're at with overriding Javascript in WordPress Backbone templates. We should be having discussions about how we can bake in customizability like you're asking for into core, but we're not because there's a lack of interest/momentum. Feel free to spark conversation at our weekly WordPress core developer chats. I will be there.

  • this is great. I've got it working but the only issue is that when I try to write out something into the template from the action hook, it never shows up, likely because it's running before the view is rendered. If I stick it in a setTimeout with a delay of 100ms, it works. I couldn't find any events in revisions.js for when the template is finished rendering, is delaying it likely the only way? (code here: gist.github.com/jjeaton/ed4d22245943c2eaa4fa) – jjeaton Jun 25 '14 at 13:52
  • I modified the answer: You should use a filter hook, so your text can be output within the applyFilters call, and output inline in the template. You don't want to use a jQuery.text() to modify a microtemplate, use the affordance of Underscore.js template escaping. (code here: gist.github.com/ericandrewlewis/3f3f39dc7ebdc4a56681) – Eric Andrew Lewis Jun 25 '14 at 14:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.