Not a developer? Go to MovableType.com

Documentation

Creating Custom Asset Types

The Movable Type Asset Management System was design initially to store files, specifically image, video and audio files. But over time it was extended to support virtually any type of asset, including those assets hosted remotely.

The following is a guide to building and defining your custom assets types using items from Amazon’s Product Catalog as the source of our assets. The source code for these examples can be found in the Media Manager plugin licensed under the GPL. Not all of the relevant code is replicated here, only enough to illustrate the key aspects of defining a custom asset type.

MT::Asset Base Class

Movable Type comes with a basic implementation of an asset type. All custom assets should extend this base class in some way in order to inherit its basic functionality.

To extend the MT::Asset package, the use base command in Perl will help. The following code sample shows the beginning of a custom Amazon Asset type.

package MT::Asset::Amazon;
use strict;
use base qw( MT::Asset );
__PACKAGE__->install_properties( { class_type => 'amazon', } );
__PACKAGE__->install_meta( { columns => [ 'original_title', 'artist', 'asin',
      'product_group' ], } );

There are two key aspects to the code sample above. The install_properties method registers with the base class the name of the asset type. This will be what distinguishes this asset type from others in the database and in template tags that filter by asset type.

The second key aspect is discussed below.

Defining Custom Asset Properties

All assets shared the following properties:

  • id - a system wide unique identifier for the asset
  • blog_id - the id of the blog that contains the asset
  • label - a human readable display name for the asset
  • url - a URL to the original asset
  • description - a description of the asset
  • file_path - the path to the asset (see “Asset Paths” below)
  • file_name - the name of the file
  • file_ext - the file extension of the file
  • mime_type - the mime type of the file
  • parent -

Assets can define their own properties, and extend the list above using the following code:

__PACKAGE__->install_meta( { columns => [ 'original_title', 'artist', 'asin',
      'product_group' ], } );

All assets types are stored as strings in the database. Their values are retrieved and set programmatically using getters and setters that are defined for you automatically. For example:

my $a = MT::Asset::Amazon->new;
$a->label("my new asset");
$a->product_group("Book");
$a->save;

Their values are accessed via template tags using the following:

<mt:AssetProperty property="product_group" />

MT::Asset Subroutines and Methods

The following is a description and example of each of the various asset methods that can be overridden by your custom asset type.

class_label and class_label_plural

These defines the display name of your asset type in the user interface which occasionally needs to display your asset and identify by its type.

sub class_label { MT->translate('Amazon Item'); }
sub class_label_plural { MT->translate('Amazon Items'); }

It is advisable to always wrap strings in the MT->translate subroutine to enable the easy translation of strings in your application.

on_upload

This method is called after an upload has been completed and a user’s preferences about an asset (the tags, label and description) have been saved. It takes as input an associative array, or “hash” containing all of the input parameters submitted by the user.

This method is especially useful if your custom asset type exposes additional form elements that need to be captured and stored in association with the asset being uploaded and saved.

sub on_upload { my $asset   = shift; my ($param) = @_; 1; }

has_thumbnail

This should return true if the asset has a thumbnail that can be displayed, false otherwise. The default is false.

sub has_thumbnail { 1; }

See also: insert_options

insert_options

The insert_options subroutine is responsible for returning the HTML that will be appended to the form to the Insert Asset dialog. This is used by plugins that want to surface customized and specialized options specific for their asset. For example, the Media Manager plugin uses this mechanism for allowing the user to customize the asset’s size, rotation and alignment within an entry.

The following code fragment shows the basics for loading a template and then returning its HTML to be inserted in the dialog.

sub insert_options {
    my $asset = shift;
    my ($param) = @_;
    my $app   = MT->instance;
    my $blog  = $asset->blog or return;
    my $plugin = MT::Plugin::MediaManager->instance;
    my $tmpl = $plugin->load_tmpl('dialog/asset_options_amazon.tmpl');
    return $app->build_page( $tmpl, $param );}
}

Specifying Additional Stylesheets within the Dialog

