XHTML+MathML+SVG in Movable Type 4
January 13, 2008
Jacques Distler put a great deal of effort into modifying Movable Type 3 so that it could render MathML using the itex2MML plugin, serve XHTML+MathML+SVG content, allow MathML in comments, and validate those comments. This short guide describes how to do the same for Movable Type 4.01. It addresses the following three requirements:
Conversion of one’s own content to valid XHTML+MathML should be automated. I assume here that posts are authored in Markdown with inline itex expressions.
Content should be served with the correct MIME type of
application/xhtml+xml
. However, there is a caveat: some browsers simply can’t handleapplication/xhtml+xml
while others say they can but can’t.itex math expressions should be allowed in comments and validation of those comments should be automated. A single ill-formed comment can render the entire page invalid.
There are other important aspects that I do not consider here, including validation of trackbacks and syndicated content, both equally as important as validating comments, and validation of the administrative interface so that authors can preview posts containing inline MathML or SVG content. When Jacques updated the MT3 interface to be XHTML compliant he ended up with a 1340-line diff file. Fortunately, my preliminary testing indicated that previewing works out-of-the-box on MT 4.01. I hope this means there isn’t much work to be done in this area.
Installation
Uncompress the Movable Type 4 distribution in your website’s document root directory.
Edit
lib/MT/Blog.pm
and changesanitize_spec
totext
in the blog table.
This is related to allowing MathML in comments (addressed below). However,
it is best to perform this step early. Later on, we will edit the database
and paste a very long string in the sanitize_spec
row. The MySQL type of
this row needs to be text
rather than string(255)
in order to hold this
long string.
An alternative option is to simply manually alter the mt_blog
table in
the database to change the type. The advantage of editing the source code
directly is that later, if you install a plugin that updates your database,
your changes will not be lost. Otherwise, Movable Type would read the type
from lib/MT/Blog.pm
and change it back to string(255)
, truncating your
custom whitelist.
Create an empty MySQL database.
Visit the website root in your browser, configure the database, and complete the installation.
Generating MathML Content
This part is rather straightforward. First, install the itex2MML plugin. You will need to compile the itex2MML binary and edit the plugin to point to it. Then, posts authored using the MarkdownMML filter will result in valid XHTML+MathML content.
Serving MathML Content
To serve embedded MathML, we have to make sure that Movable Type is always emitting valid XHTML, served with the proper content type to each browser This involves three steps:
Set the DTD
Put the DTD string in a module called, say, “DTD”:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg-flat.dtd">
Then in the other templates which should include the DTD you can simply write
<$MTInclude module="DTD"$>
. The <?xml>
tag has to be the very first thing
in the file. Unfortunately there are other tags (e.g., MTSetVar
) at the
top of the templates that need to execute before the Header module is loaded.
These tags leave whitespace behind so instead of simply including the DTD
module in the Header module, the least redundant way, I placed a DTD include
at the beginning of every template that needed it. This includes the
following templates: Archive Index, Main Index, Entry, Entry Listing, Page,
Comment Preview, Dynamic Error, and Search Results. Make sure to remove the
old <!DOCTYPE>
tag from the Header module.
For reference, there is a list of Movable Type 4.0 Default Templates.
Set the MIME Type
Here I’m following Jacques’s post, “Serve it up!”. To
configure Apache to set the required content type for each browser, place the
following in your .htaccess
file:
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_USER_AGENT} MSIE.*
RewriteRule \.html$|\.xhtml$ - [T=text/html]
RewriteCond %{HTTP_USER_AGENT} Gecko|W3C.*Validator|MSIE.*MathPl
RewriteRule \.html$|\.xhtml$ - [T=application/xhtml+xml]
RewriteCond %{HTTP_USER_AGENT} Chimera|Camino|KHTML
RewriteRule \.html$|\.xhtml$ - [T=text/html]
RewriteCond %{HTTP_USER_AGENT} Camino.*MathML-Enabled
RewriteRule \.html$|\.xhtml$ - [T=application/xhtml+xml]
These rules tell Apache to set the content type to text/html
for Internet
Explorer, Chimera, Camino, and KHTML-based browsers (except for MSIE with the
Math Player plugin and MathML-enabled Camino builds) and
application/text+xhtml
for everyone else. Here, I have allowed for either
.html
or .xhtml
extensions. You can set the default (archive) file
extension under Blog Settings | Publish.
We also need to make sure all CGI-generated pages are also served with the
proper MIME type (for example /mt-comments.cgi?entry_id=1
,
which is still served as html/text
). We can do this by passing an
environment variable to Movable Type which gets set by Apache based on the
user agent string. First, add the following to your .htaccess
or Apache
configuration:
RewriteCond %{HTTP_USER_AGENT} MSIE.*
RewriteRule ^mt-comments|^mt-search - [E=HTTP_CONTENT_TYPE:text/html]
RewriteCond %{HTTP_USER_AGENT} Gecko|W3C.*Validator|MSIE.*MathPlayer
RewriteRule ^mt-comments|^mt-search - [E=HTTP_CONTENT_TYPE:application/xhtml+xml]
RewriteCond %{HTTP_USER_AGENT} MSIE.*|Chimera|Camino|KHTML
RewriteRule ^mt-comments|^mt-search - [E=HTTP_CONTENT_TYPE:text/html]
RewriteCond %{HTTP_USER_AGENT} Camino.*MathML-Enabled
RewriteRule ^mt-comments|mt-search - [E=HTTP_CONTENT_TYPE:application/xhtml+xml]
Then, edit lib/MT/App.pm
as follows:
--- lib/MT/App.pm 2007-10-18 20:55:05 +0000
+++ lib/MT/App.pm 2007-10-18 23:03:11 +0000
@@ -518,6 +518,9 @@
sub send_http_header {
my $app = shift;
my($type) = @_;
+ if ($ENV{'HTTP_CONTENT_TYPE'}) {
+ $type = $ENV{'HTTP_CONTENT_TYPE'};
+ }
$type ||= 'text/html';
if (my $charset = $app->charset) {
$type .= "; charset=$charset"
Validate Other Pages
Edit any remaining template items that don’t validate. Check all types of pages such as:
/
/archives/
/mt-comments.cgi?entry_id=1
/mt-search.cgi?search=test
In particular, you should escape the script that appears in the Comment Form:
<script type="text/javascript">
<!--//--><![CDATA[//><!--
<MTIf name="comment_preview_template">is_preview = true;</MTIf>
writeCommenterGreeting(commenter_name, <$MTEntryID$>, <$MTEntryBlogID$>, commenter_id, commenter_url);
//--><!]]>
</script>
NumericEntities
Finally, install the NumericEntities plugin to prevent sending named entities to browsers that can’t handle them.
MathML in Comments
Here I will outline how to enable MathML in comments and describe one solution for to automatically validating them. I will punt on the issue of Trackbacks.
Processing itex in Comments
First, visit Blog Settings | Comment and check “Allow HTML.”
Change “Limit HTML Tags” to “Use my settings” and enter the tags listed in Jacques’s whitelist.
Since this textarea
is so small, I actually just put some temporary tags
here and then edited the blog_sanitize_spec
column of my mt_blog
table.
Make sure you get these right or you may experience MathML problems.
- Change “Text Formatting” to “itex to MathML with parbreaks.”
Enforcing Validation
Ideally we only want to accept comments that successfully validate after passing them through the MarkdownMML filter. This needs to be handled by a plugin. Jacques wrote the MTValidate which is a wrapper around the Perl module which powers the W3C Validator. It runs locally and thus has several dependencies.
I hacked up another plugin, called FragmentValidator, which can send an XHTML fragment off to the W3C Validator service for validation.1 Below, I show how to modify the default templates to force comment previews and to only allow submission of valid comments.
First, install the FragmentValidator plugin.
The Comment Preview template can handle the validation:
<MTValidateFragment> <$MTInclude module="DTD"$> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Your Comment</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> </head> <body> <$MTInclude module="Comment Detail"$> </body> </html> </MTValidateFragment>
as well as the reporting:
<MTIfFragmentInvalid>
<p>Your is <strong>not</strong> valid <code>XHTML 1.1 + MathML 2.0 + SVG 1.1</code>. Please revise and re-submit.</p>
</MTIfFragmentInvalid>
<MTIfFragmentValid>
<p>Your is valid <code>XHTML 1.1 + MathML 2.0 + SVG 1.1</code>.
You may submit it.</p>
</MTIfFragmentValid>
To ensure that the submit button is available only after previewing and successful validation, revise the Comment Preview and Comment Form templates as follows:
<MTIf name="comment_preview_template"> <MTIfFragmentValid> <input type="submit" accesskey="s" name="post" id="comment-submit" value="Submit" /> </MTIfFragmentValid> <input type="button" name="cancel" id="comment-cancel" value="Cancel" onclick="window.location='<$MTEntryPermalink$>'" /> </MTIf>
Note that this approach does not prevent someone from previewing a valid comment, thus making the submit button visible, and then editing it and ultimately submitting an invalid comment. Although I haven’t implemented this myself, the MTHash plugin allows one to force comment previews.
-
This plugin is just a quick hack and certainly leaves much to be desired. Since I don’t use Movable Type myself I don’t have plans to improve it. If you’d like to do so, feel free. Alternatively, for a local validation approach I would suggest starting with the MTValidate plugin. ↩