Adding User-Defined Fields in Movable Type

By on October 8, 2003

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


<textarea cols="" class="width500" name="user1" rows="5" wrap="virtual">




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.

###

What Links Here

Comments

  1. john says:

    Outstanding Deane - Next time I need this functionality I'll give it a try.

  2. Deane says:

    For my next trick, I want MT to use different "edit_entry" templates for each blog, so that you can customize the editing interface -- label the fields differently, have different form elements, etc.

    For instance, you could have a blog for an event calendar, and have fields for "recorrance," "start time," "end time," etc. Instead of the user having to remember want info gets hacked into what field, you could present them with a customized editor.

  3. Why didn't I see this post earlier? I could kiss you on the lips for this one.

    I've been thinking, if I could just add 2 fields, I could take the "web links" portion of blogs4God and move it entirely into MovableType.

  4. Deane says:

    I just did an install with no less than six user-defined fields: one for key/value information, and five for whatever. It seems to work perfectly.

  5. Hans says:

    Thanks for the great hack! It is very well described and even feaseable for a "faint of heart" as me.

    An addition: If you want to use the public search module built in MovableType (I use 2.661) you just need to extend a single line in

    /lib/MT/App/Search.pm

    Around line 440 you'll find the subroutine sub searchhit

    Just add your new field like this:

    @textelements = ($entry->title, $entry->text, $entry->textmore, $entry->keywords, $entry->user1);

    and you are done.

  6. Hi,

    this hack doesn't seem to work with version 3.11. Was anyone of you able to do the same thing in v3.11?

  7. sasco says:

    The main problem I'm having in MT v.3.* is that the template file no longer uses tables, instead it uses CSS for everything. I'm sure this is still a feasable hack but it's going to take someone a lot smarter than me to figure it out.

  8. Ah I figured it out!

    I added a post on how to add an extra field (including changing some other back-end display options and taking into account your new field in the export function) on my site (see Category MovableType) in Dutch. I'm planning on translating the post to English as well, but in the mean time:

    If anyone needs any help on this in any other language then just let me know (not here, but on my own site...)!

  9. Fabio says:

    Hi :) just used that and some other stuff i developed to build some avatar stuff on my site's comments system :))

    thanks! if you want to know what i did contact me on morroida at gmail :)

  10. mattiz says:

    You are a hero! This works excellent and very easy to do.

  11. This hack is great, and makes MT more of an all purpose CMS. I wish it didn't prevent upgrading MT's version though.

  12. Craig says:

    Thanks for these instructions. I implemented them with 2.66 but soon enough it got to be a pain to upload my saved tmpl files all the time.

    I think I will settle for Brad Choate's KeyValue plugin, even if it is a little less 'elegant' because I don't need to "upgrade" every upgrade. This is a personal choice but for people who would rather not bother tweaking code everytime, it is worthy of consideration.

  13. Ben says:

    Hi, Thanks for the tip. Unfortunately, I am looking for native support of this feature since I am running under the BerkleyDB, not MySql. But, thanks again for sharing this with me. I'll bookmark it and maybe I will switch to MySql in the near future so this will work.

  14. Dave says:

    Hey thanks for this - got the bank end working fine - but when i try to build the site, i get

    Undefined subroutine &MT::Template::Context::hdlrentry_location called at lib/MT/Builder.pm line 159. any ideas?

    line 59 is : my $out = $h->($ctx, \%args, $cond);

    thanks!

  15. Dave says:

    Found the problem! Is was the space between sub and hdlrentry_user1 {

    sub hdlrentry_user1 {

    should be

    subhdlrentry_user1 {

    thanks!

  16. Dave says:

    Ok, I'm stuck now!

    It's all working in the backend

    I've got nice fat tags in my individual template like

    But nothing appears!

    Any ideas?

  17. Dave says:

    fixed it - stoopid typo in context.pm - sorry!

  18. Kevin says:

    Hi, thanks for the tip! I have a question I'm hoping someone can answer. I added a new field that holds an integer, and everything worked great. So I tried adding a second field the exact same way as the first (only difference is that it holds a varchar(40) data type in the mysql db). However, whenever I reference my other tag, I get the following error:

    "Building results failed: Error in tag: Error in tag: You used an 'MTEntryBoxShot' tag outside of the context of an entry; perhaps you mistakenly placed it outside of an 'MTEntries' container?"

    Again, I followed the instructions exactly the same in both cases, but for some reason, the second on I added wants to be inside an tag. Any ideas?

  19. Kevin says:

    Nevermind, I figured out my problem :)

    Thanks again for such a well written tutorial, this was a life-saver for me on a project I'm working on right now.

  20. Cameron says:

    designing something that needs another field in 3.2, but unfortunately I get an error 500 when I try this method. Darn!

  21. But you have to pay for it so i can use the time for you.

  22. Inda says:

    Hi, people!! Great new site about gay sex! The youngest, freshest and hardest porn for FREE! Updates Everyday! gay twins kissing Best porno pics and video !!! All category, millions movies and photos !!!

Add a Comment