Media Manager customizes the display of the Insert Options dialog quite a bit. It accomplishes this by inserting a reference to its own stylesheet into the header of the dialog pane. This is accomplished by setting the html_head parameter within the $param hash passed to insert_options. For example:

$param->{html_head} = '<link rel="stylesheet" href="'.$app->static_path.'plugins/MediaManager/styles/app.css" type="text/css" />';

thumbnail_url

Assets can optionally display thumbnails. If a thumbnail can be associated with an asset (e.g. a book cover from Amazon) then the thumbnail_url is responsible for composing the URL to that thumbnail file.

sub thumbnail_url {
    my $asset = shift;
    my (%param) = @_;
    $param{'width'} = $param{'Width'} if ($param{'Width'});
    $param{'height'} = $param{'Height'} if ($param{'Height'});
    $param{'scale'} = $param{'Scale'} if ($param{'Scale'});
    my $url = '';
    # do stuff - set the $url variable for example
    return $url;
}

The thumbnail_url subroutine takes as input an associative array that defines the list of argument passed to method by either the <mt:AssetThumbnailURL> template tag or the MT::Asset->thumbnail_urlmethod itself when invoked directly.

This associative array contains an undefined set of input parameters. But it must support at least the following:

  • Width
  • Height

These two input parameters can be used to generate a thumbnail confined by one or both of these size constraints.

as_html

The as_html subroutine serializes an asset into HTML for inclusion inside of an entry when it is inserted by the user.

For example Media Manager uses this callback to compose a thumbnail that is linked to the item found at Amazon.com.

For example:

sub as_html {
    my $asset   = shift;
    my ($param) = @_;
    my $text    = '';
    if ( $param->{'include'} ) {
        # snip
        my $thumb = $asset->thumbnail_url(
                                           size_abs => $param->{'size_abs'},
                                           rotation => $param->{'img_rot'},
                                           img_drop => $param->{'img_drop'},
                                           );
        $text = sprintf(
                        '<a href="%s"><img alt="%s" src="%s" %s/></a>',
                        MT::Util::encode_html( $asset->url ),
                        MT::Util::encode_html( $asset->label ),
                        MT::Util::encode_html( $thumb ),
                        $wrap_style,
                        );
        return $asset->enclose($text);
    }
}

The as_html callback takes as input an associative array containing all of the input parameters submitted by the form that was rendered by the insert_options method discussed above.

The method must return HTML. That HTML will be inserted into the entry directly at the cursors current location. It is highly recommended that any asset you wish to insert into a post is done so by using the MT::Asset->enclose method which is responsible for encapsulating the asset in the markup that will allow Movable Type to track in which entries an asset is inserted or associated.

Customizing the Create Asset Dialog and Process

The Media Manager plugin wished to surface a slightly modified process for creating assets. This process involves surfacing a form to the user within a dialog that would allow them to submit search terms to Amazon, display those search results within the dialog, allow the user to select an item within the search results and then hand off control of the dialog to Movable Type to handle the rest (e.g. inserting the asset into an entry).

Adding a Menu Item

Media Manager adds a “Create Amazon Item” to the “Create Menu” using the following code found in MediaManager.pl.

sub init_registry {
    my $plugin = shift;
    $plugin->registry({
        applications => {
            cms => {
                methods => {
                    amazon_find =>
                         '$MediaManager::MediaManager::CMS::find',
                    amazon_find_results =>
                         '$MediaManager::MediaManager::CMS::find_results',
                    asset_options => 
                         '$MediaManager::MediaManager::CMS::asset_options',
                },
                menus => {
                    'create:amazon' => {
                        label  => 'Amazon Asset',
                        order  => 302,
                        dialog => 'amazon_find',
                        view   => "blog",
                    },
                },
            },
        },
    });
}

See also “Defining Custom Menu Items” for more information.

The above code sample registers the menu item and also defines the handlers found within the application to process and display the HTML for each step within the wizard. These pages are:

  • The Amazon Search Form
  • The Amazon Search Results Form
  • The Asset Options Screen, which is handled mostly by Movable Type (see code sample)

