Skip to content

English plugin dev 4 3

semuel edited this page Apr 25, 2012 · 6 revisions

Developing a transformer plug-in

What is a Transformer plugin?

Transformer plugin is a bad idea. It is an API failure. And it is useful, sometimes.

Transformer plugin is changing a management screen, adding elements that are unique to this plugin. It is done in a callback (usually “template_param.<template_name>”, as it gets the soon-to-be rendered template as parameter, and also the template parameters) and using the template API to add / remove / change elements in the template.

It is a bad idea because: 1. you are changing a template in a plugin on the fly, (making debugging difficult) and 2. you are relying on current template structure, which can change in the next version. or other plugin might change it before you, on the fly.

But still, it is very useful, and for example Six Apart own Custom Fields addon uses this a lot, for injecting the custom fields editing inputs in the various edit screens.

Creating a Transformer Plugin

Specification

  • Hide the ‘Extended’ tab in the entry-edit screen
    • 「お客様から自分が必要無いな機能を外してほしい」と言われたと仮定
  • DOM ID=keywords の次にURLを記入できる欄を追加する
    • URL欄が空白の場合は"http://www.example.com/"を入力する
    • URL欄は表示だけで、保存、修正などできなくてよい(あくまで表示例)

config.yaml

id: MyPlugin13
key: MyPlugin13
name: <__trans phrase="Sample Plugin Transformer">
version: 1.0
description: <__trans phrase="_PLUGIN_DESCRIPTION">
author_name: <__trans phrase="_PLUGIN_AUTHOR">
author_link: http://www.example.com/about/
doc_link: http://www.example.com/docs/
l10n_class: MyPlugin13::L10N

callbacks:
    MT::App::CMS::template_source.edit_entry: $MyPlugin13::MyPlugin13::Transformer::hdlr_template_source_edit_entry
    MT::App::CMS::template_output.edit_entry: $MyPlugin13::MyPlugin13::Transformer::hdlr_template_output_edit_entry
    MT::App::CMS::template_param.edit_entry: $MyPlugin13::MyPlugin13::Transformer::hdlr_template_param_edit_entry

Commentary

Note the three callbacks registered in config.yaml

  • MT::App::CMS::template_source.(template_name): $(Handler)::(Perl Module)::(Perl Function)
    • template_source is a hook-point to change the raw template text before processing
    • This can be applied to all the templates in $MT_DIR/tmpl/cms. System setting templates are conf_xxx.tmpl, listing templates are list_xxx.tmpl, and new / modifying screens are edit_xxx.tmpl. In this example, we are modifiying the edit screen, that is using the edit_entry.tmpl file. Note that in the callback the name of the file is used without the extension.
  • MT::App::CMS::template_output.(template_name): $(Handler)::(Perl Module)::(Perl Function)
    • template_output is the hook point for changing the resulted page. (after the rendering)
    • Here too we are modifying the entry edit screen, edit_entry
  • MT::App::CMS::template_param.(template_name): $(Handler)::(Perl Module)::(Perl Function)
    • template_param is the place to modify the parameters given to the template, and have DOM-like access to the different elements (tags) in the template
    • Here too we are modifying the entry edit screen, edit_entry

MyPlugin13/Transformer.pm

package MyPlugin13::Transformer;
use strict;

sub hdlr_template_source_edit_entry {
    my ($cb, $app, $tmpl_ref) = @_;
    my $old = <<EOF;
                        <div class="tab" mt:command="set-editor-extended" mt:tab="extended">
                            <label><a href="javascript:void(0);"><__trans phrase="Extended"></a></label>
                        </div>
EOF
    $old = quotemeta($old);
    $old =~ s!(\\ )+!\\s+!g;

    my $new = "";

    $$tmpl_ref =~ s!$old!$new!;
}

sub hdlr_template_output_edit_entry {
    my ($cb, $app, $tmpl_str_ref, $param, $tmpl) = @_;

    # do something
}

sub hdlr_template_param_edit_entry {
    my ($cb, $app, $param, $tmpl) = @_;

    my $host_node = $tmpl->getElementById('keywords');
    my $innerHTML = '<input type="text" name="url_field" id="url_field" class="full-width" mt:watch-change="1" value="<mt:var name="url_field" escape="html">" autocomplete="off" />';

    my $block_node = $tmpl->createElement(
        'app:setting',
        {
            id => 'url_field',
            label => 'URL',
            label_class => 'top-label',
        }
    );

    $block_node->innerHTML( $innerHTML );
    $tmpl->insertAfter($block_node, $host_node);

    $param->{url_field} = "http://www.example.com/" if !$param->{url_field};
}

1;

Commentary

package MyPlugin13::Transformer;
use strict;
  • The name of the package, and don’t forget to use use strict
