English plugin dev 2 1 - Hiranyaloka/Documentation GitHub Wiki

Developing Function Tags

What is a function tag?

Function tag is a tag that runs a function, and is replaced by the function’s output

The tag that we developed last chapter, “<MTHelloWorld>”, was a simple one that only output “Hello, world!”. However, tags can perform complex operations and access the database, for preparing their output

Specification and Test cases for the Plugin

Specification

We want our plugin to meet these criteria:

  • System-level plugin setting screen
    • Plug-in Name: “Functions tag sample plugin 1.0”
    • Description: “Function Test Tag Plugin”
    • Documentation: http://www.example.com/docs/
    • Author: http://www.example.com/about/
    • Resources: Tag <$MTCSSByCategory$>
  • The user just need to add the “<MTCSSByCategory>” in the entries’s HTML <head> element
  • If there is no category associated with this entry, the tag should not output anything
  • If there is a category associated with this entry then:
    • Find the category’s parent categories
    • If this category does not parent, create a folder named “cat-”.$category->basename
    • If this category have a parent, create a folder named “cat-”.$parent-&t;basename.“-”.$category->basename
    • And so on, for whatever hierarchical level that exists
    • The CSS file name should be set in the mt-confing.cgi file, and be put under the folder created, in the folder pointed by the StaticWebPath parameter
      • mt-config.cgiに設定されているStaticWebPath配下の"support"ディレクトリ以下のCSSにひもづける 例)"/mt-static/support/css/cat-foo-subfoo/screen.css"
    • The tag should output the HTML tag to include the created CSS file: “<link rel=”stylesheet" href=“/mt-static/support/css/cat-foo-subfoo/screen.css” type=“text/css” />"
  • If there is not already a CSS file in the designated location, this file should be created
  • This tag should not be used in blog archives, categories, etc. If used, it should rise an error

Preparing the test environemt

Add the following line to “$MT_DIR/t/mysql-test.cfg”:

StaticWebPath /mt-static/

Directory structure

As we did in the previous chapters, we put the test files under the ‘t’ directory

i.e.)00-compile.t, 01-tags.t

$MT_DIR/
|__ plugins/
   |__ MyPlugin05/
      |__ t/
         |_ 00-compile.t
         |_ 01-tags.t

Test Case 0 (00-compile.t)

use strict;
use lib qw( t/lib lib extlib );
use warnings;
use MT;
use Test::More tests => 5;
use MT::Test;

ok(MT->component ('MyPlugin05'), "MyPlugin05 plugin loaded correctry");

require_ok('MyPlugin05::L10N');
require_ok('MyPlugin05::L10N::ja');
require_ok('MyPlugin05::L10N::en_us');
require_ok('MyPlugin05::Tags');

1;

Test Case 1 (01-tags.t)

This test case need to verify these three things:

  1. Empty response for empty string
  2. For entry that has a category, CSS link “<link rel=”stylesheet" href=“/mt-static/support/css/cat-foo-subfoo/screen.css” type=“text/css” />" will be printed
  3. For entry that does not have a category, empty response is to be returned
(-omitted-)
#===== Edit here
my $test_json = <<'JSON';
[
{ "r" : "1", "t" : "", "e" : ""},
{ "r" : "1", "t" : "<mt:Entries id=\"7\"><MTCSSByCategory></mt:Entries>",
 "e" : "<link rel=\"stylesheet\" href=\"/mt-static/support/css/cat-foo-subfoo/screen.css\" type=\"text/css\" />"},
{ "r" : "1", "t" : "<mt:Entries id=\"8\"><MTCSSByCategory></mt:Entries>", "e" : ""}
]
JSON
#=====

Writing a function-tag plugin (Perl)

We create this plugin based on MyPlugin03 that was built previously

config.yaml

As we did with the modifier tag in the previous chapter, now we add a function tag:

id: MyPlugin05
name: <__trans phrase="Sample Plugin function tag">
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: MyPlugin05::L10N

tags:
    function:
        CSSByCategory: $MyPlugin05::MyPlugin05::Tags::_hdlr_cssbycategory

L10N.pm

package MyPlugin05::L10N;
use strict;
use base 'MT::Plugin::L10N';

1;

L10N/en_us.pm

package MyPlugin05::L10N::en_us;

use strict;
use base 'MyPlugin05::L10N';
use vars qw( %Lexicon );

%Lexicon = (
    '_PLUGIN_DESCRIPTION' => 'Sample function tag',
    '_PLUGIN_AUTHOR' => 'Plugin author',
);