Defining Handlers for a Create Asset Wizard

Each page within a Create Asset Wizard you wish to build requires its own dedicated handler. Each handler is responsible for displaying the HTML for its corresponding page. Movable Type will manage for you the displaying of the dialog. All you have to do is define its contents.

For example, here is the code necessary for displaying the initial page of the wizard:

sub find {
    my $app = shift;
    my $q = $app->{query};
    my $blog = $app->blog;
    my $tmpl = $app->load_tmpl('dialog/find.tmpl');
    $tmpl->param(blog_id      => $blog->id);
    $tmpl->param(catalog_loop => _catalog_loop('Blended'));
    return $app->build_page($tmpl);
}

The template (dialog/find.tml) found within your plugins tmpl directory is as follows:

<mt:setvarblock name="page_title">
    <__trans phrase="Find Item at Amazon">
</mt:setvarblock>
<mt:include name="dialog/header.tmpl">
<form method="post" action="<mt:var name="script_url">">
    <input type="hidden" name="__mode" value="amazon_find_results" />
    <input type="hidden" name="blog_id" value="<mt:var name="blog_id">" />
    <input type="hidden" name="entry_insert" value="<mt:var 
        name="entry_insert">" />
    <input type="hidden" name="magic_token" value="<mt:var 
        name="magic_token">" />
    <mtapp:setting
        id="file"
        label_class="top-label"
        label="<__trans phrase="Keywords">"
        hint="Enter the keywords with which to search the Amazon product catalog"
        show_hint="1">
      <input name="kw" type="text" size="60" />
    </mtapp:setting>
    <mtapp:setting
        id="catalog"
        label_class="top-label"
        label="<__trans phrase="Show only">"
        hint=""
        show_hint="0">
      <mt:include name="include/catalog_select.tmpl">
    </mtapp:setting>
    <div class="actions-bar">
        <div class="actions-bar-inner pkg actions">
            <button
                type="submit"
                accesskey="s"
                title="<__trans phrase="Search (s)">"
                class="primary-button"
                ><__trans phrase="Search"></button>
            <button
                onclick="closeDialog(); return false"
                type="submit"
                accesskey="x"
                title="<__trans phrase="Cancel (x)">"
                ><__trans phrase="Cancel"></button>
        </div>
    </div>
</form>
</mt:if>
<mt:include name="dialog/footer.tmpl">

Each page within the wizard is defined similarly. The last step within the wizard however needs some special handling because it should hand off control to Movable Type to handle the rest of the Asset Options collection process and inserting the Asset into an entry (if applicable).

The following mode handler does not define any HTML to be displayed in the dialog directly. Its primary function is to create the asset and save it inside the database. Once that is complete, the mode handler can dispatch the rest of the request to Movable Type’s built in complete_insert method. The complete_insert method will then display the HTML associated with doing any final editing of the Asset that was just created.

sub asset_options {
    my $app = shift;
    my $q = $app->{query};
    my $blog = $app->blog;
    my $asin = $q->param('selected');
    require MT::Asset::Amazon;
    my $asset = MT::Asset::Amazon->new;
    $asset->blog_id($q->param('blog_id'));
    $asset->label($q->param('label'));
    $asset->url($item->{DetailPageURL});
    $asset->created_by( $app->user->id );
    # <snip>
    # plus other properties that you may want to populate your asset with
    my $original = $asset->clone;
    $asset->save;
    $app->run_callbacks( 'cms_post_save.asset', $app, $asset, $original );
    return $app->complete_insert(
        asset => $asset,
        asin => $asin,
        thumbnail => $asset->thumbnail_url,
    );
}

Asset Paths

Often paths to assets stored in the database use the prefix %a. The special token refers to the archive path of the blog that contains the asset. This allows for blogs whose archive path is changed, restored from a backup or moved to have all of the paths to its assets to be preserved and for assets to be moved accordingly.

Therefore developers storing assets on the local file system should utilize this token in their own asset path to ensure the portability of the assets they define and store in the database.

Back