sub hdlr_template_source_edit_entry {
    my ($cb, $app, $tmpl_ref) = @_;
    my $old = <<EOF;
                        <div class="tab" mt:command="set-editor-extended" mt:tab="extended">
                            <label><a href="javascript:void(0);"><__trans phrase="Extended"></a></label>
                        </div>
EOF
    $old = quotemeta($old);
    $old =~ s!(\\ )+!\\s+!g;

    my $new = "";

    $$tmpl_ref =~ s!$old!$new!;
}
  • sub hdlr_template_source_edit_entry{}
    • Parameters:
      • $cb – reference to the callback itself.
      • $app – MT application handle.
      • $tmpl_ref – a reference to the raw source text of the template
    • $old contains the part of the part of the template that we want to modify
    • We are using quotemeta() to make it regex-safe
    • In a desperate attempt to make it cross-version, we generalize the whitespaces to \s+
    • here, $new is just an empty string – we are deleting this section
    • then we substitute the new for old in the text ref – $$tmpl_ref
    • The “Extended” tab in the text editor will disappear
sub hdlr_template_output_edit_entry {
    my ($cb, $app, $tmpl_str_ref, $param, $tmpl) = @_;

    # do something
}
  • sub hdlr_template_output_edit_entry {}
    • This callback is similar to template_source, but it gets the output of the template, after it was processed.
    • Parameters: (skipping $cb and $app as they are similar in the function above)
      • $tmpl_str_ref – ref to the output string
      • $param – the parameters that were used to render the template
      • $tmpl – the template object
    • We don’t really need this callback for our plugin, so it does nothing
sub hdlr_template_param_edit_entry {
    my ($cb, $app, $param, $tmpl) = @_;

    my $host_node = $tmpl->getElementById('keywords');
    my $innerHTML = '<input type="text" name="url_field" id="url_field" class="full-width" mt:watch-change="1" value="<mt:var name="url_field" escape="html">" autocomplete="off" />';

    my $block_node = $tmpl->createElement(
        'app:setting',
        {
            id => 'url_field',
            label => 'URL',
            label_class => 'top-label',
        }
    );

    $block_node->innerHTML( $innerHTML );
    $tmpl->insertAfter($block_node, $host_node);

    $param->{url_field} = "http://www.example.com/" if !$param->{url_field};
}
  • sub hdlr_template_param_edit_entry {}
    • Use getElementById to find the ‘keywords’ element in the DOM
    • createElement creates a new template node to be inserted into the DOM
    • innerHTML is a getter / setter to the node content (in the create command only the attributes were given
    • insertAfter($block_node, $host_node) does, as the name suggest, inserts $block_node after $host_node
    • $param is the parameters that are passed to the template, to be used in the building
      • In the template code that we add to the screen, we use the variable url_field : <mt:var name="url_field"> and in this function we set it to (http://www.example.com/) unless already set
      • This value is not saved to the database in this code. it is just being displayed, and when the entry is saved the value will be sent to the server. For capturing and saving this value, we would have used other callbacks, i.e. post_save. but this is out of the scope for this chapter

DOM manipulating functions

These functions are defined in MT::Template

sub getElementsByTagName {} Find element by the tag name
sub getElementsByClassName {} Find element by class name
sub getElementsByName {} Find element by the name attribute
sub getElementById {} Find element by id
sub createElement {} Create new element
sub createTextNode {} Create element that contain just text (and not other template tags)
sub insertAfter {} Insert a new element after selected element
sub insertBefore {} Insert a new element before selected element
sub childNodes {} Get a list of top-level elements
sub hasChildNodes {} does this template have elements at all?
sub appendChild {} Add an element after the last child of a selected element

The node/element manipulation functions are defined in MT::Template::Node

sub nodeValue {} return the un-compiled node content
sub childNodes {} well, child nodes
sub attributes {} attributes as a hash ref
sub attribute_list {} attributes as an array ref
sub tag {} tag name, un-normalized
sub setAttribute {}
sub getAttribute {}
sub firstChild {}
sub lastChild {}
sub nextSibling {}
sub previousSibling {}
sub ownerDocument {} The node’s template object
sub hasChildNodes {}
sub nodeType {} if it a text node, function or block tag
sub nodeName {} tag name, normalized
sub innerHTML {} Getter – similar to nodeValue.
setter – compiles the new template code and insert under this node
sub appendChild {} Add a child node after the last
sub removeChild {} remove a selected child node
sub upgrade {} Mark this node as UTF8

Directory Structure

$MT_DIR/
|__ plugins/
   |__ MyPlugin13/
      |__ config.yaml
      |__ lib/
         |_ MyPlugin13/
            |__ L10N.pm
            |_ L10N/
            |  |_ en_us.pm
            |  |_ ja.pm
            |__ Transformer.pm

Plugin Download

MyPlugin14.zip(2.63KB)

Summary

While powerful and can give nice results, please always remember that the template structure of the management screens may change from version to version, and you need to carefully re-test your plugin with every minor release

Navigation

Prev:Creating a new application << Index >> Next:Modifying the management screen menu

Clone this wiki locally