1. MT DDC Tokyo
2010.07.31
Theme Framework in Depth
Author: Yujiro Araki
Translation: Nick
2. About “Yujiro”
・ Yujiro Araki(http://twitter.com/yujiro)
・ The author of “Koiki-Kuukan”
http://www.koikikukan.com/
・ He wrote a lot of Movable Type technical
books. (comment by Nick)
2/141
14. How can you customize ?
It’s easier to edit exported theme files.
You can also create a new theme from a
scratch.
14/141
15. You can
• Add/change/delete each theme element
templates/categories/custom fields/web pages, etc
• Customize admin screens
• Add styles for the theme
• Add thumbnails of the theme
15/141
22. theme.yaml
• defines the structure of Theme package
• What is YAML ?
Data definition format by hashes and arrays
A YAML Primer: Yet Another Markup
Language
http://www.movabletype.org/documentation/developer/
a-yaml-primer-yet-another-markup-language.html
22/141
29. Indent in theme.yaml
elements:
plugin_default_pages:
component: ~
data:
post_2:
elements:
plugin_default_pages:
component: ~
data:
Bad intend post_2:
29/141
30. Only UTF-8 is permitted
An error occurs if you use any other
character set than UTF-8.
30/141
31. Note
The following slides are
quoted from the book:
“Movable Type5 professional
guide”,
Written by Yujiro Araki.
31/141
32. What to define in theme.yaml
・Basic information
・Elements
32/141
33. Basic information
label: Classic Blog
id: classic_blog
author_name: Six Apart, Ltd.
author_link: http://www.sixapart.com/
version: 1.0
class: blog
protected: 1
description: __trans phrase=Typical and authentic blogging design comes
with plenty of styles and the selection of 2 column / 3 column layout.
Best for all the bloggers.
thumbnail_file: thumb.png
thumbnail_file_medium: thumb-medium.png
thumbnail_file_small: thumb-small.png
・・・snip・・・
・・・ ・・・
33/141
34. Basic information 1
label: Classic Blog
id: classic_blog
author_name: Six Apart, Ltd.
author_link: http://www.sixapart.com/
version: 1.0
class: blog
protected: 1
description: __trans phrase=Typical and authentic blogging design comes
with plenty of styles and the selection of 2 column / 3 column layout.
Best for all the bloggers.
thumbnail_file: thumb.png
thumbnail_file_medium: thumb-medium.png
thumbnail_file_small: thumb-small.png
*snip*
34/141
35. Basic information 2
label: Classic Blog
id: classic_blog
author_name: Six Apart, Ltd.
author_link: http://www.sixapart.com/
version: 1.0
class: blog
protected: 1
description: __trans phrase=Typical and authentic blogging design comes
with plenty of styles and the selection of 2 column / 3 column layout.
Best for all the bloggers.
thumbnail_file: thumb.png
thumbnail_file_medium: thumb-medium.png
thumbnail_file_small: thumb-small.png
*snip*
35/141
36. Basic information 3
label: Classic Blog
id: classic_blog
author_name: Six Apart, Ltd.
author_link: http://www.sixapart.com/ __trans phrase=will be
version: 1.0 translated to the user
class: blog
language
protected: 1
description: __trans phrase=Typical and authentic blogging design comes
with plenty of styles and the selection of 2 column / 3 column layout.
Best for all the bloggers.
thumbnail_file: thumb.png
thumbnail_file_medium: thumb-medium.png
thumbnail_file_small: thumb-small.png
*snip*
36/141
37. Basic information 4
label: Classic Blog
id: classic_blog
author_name: Six Apart, Ltd.
author_link: http://www.sixapart.com/
version: 1.0
class: blog
protected: 1
description: __trans phrase=Typical and authentic blogging design comes
with plenty of styles and the selection of 2 column / 3 column layout.
Best for all the bloggers.
thumbnail_file: thumb.png
thumbnail_file_medium: thumb-medium.png
thumbnail_file_small: thumb-small.png
・・・snip・・・
・・・ ・・・
37/141
38. Definition of the Elements
The elements include
・template sets
・categories
・folders
・custom fields
・outside files
etc.
38/141
42. Defining each element
Component name
core, commercial, etc
elements:
element name: Importer name
component: core
(required)
importer: template_set
name: template set
data: Element name
・・・snip・・・
(arbitrary)
Beginning of the
element data
43. Example : template_set
element:
template_set:
component: core
importer: template_set
name: template set
data:
label: Classic Blog
base_path: templates
require: 1
templates:
・・・snip・・・
44. Defining template_set
element:
template_set: Importer name
component: core
importer: template_set
name: template set
data: Template set’s
label: Classic Blog name
base_path: templates
require: 1
templates: Template file path
・・・snip・・・
Required flag
Next slide Beginning of the template data
49. Adding a new RSS template
templates:
index:
*snip*
rss:
label: RSS
outfile: rss.xml
rebuild_me: 1
THEME_NAME/templates/rss.mtml
The template file should be saved to this location
52. Defining widget templates
widget:
about_this_page:
label: About This Page
archive_widgets_group:
label: Archive Widgets Group
author_archive_list:
label: Author Archives
calendar:
label: Calendar
category_archive_list:
label: Category Archives
creative_commons:
label: Creative Commons
current_author_monthly_archive_list:
label: Current Author Monthly Archives
current_category_monthly_archive_list:
label: Current Category Monthly Archives
THEME_NAME/templates/ category_archive_list.mtml
53. Defining widget sets
widgetset:
3column_layout_primary_sidebar:
label: '3-column layout - Primary Sidebar'
order: 1000
widgets:
- Archive Widgets Group
- Page Listing
- Syndication
- OpenID Accepted
- Powered By
3column_layout_secondary_sidebar:
label: '3-column layout - Secondary Sidebar'
order: 1000
widgets:
- Search
- Home Page Widgets Group
- About This Page
Sort order is defined here
Only for the available widget
54. Defining default_categories
If you export categories in YAML format
elememts:
default_categories:
component: core
importer: default_categories
data:
Howto:
label: Howto
description: mainly describes…
children:
Plugins:
label: How to use plgins
templates:
label: How to write MT’s…
themes
label:introducing…
Journal:
label: jounal
description: daily journal
55. Defining each category
elements:
default_categories:
component: core
importer: default_categories
data:
howto:
label: Howto
description: mainly describes about
children:
foo:
label: bar
foo:
label: bar
foo:
label: bar
description: foobar
60. Defining custom fields
You can create a new custom field
custom_fields: by editing the theme file directly.
component: commercial
importer: custom_fields
data:
*snip*
cf_1:
obj_type: page
name: productname
type: text
required: 1
tag: PageProductName
You can’t use the existing basename.
61. Defining blog_static_files
Static files such as images, css and
JavaScript to be used in the theme.
・ It will not be associated as an asset.
・ You have to put files under the blog site-
path.
63. Copy files from the specified directories
MT_DIR
elememts: Theme name/
blog_static_files:
component: core blog_static/
importer: blog_static_files css/
data: main.css
- css style.css
- images images/
- js
banner.jpg
credit.png
js/
user.js
Movable Type will automatically jQuery/
jQuery.js
copy the files in directories when jQuery.json.js
exporting the theme.
Sub directories are
included also.
64. Copy files when applying the theme
Directories and files will be copied in
the same structure.
Theme name/
Website path/blog path
blog_static/
css/ css/
main.css main.css
style.css
style.css import
images/ images/
banner.jpg banner.jpg
credit.png credit.png
js/ js/
user.js user.js
jQuery/
jQuery/
jQuery.js
jQuery.js
jQuery.json.js
jQuery.json.js
66. Defining default_prefs
default_prefs:
component: core
importer: default_prefs
data:
name: test
description: This is my first website.
site_url: http://user-domain/
site_path: /home/www/foo
file_extension: html
67. l10n_lexicon
You can translate specific sentence
to the user language by defining it in
the theme file .
For example:
・Template name
・Error message
68. Localization
label: Classic Blog
id: classic_blog
author_name: Six Apart, Ltd.
author_link: http://www.sixapart.com/
version: 1.0
class: blog
*snip*
l10n_lexicon:
ja:
Header: ヘッダー
Footer: フッター
Main Index: メインページ
Page: ウェブページ
Links: リンク
*snip*
69. Example : localizing a template name
l10n_lexicon:
ja:
Main Index: メインページ
elements:
template_set:
data:
templates:
index:
*snip*
main_index:
label: Main Index
outfile: index.html
rebuild_me: 1
*snip*
70. Localization files
MT_DIR
Theme name/
blog_static/
File path/
You can also save Static files
L10n files separately
・・・
from the theme.yaml templates/
( )
Template files(.mtml)
・・・
l10n_ja.yaml
theme.yaml
thumb.png
thumb-medium.png
thumb-small.png 70/141
71. Separating localization files
label: Classic Blog
id: classic_blog
author_name: Six Apart, Ltd.
author_link: http://www.sixapart.com/
version: 1.0
class: blog
*snip*
l10n_lexicon:
l10n_ja.yaml
ja: l10n_ja.yaml
*snip* Header: ヘッダー
Footer: フッター
Main Index: メインページ
Page: ウェブページ
Links: リンク
72. Protected Theme
If you flag the theme as “protected”,
• You have to change the theme name when exporting it.
• You cannot overwrite the existing theme files.
74. Protected Theme
theme.yaml
label: My First website'
id: test
version: 1.0
class: blog
protected: 1
Alert message will be shown,
export and you have to change
the basename.
75. You cannot uninstall the “protected” theme
System → design → theme
protected
Unprotected
79. optional_components
theme.yaml
label: Classic Blog
id: classic_blog
author_name: Six Apart, Ltd.
author_link: http://www.sixapart.com/
version: 1.0
class: blog
optional_components:
poweredit: 0.15
You can still apply this Theme.
It is convenient when you want to enforce users to use the specific plugins.
82. Static Files for the style
MT_DIR
pico/ Files under “static” directory
templates/ will be copied to “mt-static”.
static/
style_library/
pico-darkblue/
screen.css
thumbnail.gif
thumbnail-large.gif
pico-darkgrey/
pico-tan/
pico-white/
pico.html
82/141
86. Defining each style (files)
MT_DIR
pico/
templates/
static/
style_library/
pico-darkblue/
screen.css
thumbnail.gif
thumbnail-large.gif
pico-darkgrey/
Thumbnail for
pico-tan/
the style
pico-white/
pico.html
86/141
87. Defining in theme.yaml
Base path of the style(base.css)
theme.yaml
label: Classic Blog
中略…
中略
…中略
elements:
template_set: Path of the style repository
中略…
中略
…中略
data:
label: Pico
base_path: templates
require: 1
base_css: style_library/base.css
stylecatcher_libraries:
pico:
url: '{{support}}theme_static/pico/style_library/pico.html'
label: Pico Styles
description_label: A collection of styles compatible with Pico themes.
order: 1
87/141
88. Defining repository URL
url: '{{support}}theme_static/pico/style_library/pico.html'
This will be extracted as a support directory path
(equal to $mt:SupportDirectorURL$ tag)
mt-static/
Style resources
(under “style_library” files)
support/
will be copied to
theme_static/ “mt-static” path.
pico/
style_library/
pico.html
{{static}} is also available
(equal to $mt:StaticWebPath$)
)
88/141
89. Defining Pico repository
pico.html
!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN“
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd
html xmlns=http://www.w3.org/1999/xhtml xml:lang=en
head
meta http-equiv=Pragma content=no-cache /
meta http-equiv=Content-Type content=text/html; charset=utf-8 /
meta http-equiv=content-script-type content=text/javascript /
meta http-equiv=content-style-type content=text/css /
meta http-equiv=imagetoolbar content=no /
titlePico Themes/title
link rel=theme type=text/x-theme href=pico-white/screen.css /
link rel=theme type=text/x-theme href=pico-tan/screen.css /
link rel=theme type=text/x-theme href=pico-darkgrey/screen.css /
link rel=theme type=text/x-theme href=pico-darkblue/screen.css /
/head
body
pThis is the Pico Theme set./p
/body
/html
Specify styles as a list of stylesheets
by link tag.
89/141
90. Defining screen.css
screen.css
/*
name: Pico (White)
designer: Jim Ramsey
designer_url: http://www.jimramsey.net/
layouts: layout-w-b
*/
/* White */
body {
background: #fff;
color: #333;
}
*snip*
Style information at the
head of the css file with a
CSS comment style.
90/141
91. Defining layouts
screen.css
/*
A Six Apart theme adapted for Movable
Type default templates
name: Cityscape Portland
designer: Tiffany Chow
designer_url: http://tiffany.vox.com/
layouts: layout-wtt, layout-twt, layout-wt,
layout-tw
*/
/* Default -----------------------------------------------
----------------- */
/* Global */
91/141
93. alt-tmpl
MT_DIR You have to place the templates same as the original
structure. example. alt-tmpl/include/header.mtml
Theme name/
alt-tmpl/
hoge.mtml
static/ CMS template files
css/
js/
templates/
( )
Template files(.mtml)
l10n_ja.yaml
・・・
theme.yaml
thumb.png
thumb-medium.png
thumb-small.png 93/141
96. You can extend the theme framework to
・export various MT objects
・import new objects
You can import and export almost any data
in Movable Type
96/141
97. Example
You can export pages by creating your own
exporter plugin.
plugin:PageExporter
97/141
98. Exporter and Importer
Movable Type Movable Type
exporter theme.yaml importer
export import
You can implement custom importer and
exporter by the plugin.
99. Structure of the sample exporter plugin
MT_DIR
Plugin configuration file
plugins/
PageExporter/
config.yaml
Plugin Implementation
lib/
PageExporter/
tmpl/ theme.pm
export_page.tmpl
Templates for this exporter plugin
99/141
100. config.yaml
Element name
(corresponds to checkboxes which
l10n_lexicon:
appear on the exporting screen)
ja:
Pages: ウェブページ
theme_element_handlers: This will be displayed as a
plugin_default_pages: checkbox’s label on exporting
label: Pages
importer: screen.
import: $PageExporter::PageExporter::Theme::import_pages
info: $PageExporter::PageExporter::Theme::info_pages
exporter:
params: plugin_default_pages_export_ids
condition: $PageExporter::PageExporter::Theme::condition
template: $PageExporter::PageExporter::Theme::template
export: $PageExporter::PageExporter::Theme::export
Definition of the exporter
Definition of the importer
100/141
101. Defining exporter
component: component id of the importer
:
params: parameters for the option screen
config.yaml :
condition: to determine whether display or not
:
template: template files for exporting screen
id: PageExporter
name: PageExporter :
export: exporting method
version: 0.01
l10n_lexicon:
ja:
Pages: ウェブページ
theme_element_handlers:
plugin_default_pages:
label: Pages
importer:
import: $PageExporter::PageExporter::Theme::import_pages
info: $PageExporter::PageExporter::Theme::info_pages
exporter:
params: plugin_default_pages_export_ids
condition: $PageExporter::PageExporter::Theme::condition
template: $PageExporter::PageExporter::Theme::template
export: $PageExporter::PageExporter::Theme::export
101/141
102. condition
Blog object
theme.pm
sub condition {
my ( $blog ) = @_;
my $page = MT-model('page')-load({ blog_id = $blog-id },
{ limit = 1 });
return defined $page ? 1 : 0;
}
Returns:0 or 1
This condition will check whether there would be exportable
web pages or not
→ If exits, exporting checkbox will be displayed.
102/141
104. Use UTF-8 in config.yaml
(if you are in two byte character environment. )
config.yaml
・・・snip・・・
・・・ ・・・
l10n_lexicon:
ja:
Pages: ウェブページ
theme_element_handlers:
plugin_default_pages:
label: Pages
・・・snip・・・
・・・ ・・・
Saving in non UTF-8 code, No problem with UTF-8
they will be garbled!
104/141
105. Template
theme.pm App, blog instance, and saved data in option
sub template { screen (if exists)
my $app = shift;
my ( $blog, $saved ) = @_; Set parameter if you want to display
my @pages = MT-model('page')-load({
the optional list
blog_id = $blog-id,
});
return unless scalar @pages;
my @list;
my %checked_ids;
if ( $saved ) {
%checked_ids = map { $_ = 1 } @{ $saved-{plugin_default_pages_export_ids} };
}
for my $page ( @pages ) {
push @list, {
page_title = $page-title,
page_id = $page-id,
checked = $saved ? $checked_ids{ $page-id } : 1,
};
}
my %param = ( pages = ¥@list );
my $plugin = MT-component('PageExporter');
return $plugin-load_tmpl('export_page.tmpl', ¥%param);
}
Return value : results of lording templates
105/141
108. export
argument:same template
sub export { Get checked data
my ( $app, $blog, $settings ) = @_;
my @pages;
if ( defined $settings ) {
my @ids = $settings-{plugin_default_pages_export_ids};
@pages = MT-model('page')-load({ id = ¥@ids });
} else {
@pages = MT-model('page')-load({ blog_id = $blog-id });
}
return unless scalar @pages;
my $data = {};
for my $page ( @pages ) { Set export date
my $cats = $page-categories;
my $folder;
in parameters
for my $cat (@$cats) { $folder = $cat-basename; }
my $hash = {
title = $page-title,
text = $page-text,
text_more = $page-text_more,
excerpt = $page-excerpt,
keywords = $page-keywords,
basename = $page-basename,
tags = join(',', $page-get_tags),
folder = $folder,
};
$data-{ $page-basename } = $hash;
}
return %$data ? $data : undef;
}
Returns the reference of hash variable108/141
109. Exported data
theme.yaml post:
--- basename: test
author_link: '' excerpt: ''
author_name: nick
class: blog
folder: theme
description: '' keywords: ''
elements: tags: versioning
plugin_default_pages:
component: ~ text: Lorem ipsum dolor sit amet,
data: consectetur adipisicing elit, sed do eiusmod
post:
basename: test tempor incididunt ut labore *snip*
excerpt: ''
folder: theme
text_more: ''
keywords: '' title: test
tags: versioning
text: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
text_more: ''
title: test
importer: plugin_default_pages
id: theme_from_blog2
label: Theme from blog2
name: Theme from blog2
version: 1.0
109/141
110. Defining importer
Info: Information about the data
config.yaml Validator: validate the imported
id: PageExporter data
name: PageExporter Import: execute the import
version: 0.01
theme_element_handlers:
plugin_default_pages:
label: Pages
importer:
info: $PageExporter::PageExporter::Theme::info_pages
import: $PageExporter::PageExporter::Theme::import_pages
exporter:
params: plugin_default_pages_export_ids
template: $PageExporter::PageExporter::Theme::template
export: $PageExporter::PageExporter::Theme::export
condition: $PageExporter::PageExporter::Theme::condition
110/141
111. info
Argument: element objects, theme objects, blog objects
sub info {
my ( $element, $theme, $blog ) = @_;
my $data = $element-{data};
return sub {
MT-translate( 'Pages' ) .'('. MT-translate( '[_1] pages', scalar
keys %{$element-{data}} ) .')';
};
}
Returns: the information for “Blog/Website themes” screen
Displaying the information
111/141
112. import
sub import_pages {
Arguments: element objects, theme objects, applied objects
my ( $element, $theme, $obj_to_apply ) = @_;
my $entries = $element-{data};
_add_entries( $theme, $obj_to_apply, $entries, 'page' )
or die Failed to create theme default Pages;
return 1;
}
Returns: “1” when it completes importing,
sub _add_entries { $obj-save orwhen it’s failed
“error” die $obj-errstr;
my ( $theme, $blog, $entries, $class ) = @_; my $path_str;
my $app = MT-instance; if ( $class eq 'page' ($path_str = $entry-{folder}) ) {
my @text_fields = qw( my @paths = split( '/', $path_str );
title text text_more my ($current, $parent);
excerpt keywords PATH: while ( my $path = shift @paths ) {
); my $terms = {
for my $basename ( keys %$entries ) { blog_id = $blog-id,
my $entry = $entries-{$basename}; basename = $path,
next if MT-model($class)-count({ };
basename = $basename, $terms-{parent} = $parent-id if $parent;
blog_id = $blog-id, $current = MT-model('folder')-load($terms);
}); if ( !$current ) {
next if MT-model($class)-count({ unshift @paths, $path;
title = $entry-{title}, while ( my $new = shift @paths ) {
blog_id = $blog-id, my $f = MT-model('folder')-new();
}); $f-set_values({
my $obj = MT-model($class)-new(); blog_id = $blog-id,
my $current_lang = MT-current_language; author_id = $app-user-id,
MT-set_language($blog-language); label = $new,
$obj-set_values({ basename = $new,
map { $_ = $theme- });
translate_templatized( $entry-{$_} ) } $f-parent( $parent-id ) if $parent;
grep { exists $entry-{$_} } $f-save;
@text_fields $parent = $f;
}); }
MT-set_language( $current_lang ); last PATH;
$obj-basename( $basename ); }
$obj-blog_id( $blog-id ); $parent = $current;
$obj-author_id( $app-user-id ); }
$obj-status( my $place = MT-model('placement')-new;
exists $entry-{status} ? $entry- $place-set_values({
{status} : MT::Entry::RELEASE() blog_id = $blog-id,
); entry_id = $obj-id,
if ( my $tags = $entry-{tags} ) { category_id = $parent-id,
my @tags = ref $tags ? @$tags : is_primary = 1,
split( /¥s*¥,¥s*/, $tags ); });
$obj-set_tags( @tags ); $place-save;
} }
}
1;
}
112/141
113. Exporter plugins by Yujiro
PageExporter
SettingExporter
AssetExporter
EntryExporter
113/141