1;

L10N/ja.pm

package MyPlugin05::L10N::ja;

use strict;
use base 'MyPlugin05::L10N::en_us';
use vars qw( %Lexicon );

%Lexicon = (
    'Sample Plugin function tag' => 'サンプルプラグイン ファンクションタグ',
    '_PLUGIN_DESCRIPTION' => 'ファンクションタグ テストプラグイン',
    '_PLUGIN_AUTHOR' => 'プラグイン作者',
);

1;

Tags.pm

package MyPlugin05::Tags;
use strict;

sub _hdlr_cssbycategory {
    my ($ctx, $args) = @_;

    my $entry = $ctx->stash('entry')
        || $ctx->error(MT->translate('You used an [_1] tag outside of the proper context.', 'CSSByCategory'));

    my $cat = $entry->category() || return;

    my @basenames = ();
    while (1){
         if ($cat->parent() == 0) {
             push (@basenames, $cat->basename());
             last;
         }
         push (@basenames, $cat->basename());
         $cat = MT::Category->load($cat->parent());
    }

    my $cat_tmp = 'cat-' . (join '-', reverse @basenames);
    $cat_tmp .= '/screen.css';

    my $mt = MT->instance;
    my $css_dir = $mt->config('StaticWebPath') || '/mt-static/';
    unless ($css_dir =~ m/\/$/) {
        $css_dir .= '/';
    }
    $css_dir .= 'support/css/';

    return '<link rel="stylesheet" href="' . $css_dir . $cat_tmp . '" type="text/css" />';
}

1;

