Not a developer? Go to MovableType.com
Frequently in this manual certain conventions and terminology will be used in reference to specific design and implementation patterns. In lieu of defining these constructs and conventions over and over again, they are defined below. Familiarizing yourself with these concepts and terms will greatly enhance your ability to understand the contents of this reference manual.
Registry configuration files are expressed in YAML syntax. YAML is a simple, lightweight markup language for representing simple data structures in a platform and language independent way. For more information about YAML's syntax, visit its homepage.
Then the type SCALAR is specifically defined for a registry key or item, which is the default value for all registry keys, then there can be one and only one value associated with the key. If an item is added to the registry for which their is already a corresponding name/value pair, then the value of the item being added will overwrite the previous value.
When the type ARRAY is specifically defined for a registry key or item, then the value of that registry item is assumed to have multiple values. If an item is added to the registry for which their is already an existing name/value pair then the new value and the old values are aggregated into a single array. Consequently, it means that developers should assume that the value of such a registry item is an array and can be accessed using Perl's array syntax.
Example
$registry = {
urls => [
'http://somedomain.com/foo',
'http://somedomain.com/bar',
'http://somedomain.com/baz',
],
};
Is equivalent to:
$registry = {
urls => 'http://somedomain.com/foo',
urls => 'http://somedomain.com/bar',
urls => 'http://somedomain.com/baz',
};
A "hash" refers generically to any collection of related key/value pairs. In that way the registry itself is a hash that contains other hashes. When the type HASH is specifically defined for a registry key or item it therefore means that its value a slightly more complex data structure containing a sequence of name/value pairs.
Example
$registry = {
permissions => {
'system.create_blog' => {
label => trans("Create Blogs"),
group => 'sys_admin',
order => 100,
},
},
};
A handler is a function to which events are passed for processing. The value of a handler is a coderef.
A "coderef" is the means by which a developer can reference a block of code that needs to be executed by the framework. A coderef can be expressed in the following ways:
$registry = {
permissions => {
'system.create_blog' => {
label => trans("Create Blogs"),
group => 'sys_admin',
order => 100,
},
},
};
The name of the component being registered.
The package name of the component being registered. The package being referenced should contain all the business logic for the component being registered. This package is automatically loaded when Movable Type initializes itself and all of its components.
The name of the author of the component.
The `version` registry key stores the version number of the plugin or component being described. This value is used to display to a user, and is used to determine if newer versions of this plugin or component exist.
The value of `version` should be any valid positive floating point number and should not contain and letters or special characters.
Sample Values
For those components that have custom data types that they define, it is highly recommended that they also define a `schema_version`. The `schema_version` is used by Movable Type to determine if changes are required to the structure of database when new components or plugins are installed. Each time Movable Type is loaded, the schema version of the database is compared to that of each component is compared to what is declared and stored within the system. If the declared schema version is greater then what is on record for that component, Movable Type begins the upgrade process to bring the schemas in sync.
The `schema_version` should be a simple integer, but can be a floating point number to indicate minute changes to the schema. Each time the schema for a component or plugin changes, the `schema_version` should be incremented.
Plugins and components may need to persist information into a database. Therefore, database tables need to be defined and generated within Movable Type's database. These tables are created and maintained for each registered "object type" in the registry. Movable Type's core even utilizes this framework to keep its own database up to date from release to release of the core platform.
The value of `object_types` is an array of keys, where the name of each key is the name of the data type being registered. The value then associated with each key is a package name where the complete definition of the object or class can be found. The package then contains specifics about the structure of the data in the database, in addition to all the business logic associated with the object.
See also: MT::Object man page. (TODO)
Example
$registry = {
version => MT->VERSION,
schema_version => MT->schema_version,
object_types => {
'foo' => 'MT::Foo',
'bar' => 'MT::Bar',
},
};
MT::Foo.pm
package MT::Foo;
use strict;
__PACKAGE__->install_properties({
column_defs => {
'id' => 'integer not null auto_increment',
'blog_id' => 'integer not null',
'title' => 'string(255)',
'text' => 'text',
},
});
Movable Type allows object types to be extended. This allows plugins and components to insert and associate additional pieces of data with a pre-existing data type. Furthermore, these additional pieces of data become a seamless extension of the original data element, allowing that object to be sorted by and filtered by the new data element quickly and easily.
Extending an object is done by declaring the extension within the registry. For example, to add a new "ratings" field to the core entry object for the purposes of allowing users to attach a zero to fives star rating to entry, one would do the following:
$registry = {
object_types => {
'entry' => {
rating => integer,
},
},
};
Whenever a component or plugin declares an object type that already exists, the that object_type declaration acts as an extension to the pre-existing object type. Remember, if a plugin or component every changes an object type it declares, or changes the nature of an extension to a pre-existing object type, the schema_version associated with that plugin or component should be incremented to signal to Movable Type that some database maintenance may be required.
This feature is documented, but not currently used.
Movable Type comes with support for a number of different databases including (but not necessarily limited to) MySQL, Postgres and SQLite. Furthermore, support for Oracle and Microsoft SQLServer is provided through an additional add-on, called the "Enterprise Connectivity Pack." Support for these databases and others is provided through "object drivers." Object drivers provide a database framework for:
Some developers may wish to bind Movable Type in someway to a currently unsupported database or backend storage system. In order to determine if such a system can be compatible with Movable Type's object driver system, make sure backend system meets the following minimum requirements:
Movable Type allows plugins and components to generate custom permissions that can be assigned to users. Permissions registered in this way will automatically appear within the user interface allowing administrators to immediate begin assigning that permission to users and roles.
The value associated with the `permissions` registry key is an array of permissions being defined. Each permission being registered in this way is itself a hash which defines the specific parameters governing the display and usage of that permission within the application.
Permissions within the registry support the following properties:
Note: permissions within the registry are not automatically rendered and made available through the user interface to be assignable by an admin. For the time being that must be done with a transformer callback.
Permissions can be defined to operated within two different scopes: a blog specific scope or a system wide scope. The scope of a permission is specified by prefixing the permission's unique id or name with either "blog" or "system" followed by a period (".").
For example, to register a custom permission, use the following syntax.
$registry = {
permissions => {
'system.create_blog' => {
label => trans("Create Blogs"),
group => 'sys_admin',
order => 100,
},
},
};
Movable Type provides developers with two mechanisms for configuration of their software. The most basic form of configuration is through Movable Type's configuration file, or `mt-config.cgi`. Movable Type's configuration file is in some cases a preferred mechanism for configuration due to the following reasons:
When parsing `mt-config.cgi` Movable Type will validate the directives found there and produce an error if it encounters something it doesn't understand. Therefore, developers must declare their configuration directives by registering them with Movable Type.
config_settings => {
'AtomApp' => {
type => 'HASH',
default => 'weblog=MT::AtomServer::Weblog'
},
'DefaultEntryPrefs' => {
type => 'HASH',
default => {
type => 'Default', # Default|All|Custom
button => 'Below', # Above|Below|Both
height => 162, # textarea height
},
},
'DefaultLanguage' => {
default => 'en_US',
},
},
Configuration settings have the following properties.
Examples:
PluginSwitch foo=bar
PluginSwitch baz=boo
Schema 1
Schema 2
Goozer abc
Text filters provide the means by which text being published to the blog can be transformed in some way just prior to being published. Filters can either be associated with an entry so that its contents are automatically transformed, or they can be invoked directly via an attribute placed within any template tag. The key for any filter also serves as the text filter's unique name.
text_filters => { 'my_filter' => { label => 'Transform this Text', handler => 'MT::Foo::text_transform', }, }
To learn more about how to create a text filter, see TODOText Filters.
A global text filter is a filter that is performed upon the value of any template tag using a special attribute. To learn more about global text filters, see: "tags."
See also: How to create a text filter.
Movable Type allows developers to define and create their own applications embedded within the core application that utilize the Movable Type platform for defining custom permissions, database management, et al. These application often require their own highly customized user interface.
To facilitate the creation of these highly customized user interfaces, developers can define an "application" within the registry. Each registered application can define the following:
To register a mode in an existing application use the code below.
Example
$registry = {
applications => {
'cms' => {
methods => {
myapp_new-mode => sub { // my handler // },
},
},
},
};
The ease with which new modes are added to the "cms" application, the default MT app, makes it tempting for developers to never define their own applications, and to always add new modes to cms. However, developers of complex plugins are encouraged to create their own applications for more complex plugins. Furthermore, when adding modes to a pre-existing application, developers are encouraged to prefix their modes with a unique identifier of some kind to help prevent namespace collisions in the future.
Movable Type's core defines the "cms" application. This application supports its own set of registry keys. One such registry key allows users to define custom import formats to allow users to easily take content from one application and import it directly into Movable Type.
import_formats => {
'import_mt_format' => {
label => 'Another system (Movable Type format)',
code => \&MT::ImportExport::import,
options => [ 'title_start', 'title_end', 'default_status' ],
options_template => 'import_others.tmpl',
},
},
},
applications => {
'ack' => {
handler => 'MyApp::Ack',
cgi_base => 'ack',
list_actions => {
'entry' => {
'do_something' => {
label => "Batch Do Something",
order => 100,
code => \&do_something,
permission => 'edit_all_posts,publish_post',
},
},
menus => {
'foo' => {
label => "Create",
order => 100,
},
'foo:bar' => {
label => "Do Something",
order => 100,
mode => 'hdlr_bar',
args => { _type => 'entry' },
permission => 'create_post',
requires_blog => 1,
},
},
methods => sub { MT->app->core_methods() },
},
};
For defining callbacks.
Movable Type provides a framework for registering frequently occurring tasks, or tasks that must occur on a fixed schedule. These tasks are managed in of two ways:
A plugin declares a new task in the registry using the following code:
tasks => { 'FuturePost' => { key => 'FuturePost', label => 'Publish Future Posts', frequency => 15 * 60, # no more than every 15 minutes code => sub { MT->instance->publisher->publish_future_posts; } }, }
TODO: What does it mean when a template tag is prefixed with "App:" - I can guess, but how should I explain it? They are namespaced just to communicate scope. No functional implications.
entry => {
published => {
label => 'Published entries',
order => 100,
handler => sub {
my ( $terms, $args ) = @_;
$terms->{status} = 2;
},
},
received_comments_in_last_7_days => {
label => 'Entries with comments in the last 7 days',
order => 500,
handler => sub {
my ( $terms, $args ) = @_;
my $ts = time - 10 * 24 * 60 * 60;
$ts = MT::Util::epoch2ts( MT->app->blog, $ts );
$args->{join} = MT::Comment->join_on(
'entry_id',
{ created_on => [ $ts, undef ], },
{ range_incl => { created_on => 1 }, }
);
$args->{sort} = \'comment_created_on';
$args->{direction} = 'descend';
},
},
},
upgrade_functions => {
'core_create_placements' => {
version_limit => 2.0,
priority => 9.1,
updater => {
type => 'entry',
label => 'Creating entry category placements...',
condition => sub { $_[0]->category_id },
code => sub {
require MT::Placement;
my $entry = shift;
my $existing = MT::Placement->load({ entry_id => $entry->id,
category_id => $entry->category_id });
if (!$existing) {
my $place = MT::Placement->new;
$place->entry_id($entry->id);
$place->blog_id($entry->blog_id);
$place->category_id($entry->category_id);
$place->is_primary(1);
$place->save;
}
$entry->category_id(0);
},
},
},
}
tags => {
block => {
TagNameFoo => &foo_handler,
},
function => {
TagNameFoo => &foo_handler,
},
modifier => { # attribute only, not a tag name - this is for global filters
TagNameFoo => &foo_handler,
},
}
Movable Type enables developers to define their own archive types, allowing them to group and publish articles in custom ways. I good example of a custom archive type is perhaps a collection of entries based on a custom timeframe or other shared attribute.
The structure of an archive_type in the registry is as follows. The value associated with the unique archive_type key is an ArchiveType object.
archive_types => {
'Weekly' =>
ArchiveType( name => 'Weekly',
archive_label => \&weekly_archive_label,
archive_file => \&weekly_archive_file,
archive_title => \&weekly_archive_title,
date_range => \&weekly_date_range,
archive_group_iter => \&weekly_group_iter,
archive_group_entries => \&weekly_group_entries,
dynamic_template => 'archives/week/<$MTArchiveDate format="%Y%m%d"$>',
default_archive_templates => [
ArchiveFileTemplate( label => MT->translate('yyyy/mm/day-week/index.html'),
template => '%y/%m/%d-week/%i', default => 1 ),
],
dynamic_support => 1,
date_based => 1,
),
}
commenter_authenticators => {
'OpenID' => {
class => 'MT::Auth::OpenID',
label => 'OpenID',
login_form => <<OpenID,
<form method="post" action="<mt:var name="script_url">">
<input type="hidden" name="__mode" value="login_external" />
<input type="hidden" name="blog_id" value="<mt:var name="blog_id">" />
<input type="hidden" name="entry_id" value="<mt:var name="entry_id">" />
<input type="hidden" name="static" value="<mt:var name="static" escape="html">" />
<fieldset>
<mtapp:setting
id="openid_display"
label="<__trans phrase="OpenID URL">">
<input type="hidden" name="key" value="OpenID" />
<input name="openid_url" />
</mtapp:setting>
</fieldset>
</form>
OpenID
login_form_params => \&_commenter_auth_params,
},
}
captcha_providers => {
'mt_default' => {
label => 'Movable Type default',
class => 'MT::Util::Captcha',
}
}
Mark Carey on June 10, 2007, 6:05 a.m. Reply
Under list_actions, it states: “A list action is an action performed on a number of entries at a single time. Permitted values include: entry, page, asset, commenter, author, role and group.” What about comment and trackback (ping)? Are these not supported? Or do these need to be added to the docs?