Movable Type Registry Reference
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 Syntax
Registry configuration files are expressed in YAML syntax (and, more specifically, that of YAML::Tiny). YAML is a simple, lightweight markup language for representing simple data structures in a platform and language independent way.
Data Type
scalar
The SCALAR data type is the default data type for registry items. SCALAR registry items, like scalar variables, can only hold one value. Hence, if you think of the registry as a hash, a scalar registry item is a single key which can have one and only one value. If an item is added to the registry for which there is already a corresponding name/value pair, then the value of the item being added will overwrite the previous value.
array
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',
};
hash
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,
},
},
};
handlers
A handler is a function to which events are passed for processing. The value of a handler is a coderef.
coderefs
A “coderef” — short for code reference — 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:
- As a reference to a fully qualified function name. Examples:
\&function_name
\&MT::ComponentName::function_name
- As an inline code block and anonymous subroutine
- Example:
sub { code goes here }
- Example:
Registry Keys
$registry = {
permissions => {
'system.create_blog' => {
label => trans("Create Blogs"),
group => 'sys_admin',
order => 100,
},
},
};
name
The name of the component being registered.
class
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.
author
The name of the author of the component.
version
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:
1.0
2.05
0.03
Examples of invalid values:
0.45a
version 3
1.42.1
schema_version
Components that define custom data types must also specify a schema_version
registry item. 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 component is compared to the previously last known schema version for the component. 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, you must increment the schema_version
.
object_types
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.
Example
$registry = {
version => MT->VERSION,
schema_version => MT->schema_version,
object_types => {
'foo' => 'MT::Foo', # Custom data type
'bar' => 'MT::Bar',
},
};
MT::Foo
may be defined like this in it’s own package:
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',
},
});
Extending Existing Object Types
Movable Type allows object types to be extended. This enables 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.
object_drivers
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:
*creating database tables during installation *maintaining database tables during an upgrade *generating SQL statements for a set of standard queries during normal operation
Evaluating Support for Custom Object Drivers
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:
- perform simple CRUD (create, read, update and delete) operations
- supports indexing specific fields
- supports the joining of two tables
permissions
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.
Permission Properties
Permissions within the registry support the following properties:
- label - The name of the label as it should appear when displayed to the user.
- group - The permission group the permission should be displayed within. Valid values are: * sys_admin * blogadmin ** authpub * blog_design * blogupload ** blogcomment
- order - The sort key for your permission to customize where the permission is displayed relative to other permissions.
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.
Permission Scope
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,
},
},
};
config_settings
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:
- ease of implementation
- configuration options do not clutter the user interface
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 Setting Properties
Configuration settings have the following properties.
type
- The data type of the value the configuration setting contains, Valid values are:SCALAR
(default).ARRAY
HASH
default
- The default value of the configuration setting if an explicit setting cannot be found.path
- A boolean value (1 or 0) to indicate whether the value contains a path or not. (TODO - what does it do if it is a path?) Used by MT to convert relative paths into full paths. Takes the values and converts into a full path relative to the location of themt-config.cgi
file.
In the example below, PluginSwitch
is a HASH
, Schema
is an ARRAY
and Goozer
is a SCALAR
:
PluginSwitch foo=bar
PluginSwitch baz=boo
Schema 1
Schema 2
Goozer abc
text_filters
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.
Global Text 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.”
Text Filter Properties
label
- The display name for the filter.handler
- A reference to a subroutine that handles the actual transformation.
See also: How to create a text filter.
In Progress:
applications
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:
- its own private set of list filters
- its own set of menu items that are listed in Movable Type’s built in menu system
- its own set of handlers for processing commands dispatched via the web interface
Application Properties
handler
- The package name that will process calls dispatched via the web interface.cgi-base
- Not currently used. It identifies the name of the CGI file without the .cgi file extension. It is used for URL construction.list_actions
- 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.page_actions
- A page action is a simple link that is surfaced in association with a given object.menus
- A list of menus and menu items to add to the application.methods
- A list of modes (or “commands”) and their associated handlers for processing requests to the application.
Example of a developer-defined application:
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() },
},
};
How to Add a Mode to the CMS Component
In addition to creating a separate application, developer can also register modes in the “cms” application.
Example
$registry = {
applications => {
'cms' => {
methods => {
myapp_new-mode => sub { // my handler // },
},
},
},
};
IMPORTANT NOTE: 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.
CMS Application Specific Stuff
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
- The value of the import_formats key is an array of hashes that define each supported import type and format. Each import type is a hash itself which has the following keys:label
- The name of the format being imported as it will displayed within the user interfacecode
- The handler for processing the import fileoptions
- The input keys for import type specific configuration options.options_template
- The file that contains the HTML and template code for rendering the custom configuration options for the import type.
Example
List Actions
TODO
Page Actions
TODO
Import Formats
TODO
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',
},
},
},
callbacks
For defining callbacks. TODO
tasks
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:
- by a script run externally to the Movable Type web application
- by the web application when the LaunchBackgroundTasks setting is set in the
mt-config.cgi
file
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; }
},
}
Task Properties
- key - a unique identifier for the task
- label - the display name for the task, as it will appear in the activity log and elsewhere
- frequency - the number of seconds to wait between each time the task is run
- code - the handler or callback that will be invoked when the scheduled task runs
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.
junk_filters
TODO
list_filters
TODO
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
TODO
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
TODO
tags => {
block => {
TagNameFoo => &foo_handler,
},
function => {
TagNameFoo => &foo_handler,
},
modifier => { # attribute only, not a tag name - this is for global filters
TagNameFoo => &foo_handler,
},
}
archive_types
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 Type Properties
name
- the name of the archive.archive_label
- the display name of the archive to be usedarchive_file
- TODOarchive_title
- TODOdate_range
- TODOarchive_group_iter
- TODOarchive_group_entries
- TODOdynamic_template
- The file path/pattern to be used under dynamic publishing.default_archive_templates
- An array ofArchiveFileTemplates
which defines the default file/path structure for pages belong to this archive.dynamic_support
- A boolean value (1 or 0) indicating if the archive type supports dynamic publishing.date_based
- A boolean value (1 or 0) indicating if the archive type is paginated by date. This will signal to Movable Type that the archive, in addition to what other attribute entries within the archive collection might share, are also segmented by date.
Example:
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
TODO
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>
<input type="submit" name="submit" value="<__trans phrase="Sign In">" />
</fieldset>
</form>
OpenID
login_form_params => \&_commenter_auth_params,
},
}
captcha_providers
TODO
captcha_providers => {
'mt_default' => {
label => 'Movable Type default',
class => 'MT::Util::Captcha',
}
}
default_templates
TODO