Commentary

  • In the beginning of the file, we decalre the package and of course add “use strict;”

  • Declare our handler function, that gets two parameters: “$ctx” and “$args”

  • $ctx->stash(‘entry’) gives us the current Entry object that is being published. If there is no such object, return error

  • Get the entry’s category. If the entry does not have a category, return with no output

  • Collect the basenames of this category and all its ancestors to the @basenames array, where the root category is in the highest index location. It does so by inspecting the “$cat->parent()”, which contain the parent id or zero if this is a root category. that is why we stop (“last”) when seeing parent id 0.
    When there is a parent category it is loaded (my the “MT::Category->load($cat->parent());” statement to the $cat variable, and the loop start again

  • Creating the folder: as the @basenames array is in reverse order of what we need (it contain “grandchild – child – parent” while we need “parent – child – grandchild”) we will just the “reverse” function before joining it using ‘-’. then we add the filename itself: “/screen.css”

  • “MT->instance” give us a handle to the current application running, which we use to get configuration and information. Here we ask it for the location of StaticWebPath. If there is no such directory set, we use the default ‘/mt-static/’.
    Next line validate that the directory have an ‘/’ in the end (this is an annoying bit that always comes up when handling directories and files) and add ‘support/css/’ in the end

  • Prepare and return the CSS link.
    returns output in the form of: ‘<link rel=“stylesheet” href=“/mt-static/support/css/cat-foo-subfoo/screen.css” type=“text/css/”>’

Writing a function-tag plugin (PHP)

function.mtcssbycategory.php

It is a function tag, and the tag name is MTCSCByCategory, so the super computer in the basement of SixApart Japan told us that the file name should be function.mtcssbycategory.php

<?php
    function smarty_function_mtcssbycategory($args, &$ctx) {
        $entry = $ctx->stash('entry');
        if (!$entry) {
             return $ctx->error('You used an MTCSSByCategory tag outside of the proper context.');
        }

        $cat = $entry->category();
        if (!$cat) {
            return;
        }

        while (1) {
            if ($cat->parent == 0) {
                $basenames[] = $cat->basename;
                break;
            }
            $basenames[] = $cat->basename;
            $loaded = $cat->Load("category_id = " . $cat->parent);
        }

        $cat_tmp = 'cat-' . join ('-', array_reverse($basenames));
        $cat_tmp .= '/screen.css';

        $mt = MT::get_instance();
        $css_dir = $mt->config('StaticWebPath') or '/mt-static/';
        if (!ereg('\/$', $css_dir)) {
            $css_dir .= '/';
        }
        $css_dir .= 'support/css/';

        return '<link rel="stylesheet" href="' . $css_dir . $cat_tmp . '" type="text/css" />';
    }
?>

Commentary

This is very similar to the Perl side that we saw previously, so in the following commentary the differences are marked in bold

  • The function name, in accordance with the smarty framework, is smarty_function_mtcssbycategory. also not that the order of the parameters (“$args” & “$ctx”) is reversed from the Perl version
  • $ctx->stash('entry') gets the entry object for us, and if does not exists return with error
  • Get the category of the Entry. If no category is set for this entry, return with no output
  • The basenames[] array hold the parenthood basename list. As in the Perl version, it is stored in “grandchild, child, parent” order.
    The parent property contain the parent id, or 0 if this is a root category. in which case the name is saved and the loop is stopped. (break)

    $cat is being replaced by the new loaded category in the "$cat->Load()" statement. For loading a category without destroying the previous object use this snippet:

    $category = new Category;
    $loaded = $category→Load("category_id = " . $cat→parent);
  • $category now contains the loaded object
  • We need to reverse the content of basename[], and glue it together to make the desired file name. the PHP function for reversing an array is “array_reverse”, and for joining strings is “join”
  • “MT::get_instance()” returns an application instance, which we will use to retrieve information about the website. We need the StaticWebPath parameter, that contains where the ‘/mt-static/’ is located. (web-access-wise)
    Afterward, we make sure that $css_dir have a ‘/’ in the end, as we want to append it ‘support/css/’
  • All that we have left is to build the return string – the HTML tag for loading the needed CSS file

Directory Structure

$MT_DIR/
|__ plugins/
   |__ MyPlugin05/
      |__ config.yaml
      |__ lib/
      |  |_ MyPlugin05/
      |     |__ L10N.pm
      |     |_ L10N/
      |     |  |_ en_us.pm
      |     |  |_ ja.pm
      |     |_ Tags.pm
      |__ php/
      |  |_function.mtcssbycategory.php
      |__ t/
         |_00-compile.t
         |_01-tags.t

Running the tests

We use the “prove” command to run all our tests

$ prove plugins/MyPlugin05/t/*.t
plugins/MyPlugin05/t/00-compile....ok
plugins/MyPlugin05/t/01-tags.......ok
All tests successful.
Files=2, Tests=12, 27 wallclock secs (12.70 cusr +  5.31 csys = 18.01 CPU)

After running the tests, it is time to insert our tag to the Entry template. Just before the “<title>” tag, enter the “<MTCSSByCategory>” tag, and rebuild the site

  • Template
(.. snip ..)
    <MTCSSByCategory>
    <title><$mt:EntryTitle encode_html="1"$> - <$mt:BlogName encode_html="1"$></title>
</head>
(.. snip ..)
  • Output
(.. snip ..)
    <link rel="stylesheet" href="/mt-static/support/css/cat-foo-subfoo/screen.css" type="text/css" />
    <title>Verse 4 - none</title>
</head>
(.. snip ..)

Putting the plugin to work

  • Take a MT installation. (for testing a newly built plugin – we sure hope you are not using your production one)
  • Create a classic blog
  • Install the plugin (under plugins/MyPlugin05)
  • Go to Design->Templates->Entry, and enter the <MTCSSByCategory> tag just above the <title> tag
  • Go to Entries->Categories, create a category named “foo” and sub-category named “subfoo”
  • Post three blog posts: one without any category, one with category “foo” and one with category “subfoo”
  • Inspect the output pages. There should be near the <title> tag:
    • No category: Empty
    • Parent Category: <link rel=“stylesheet” href=“/mt-static/support/css/cat-foo/screen.css” type=“text/css” />
    • Child category: <link rel=“stylesheet” href=“/mt-static/support/css/cat-foo-subfoo/screen.css” type=“text/css” />
  • Under the “/mt-static/support/” directory create two directories: “css/cat-foo” and “css/cat-foo-subfoo”
  • Inside each, create a file named “screen.css”
  • A simple example:
    • /mt-static/support/css/cat-foo/screen.css
      #container-inner,
      #content {
      background-color: #008080;
      }
    • /mt-static/support/css/cat-foo-subfoo/screen.css
      #container-inner,
      #content {
      background-color: #008000;
      }
  • After placing these files in the designated places, reload the entries in the browser and see that the design change took place, but only on the posts with categories

Summery

Function tags are the easiest way to enrich your site, changing design and behavior in response to tags, categories and more. And supporting PHP and Perl, even for non-trivial tasks, is easy and produce quite similar code in both languages

Download plugin

MyPlugin05.zip(6.64KB)

Navigation

Prev:Developing Global Modifier Plugin << Index >> Next:Developing Block-Tag Plugins

⚠️ **GitHub.com Fallback** ⚠️