I’ve complained off and on about the lack of user-defined fields in Movable Type. Today was finally the day I got off my high-horse and messed with some code.
Here is a method to add a new field to the MT database. The field can store whatever you like, can be queryed on and sorted by (via Brad Choate’s marvelous SQL plugin), and has its own template tag.
Here are some handy uses for this field.
I’ll show you how to add just one, but you can repeat the process to add as many as you like. I’ve named my field “User1,” but you can call yours whatever you need, just modify the examples accordingly.
I’ve tested this, and it seems to work fine, but no warranties are given nor implied. Additionally, this isn’t supported by anyone, and it will preclude running any upgrades from Six Apart. Finally, this is MySQL-centric. I imagine you could get it to work with other data storage systems, but I haven’t tried.
(Warning – this hack isn’t for the faint of heart. You’re going to have to add columns to your database, and rummage around in Perl code. If you don’t know a subroutine from a submarine, maybe you should skip this one.)
(Another warning – this was written for and tested on MT 2.x. No idea how this would work on 3.x.)
Step One: Add the Field to the Database Add a new field to the “mt_entry” table in your MySQL database. I made mine a text datatype, just so I could put anything in it without running afoul of length limitations and such.
If you want to order on it numerically, you can make it a numeric datatype, but you’ll need to take steps to ensure users only enter numbers in the field.
For the purposes of this instruction, I called mine “entry_user1.” You can call it whatever you like, but it has to start with “entry_”. (Remember, if you call it something else, you’ll need to modify all the code samples on this page to match your field name.)
I don’t know if the position of the field in the table matters, but I put mine on the end. (The issue being that if MT gets all the fields, and expects them in a certain order, you’re hosed.)
Step Two: Add the Column to the Data Map Find this file:
lib/MT/Entry.pm
Somewhere around line 17, you’ll find an array defintion called “columns.” It’ll look like this:
columns => [
‘id’, ‘blog_id’, ‘status’, ‘author_id’, ‘allow_comments’,
‘title’, ‘excerpt’, ‘text’, ‘text_more’, ‘convert_breaks’,
‘to_ping_urls’, ‘pinged_urls’, ‘allow_pings’, ‘keywords’,
‘tangent_cache’,
Add your new field (minus the “entry_” prefix) to end of this, so the last line looks like this:
'tangent_cache','user1',
Step Three: Add a New Textbox to the Editor Find this file:
tmpl/cms/edit_entry.tmpl
Add this code somewhere in the main editing interface table. I put mine starting at line 433, but my line numbers are a bit off from my last hack, so your mileage may vary.
User-Defined Value 1
<><tmpl_if name="AGENT_MOZILLA”> cols=”” class="width500” name="user1” rows="5” wrap="virtual”><tmpl_var name="USER1”></tmpl_var><tmpl_if></tmpl_if></tmpl_if>
This puts a new textbox in the editing interface, and links the field to it. If you want your field to run of some other form element – a drop-down, a checkbox, a radio button, etc. – modify the HTML accordingly.
Step Four: Add the Template Handlers Find this file:
lib/MT/Template/Context.pm
Look for a subroutine called “init_default_handlers.” It starts around line 70. It has a bunch of lines in it that start:
$ctx->register_handler…
Add this line:
$ctx->register_handler(EntryUser1 => \&_hdlr_entry_user1);
This tells MT what to do when it runs into the template tag “MTEntryUser1.” We’re telling to run the subroutine “_hdlr_entry_user1.”
Now you just need to add this subroutine to the file. Find a spot in the file that isn’t in the brackets of any other subroutine (the very end of the file will do), and add this:
sub hdlr_entry_user1 {
my $e = $[0]->stash('entry’)
or return $_[0]->_no_entry_error('MTEntryUser1’);
defined $e->user1 ? $e->user1 : ‘'; }
You’re all done. You now have a new field in MT. You can access the contents of the field in templates with the tag…
``
You can also use Brad’s plugin to query it:
This would give you all the entries where something had been entered in the field.
Here are a couple of handy uses for this:
Explicit Filenames I have MT generate filenames based on the entry title. But what if that filename conflicts with something else, or I just want it to have another name for some reason?
I have a user-defined field called “entry_filename” where I can enter an explicit name for MT to use. If there’s nothing in the field, it forms the filename as usual. (See Mark Pilgrim’s article for how he set this up – he did it using a plug-in to store the filename, but the basic theory is the same.)
Key/Value Data Brad has a great key/value plugin that you can use to store arbitrary data. However, you have to re-use a current field, like the Excerpt or Keywords fields, to use it. You can tack them on underneath data in an existing field, but for some users, this is just too abstract to understand (“You mean that stuff won’t appear in my article? By it will appear over there? Even though I typed it right…there?”).
Add a new field called “entry_keyvalue” and use it to store this data away from everywhere else. This is much easier for the user to get. Access it this way:
``
Arbitrary Ordering Want to control how your entries are ordered on index pages? Just add a field for “entry_sequence” and use Brad’s SQL plugin to get the entries, ordering using this field (“…ORDER BY entry_sequence”). You can number your entries from 1 to a million and they’ll appear in that order on the index page.
(Remember what I said before, however, you need to take steps to make sure users don’t enter non-numeric values in this field. A client-side JavaScript form validation script will do in a pinch.)
If you use this hack, please let me know. I’d be interested in how it works for you, how I can improve this instruction, and what you’re using it for.