hacking movable type training - day 1
DESCRIPTION
A series of slides presented to developers wishing to learn how to build plugins using Movable Type.TRANSCRIPT
Page
SixApartLtd.
HackingMovableTypeAguidefordevelopers
Page
DevelopingforMovableTypein2Days
• Day1:TheBasics– PluginStructure
– TheRegistry
– ConfiguraEonDirecEves
– TemplateTags
– Objects
– Callbacks
• Day2:BuildingUserInterfaces– Menus
– NewApplicaEonScreens
– Dialogs
– LisEngScreens
2
Page
CoursePrerequisites
• ThiscourserequiresthatyouhaveaccesstoaworkingMovableTypeinstalla:on.
• Thiscoursealsoassumesthatyouarefamiliarwithbasicsystemadministra:ontasks,suchascopyingandediEngfiles.
• Finally,thiscourserequiresknowledgeofthePerlprogramminglanguage.
Page
AboutHacking
• Whatmakesagoodprogrammer?
• UsethesourceLuke.
• Copyandpasteisyourfriend.
Page
DevelopingforMovableTypein2Days
• Day1:TheBasics➡ PluginStructure
– TheRegistry
– ConfiguraEonDirecEves
– TemplateTags
– Objects
– Callbacks
• Day2:BuildingUserInterfaces– Menus
– NewApplicaEonScreens
– Dialogs
– LisEngScreens
5
Page
PluginStructure
• Yourconfigfile– plugins/MyPlugin/config.yaml
• Yourlibraryfiles– plugins/MyPlugin/lib/*
• Yourtemplates– plugins/MyPlugin/tmpl/*
• YourstaEcimages,javascriptandCSSfiles– mt-static/plugins/MyPlugin/*
Page
PluginStructure
• Otherimportantfilesanddirectories– MyPlugin-README.txt– MyPlugin-LICENSE.txt– plugins/MyPlugin/t/*– plugins/MyPlugin/extlib/*– plugins/MyPlugin/php/*
Page
PluginPackaging
• FilesthatreallyhelpwiththepackaginganddistribuEonofanenEreplugin:– Makefile.PL
– MANIFEST.SKIP
Page
Makefile.PL
use ExtUtils::MakeMaker;WriteMakefile( NAME => "My Plugin's Display Name", VERSION => '1.1', DISTNAME => 'MyPlugin',);
Page
MANIFEST.SKIP
# version control\bCVS(^|/)\.
# CPAN chain files^MANIFEST^Makefile^META.yml$^blib/~$
# packages\.zip$\.tar\.gz$
Page
CreaEngaPluginZipFile
Thesesimplesequenceofcommands:
> perl Makefile.PL> make manifest> make zipdist
WillproducetheperfectMTPluginzipfile:• MyPlugin‐1.1.zip
Page
DevelopingforMovableTypein2Days
• Day1:TheBasics– PluginStructure
➡ TheRegistry
– ConfiguraEonDirecEves
– TemplateTags
– Objects
– Callbacks
• Day2:BuildingUserInterfaces– Menus
– NewApplicaEonScreens
– Dialogs
– LisEngScreens
12
Page
TheMovableTypeRegistry
• MovableTypeisaconfig‐drivenapplicaEon.
• The“registry”iswhereMovableTypestoresitsconfiguraEon.
• Eachpluginprovidesamini‐registryfile,config.yaml,thatdefinesitsfeatureset.
• Theconfig.yamlfileisthenmergedintoMovableType’smainregistry.
• Everypluginthereforeis50%configuraEonand50%code.
Page
config.yaml
name: Fluid Appid: FluidAppauthor_link: http://www.majordojo.com/author_name: Byrne Reesedescription: This plugin provides enhanced support for Fluid.version: 0.90plugin_link: http://www.majordojo.com/projects/mt-fluid-app.php
applications: cms: methods: fluid_update: $FluidApp::FluidApp::Plugin::cms_updatecallbacks: MT::App::CMS::template_source.header: $FluidApp::…::Plugin::xfrm
Page
YikesWhatisallthat?
Page
AYAMLPrimer
• YAML==“YetAnotherMarkupLanguage”
• XMLAlternaEve
• Lessverbose
• Humanreadableandwritable
Page
XMLvs.YAML
<?xml version="1.0"><address> <first_name>Byrne</first_name> <last_name>Reese</last_name> <email>[email protected]</email> <company> <name>Six Apart, Ltd.</name> <street_address> 548 4th Street, San Francisco, CA 94107 </street_address> </company></address>
Page
XMLvs.YAML
address: first_name: Byrne last_name: Reese email: [email protected] company: name: Six Apart, Ltd. street_address: 548 4th Street, \ San Francisco, CA 94107
Page
YourFirstPlugin
Placethisin:plugins/Good4Nothing/config.yaml
name: Good for Nothing Plugin for Movable Typeid: Good4Nothingkey: Good4Nothingauthor_link: http://www.yourwebsite.com/author_name: Your Name Heredescription: This plugin is an example plugin.version: 1.0
Page
DevelopingforMovableTypein2Days
• Day1:TheBasics– PluginStructure
– TheRegistry
➡ Configura:onDirec:ves
– TemplateTags
– Objects
– Callbacks
• Day2:BuildingUserInterfaces– Menus
– NewApplicaEonScreens
– Dialogs
– LisEngScreens
20
Page
AddingaConfigDirecEve
• WhatisaconfiguraEondirecEve?
• WhenshouldyouuseaconfigdirecEve?
• WhatarethealternaEves?
Page
AddingaConfigDirecEve
name: Good for Nothing Plugin for Movable Typeid: Good4Nothingkey: Good4Nothingauthor_link: http://www.yourwebsite.com/author_name: Your Name Heredescription: This plugin is an example plugin.version: 1.0config_settings: MyImageURL: default: http://path.com/images/foo.jpg
Page
ConfigDirecEveProperEes
• RegistryproperEes:– path
– handler
– alias
Page
DevelopingforMovableTypein2Days
• Day1:TheBasics– PluginStructure
– TheRegistry
– ConfiguraEonDirecEves
➡ TemplateTags
– Objects
– Callbacks
• Day2:BuildingUserInterfaces– Menus
– NewApplicaEonScreens
– Dialogs
– LisEngScreens
24
Page
AddingaTemplateTag
• Whatisatemplatetag?
• Wherearetemplatetagsused?
• Whatarethevarioustypesoftemplatetags?
Page
AddingaTemplateTag‐config.yaml
name: Good for Nothing Plugin for Movable Typeid: Good4Nothingkey: Good4Nothingauthor_link: http://www.yourwebsite.com/author_name: Your Name Heredescription: This plugin is an example plugin.version: 1.0config_settings: MyImageURL: default: http://path.com/images/foo.jpgtags: function: MyImageURL: $Good4Nothing::Good4Nothing::Plugin::tag
Page
AddingaTemplateTag‐Handler
# Good for Nothing Plugin for Movable Type# Author: Your Name Here, [email protected]# Copyright (C) 2008 Your Name Here# This file is licensed under the Artistic License,# or the same terms as Perl itself.
package Good4Nothing::Plugin;use strict;
sub tag { my ($ctx) = @_; my $cfg = $ctx->{config}; return $cfg->MyImageURL; }
1; # Every module must return true
Page
MoreaboutTemplateTags
• TypesofTags– funcEon
– block
– modifiers
• CondiEonalTags
• Loops
• TemplateContext
Page
MoreaboutTemplateTags
tags: function: 'SaySomething': $Example::Example::Plugin::SaySomething
'SayWhatever': $Example::Example::Plugin::SayWhatever block: 'LoopTenTimes': $Example::Example::Plugin::LoopTenTimes 'IfOdd?': $Example::Example::Plugin::IfOdd
Page
MoreaboutTemplateTags
# Example: <mt:SaySomething>sub SaySomething { my ($ctx, $args) = @_; return "Something""; }
# Example: <mt:SayWhatever say=”Hello”>sub SayWhatever { my ($ctx, $args) = @_; # What the person passed in through the # argument 'say' my $input = $args->{'say'}; return $input;}
Page
MoreaboutTemplateTags
# Example: <mt:LoopTenTimes>...</mt:LoopTenTimes>sub LoopTenTimes { my ($ctx, $args, $cond) = @_; my $out = ""; my $builder = $ctx->stash('builder'); my $tokens = $ctx->stash('tokens'); for (my $i = 1; $i <= 10; $i++) { $ctx->stash("current_loop_number",$i); $out .= "$i * " . $builder->build($ctx,$tokens,$cond); } return $out;}
# Example: <mt:IfOdd>do this</mt:IfOdd>sub IfOdd { my ($ctx, $args, $cond) = @_; my $num = $ctx->stash('current_loop_number'); if ($num % 2 == 0) { return 0; } else { return 1; }}
Page
AddingaTagModifier
• Whatisatagmodifier?
• Examplemodifiers:– <mt:block capitalize=“1”>abc</block>– <mt:block ltrim=“1”> foo</mt:block>– <mt:block regex_replace=“/foo/gi”,”bar”>
foo</mt:block>
– <mt:block count_words=“1”>Hello World</mt:block>
Page
AddingaTemplateTag‐config.yaml
name: Good for Nothing Plugin for Movable Typeid: Good4Nothingkey: Good4Nothingauthor_link: http://www.yourwebsite.com/author_name: Your Name Heredescription: This plugin is an example plugin.version: 1.0config_settings: MyImageURL: default: http://path.com/images/foo.jpgtags: modifier: lolcats: $Good4Nothing::Good4Nothing::Plugin::lolcats
Page
AddingaModifier‐Handler
# Good for Nothing Plugin for Movable Type# Author: Your Name Here, [email protected]# Copyright (C) 2008 Your Name Here# This file is licensed under the Artistic License,# or the same terms as Perl itself.
package Good4Nothing::Plugin;use strict;
sub lolcats { my ($str, $val, $ctx) = @_; return "CAN I HAZ “. uc($str);}
1; # Every module must return true
Page
DevelopingforMovableTypein2Days
• Day1:TheBasics– PluginStructure
– TheRegistry
– ConfiguraEonDirecEves
– TemplateTags
➡ Objects
– Callbacks
• Day2:BuildingUserInterfaces– Menus
– NewApplicaEonScreens
– Dialogs
– LisEngScreens
35
Page
ObjectsandDataPersistence
• AnoverviewofMovableTypeObjects–MT::Entry
–MT::Comment
– Etc.
• BeforeandaferarecordismodifiedintheMovableTypedatabase,aneventisfired.
• Pluginscanregistercallbacksforthoseevents.
Page
AboutMTObjects
• MovableType’sDataAbstracEonLayer– Data::ObjectDriver
–MT::ObjectDriver
–MT::Object
• MemcachedSupport
• EventPropagaEon
• Gegers/Segers
Page
InteracEngwithMT::Objects
• MT::MyObject‐>new()• $obj‐>save()• MT::MyObject‐>load($terms,$arguments)• MT::MyObject‐>load_iter($terms,$arguments)• $obj‐>remove($terms,$arguments)• MT::MyObject‐>remove_all• MT::MyObject‐>count($terms)• MT::MyObject‐>exists($terms)• $obj‐>clone()
Page
new()andsave()
my $foo = MT::Foo->new;$foo->first_name(’Byrne');$foo->last_name(‘Reese’);$foo->save() or die "Saving foo failed: ", $foo->errstr;
Page
load()
my @objects = MT::Foo->load( { title => "Hello World", foo => "bar", }, { sort => 'created_on', direction => 'ascend', });
foreach my $obj (@objects) { print $obj->baz(); # do something}
Page
load_iter()
my $iter = MT::Foo->load_iter( { title => "Hello World", foo => "bar", }, { sort => 'created_on', direction => 'ascend', });
while (my $obj = $iter->()) { print $obj->baz(); # do something}
Page
loadandload_iterOpEons
• sort• direcEon• limit• lastn• offset• start_val• range• range_incl• join• unique
Page
CreaEngYourOwnObject
• SchemadefiniEon• Registeringdatabaseindexes• RecordaudiEng• Primarykeys• MetaData
Page
MT::MyObject
package Example::MyObject;
use strict;use base qw( MT::Object );
__PACKAGE__->install_properties({ column_defs => { 'id' => 'integer not null auto_increment', 'blog_id' => 'integer', 'some_property' => 'string(100) not null', }, audit => 1, indexes => { id => 1, }, datasource => 'myplugin_myobject', primary_key => 'id',});
Page
MT::ObjectDatabaseDataTypes
• string• integer• boolean• smallint• dateEme• blob• text• float
Page
ExtendingExisEngObjects
• ExtendingexisEngobjects• schema_versionregistryproperty
Page
ExtendingExisEngObjects
name: Example Plugin for Movable Typeid: Examplekey: Exampledescription: This plugin is an example pluginversion: 1.0schema_version: 2object_types: entry: is_featured: smallint
Page
ExtendingExisEngObjects
• Whichthenlet’syoudothis:
use MT::Entry;my $entry = MT::Entry->load($id);$entry->is_featured(1);$entry->save;
Page
DevelopingforMovableTypein2Days
• Day1:TheBasics– PluginStructure
– TheRegistry
– ConfiguraEonDirecEves
– TemplateTags
– Objects
➡ Callbacks
• Day2:BuildingUserInterfaces– Menus
– NewApplicaEonScreens
– Dialogs
– LisEngScreens
49
Page
MovableTypeCallbacks
• Nomenclature:– Event,Callback,Fire,Listening
• EventTypes– ObjectLevel
– ApplicaEonLevel
– TransformaEon
Page
Object‐LevelCallbacks
• Events:–MT::ObjectName::pre_save
–MT::ObjectName::post_save
–MT::ObjectName::pre_load
–MT::ObjectName::post_load
–MT::ObjectName::pre_remove
–MT::ObjectName::pre_remove_all
–MT::ObjectName::post_remove_all
Page
AddingaCallback‐config.yaml
name: Good for Nothing Plugin for Movable Typeid: Good4Nothingkey: Good4Nothingauthor_link: http://www.yourwebsite.com/author_name: Your Name Heredescription: This plugin is an example plugin.version: 1.0callbacks: MT::Entry::pre_save: $Example::Example::Plugin::pre_save
Page
AddingaCallback‐Handler
package Example::Plugin;use strict;
sub pre_save { my ($cb, $obj) = @_; # $cb - holds a reference to the callback object # $obj - holds a reference to the object about # to be saved # do your thing if ($error) { return $cb->error(“Error Message”); }}
Page
Exercise:AddaCallback
• YourMission:AddarecordtotheMovableTypeAcEvityLog,justaferanentryissaved.
• AboutLogging:
MT->log({ message => "DemoPlugin: an object was saved.", class => 'system', level => MT::Log::INFO() });});