Abstract Musings

Documenting the random thoughts of a cluttered mind

Integrating the Flash Photoblog World Map With Movable Type

Last month, I added Mark Zeman’s Flash Photoblog World Map to my Photos section. When I originally put the map up, I used the admin tool which came with the map to add the map locations to a separate database. This required using two separate interfaces for each posting – not an ideal solution. So since then, I’ve been looking for a way to integrate the map with Movable Type.

I initially considered two options:

  1. Using an existing field for the extra information required by the map. This was the easiest option available. Unfortunately, it wasn’t really an option for me, since I was already using all of the fields for my posts. Details on this method are available.

  2. Or, I could add the necessary data to the EXIF data of the image. But this was hardly ideal either. I would need to use another program to add the data to the EXIF fields, not much different from using the admin tool to add the data to a database – my original problem.

So, I went looking for another option. In the end, I decided to add some custom fields to the Movable Type entry interface. In the rest of this post, I will detail what modifications I made to my Movable Type installation. But first, most of the credit for this goes to Deane at Gadgetopia for the instructions on adding custom fields to the Movable Type entry interface and Mean Dean at Heal Your Church Web Site for the instructions on using the custom preferences of the entry form to show or hide the new fields.

One last thing before I get started. A warning: this not a simple hack – use it at your own risk. I am not responsible if you attempt to implement this and something goes wrong. I agree with both Deane and Mean Dean, this is not for the faint of heart. If the idea of adding columns to your database, or rummaging around in Perl code doesn’t appeal to you, you are probably better off if you don’t use this method. You might consider the simpler option of using an existing field.

So far these modifications have worked for me using version 3.15 of Movable Type and MySQL. Also, it goes without saying that doing this will probably create problems when performing any future upgrades to your Movable Type installation.

So with all that said, if you are still interested keep reading for all the gory details.

Ok, let’s start.

Backup your files

You’ll be modifying the following four files:
/lib/MT/Entry.pm
/lib/MT/Template/Context.pm
/tmpl/cms/edit_entry.tmpl
/tmpl/cms/entry_prefs.tmpl
So you’ll want to create copies of the originals, in case you mess things up and need to start over or if things don’t work out, you can return your installation to a functioning state. It’s probably a good idea, if you can, to backup the data in your MT database as well.

Add new columns to mt_entry table

You’ll need to add some columns to your mt_entry table to hold the data entered from your custom fields. The column names I use are: entry_mapname, entry_maplocation, entry_mapdate, entry_maplatitude, entry_maplongitude. You can name them anything you’d like, but they need to start with ‘entry_’, and you’ll need to modify my other code samples if you change any of them.

I created my new columns with a datatype of varchar (length of 255), which is adequate for me, but if you don’t want a length restriction then you should be able to use a text datatype without any problems. I also added the new columns to the end of my table.

Add the new columns to the data map

Edit /lib/MT/Entry.pm
This step will modify MT so it knows where to put the data entered into your custom fields.

On or about line 18 you’ll see some code which looks 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', 'basename',

Add a new line at the end of the last line with the new column names (minus the ‘entry_’), so that the code block looks 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', 'basename',
        'mapname', 'maplocation', 'mapdate', 'maplatitude', 'maplongitude',

Remember, if you changed the column names to change them here.

Edit the entry editor preferences

Edit /tmpl/cms/entry_prefs.tmpl
This step will modify the preferences dialog for the entry editing form. It isn’t necessary, but I manage three separate blogs through my MT interface, and I didn’t want these custom fields to show up on the other two editors, since they aren’t much use there. If this isn’t useful for you, then you can safely skip this step, you’ll just need to remove the first and last lines from my code sample in the next step.

In my file, I added the following code at line 40:

<label><input type="checkbox" name="custom_prefs" value="photomap"<TMPL_IF NAME=DISP_PREFS_SHOW_PHOTOMAP> checked="checked"</TMPL_IF> /> 
<MT_TRANS phrase="Flash Photoblog Map Fields"></label><br />

You can place this elsewhere, but this puts it in the preferences dialog after the ‘keywords’ entry which is where I put my custom fields in the editor form. (More on that in the next step.)

Add textboxes to the editor for the new fields

Edit /tmpl/cms/edit_entry.tmpl
This step will modify the editor form to add the custom fields.

Add the following code. I put mine at line 249 which places it just under the keywords textarea. You should be able to place it any where you want the custom fields to appear. Also, if you skipped the previous step, then you’ll need to remove the first and last lines (<TMPL_IF NAME=DISP_PREFS_SHOW_PHOTOMAP> and </TMPL_IF>), otherwise your custom fields won’t show up.

<TMPL_IF NAME=DISP_PREFS_SHOW_PHOTOMAP>
<div class="field"><strong>Flash Photoblog Map Fields</strong></div>
<div style="width:280px;float:left;">
<div class="field">
<div style="width:6.3em;float:left;"><label for="mapname"><MT_TRANS phrase="Name"></label>: </div>
<input name="mapname" id="mapname" tabindex="6" value="<TMPL_VAR NAME=MAPNAME>" />
</div>
<div class="field">
<div style="width:6.3em;float:left;"><label for="maplocation"><MT_TRANS phrase="Location"></label>: </div>
<input name="maplocation" id="maplocation" tabindex="6" value="<TMPL_VAR NAME=MAPLOCATION>" />
</div>
<div class="field">
<div style="width:6.3em;float:left;"><label for="mapdate"><MT_TRANS phrase="Date Taken"></label>: </div>
<input name="mapdate" id="mapdate" tabindex="6" value="<TMPL_VAR NAME=MAPDATE>" />
</div>
</div>
<div>
<div class="field">
<div style="width:5.5em;float:left;"><label for="maplatitude"><MT_TRANS phrase="Latitude"></label>: </div>
<input name="maplatitude" id="maplatitude" tabindex="6" value="<TMPL_VAR NAME=MAPLATITUDE>" />
</div>
<div class="field">
<div style="width:5.5em;float:left;"><label for="maplongitude"><MT_TRANS phrase="Longitude"></label>: </div>
<input name="maplongitude" id="maplongitude" tabindex="6" value="<TMPL_VAR NAME=MAPLONGITUDE>" /> 
<script type="text/javascript">
<!--
function getGPSData(image) {
    window.open('/map/get_gps_data.php?target=' + image,'gpsdata','width=370,height=250,scrollbars=yes,status=yes,resizable=yes');
    return false;
}
// -->
</script>
<TMPL_IF NAME=NEW_OBJECT>
<a href="#" title="Get latitude and longitude for new image" onclick="return getGPSData('test.jpg')" class="help">#</a>
<TMPL_ELSE>
<a href="#" title="Get latitude and longitude for image: <TMPL_VAR NAME=ID>.jpg" onclick="return getGPSData('<TMPL_VAR NAME=ID>.jpg')" class="help">#</a>
</TMPL_IF>
</div>
</div>
<div style="clear:both;height:0;overflow:hidden;">&nbsp;</div>
</TMPL_IF>

My version of MT relies on style sheets for the layout – older versions of MT use tables for layout, so with an older version you might need to tweak this a bit. Also, I’ve added some inline styles to control the positioning of the new fields. I viewed them in Firefox 1.0 and Internet Explorer 6 and they looked fine to me, but if they cause any problems, you can strip out the inline styles and the fields will just show up one after the other.

And remember, if you skipped step 4, you’ll need to remove the first and last lines (<TMPL_IF NAME=DISP_PREFS_SHOW_PHOTOMAP> and </TMPL_IF>).

One more thing, I’ve incorporated a way to get the GPS data from the thumbnail image and put it in the latitude and longitude custom fields. It requires an extra file, which is just a modified version of the read_gps.php file from the original Flash Photoblog World Map download. The modified file can be dowloaded at http://robbyedwards.com/map/integrate.zip. It contains some javascript which when the script is called by opening a new window, sends the GPS data back to the editor, before closing the pop-up window. Just put the file “get_gps_data.php” from the download in the folder where your copy of the Flash Photoblog World Map is located. You’ll also need to modify the line window.open('/map/get_gps_data.php?target=' + image, 'gpsdata', 'width=370,height=250,scrollbars=yes,status=yes,resizable=yes'); you added in this step to point to the location where you placed the file “get_gps_data.php”. To use this script, just click on the “#” sign after the longitude textbox. For a new entry, the image needs to be uploaded as “test.jpg”. Once the entry has been saved, the script will get the GPS information from an image with the entry id as its name. Note: this script requires the exif.php script and the makers directory from the Flash Photoblog World Map download be present in the same directory as your copy of the Flash Photoblog World Map. Also, once you’ve gotten the latitude and longitude from the “test.jpg” image, you’ll need to rename it, to correspond to the file name you specify in your Flash Map’s XML file. This is optional, so if you don’t use it you can remove the code which implements it (everything from the line which reads <script type="text/javascript"> to the first ) and put your coordinates in by hand.

Add template handlers

Edit /lib/MT/Template/Context.pm
This step will add some code to create template tags for the custom fields.

Find the subroutine named “init_default_handlers”. Mine begins on line 73. Add the following lines to this subroutine:

    $ctx->register_handler(EntryMapName => \&_hdlr_entry_mapname);
    $ctx->register_handler(EntryMapLocation => \&_hdlr_entry_maplocation);
    $ctx->register_handler(EntryMapDate => \&_hdlr_entry_mapdate);
    $ctx->register_handler(EntryMapLatitude => \&_hdlr_entry_maplatitude);
    $ctx->register_handler(EntryMapLongitude => \&_hdlr_entry_maplongitude);

I added mine on line 146, which is at the end of the section dealing with the other Entry tags, but it shouldn’t really matter where you add them to that subroutine.

Next, you’ll need to add the actual subroutines that will create the template tags for the custom fields.

Add this code somewhere else in the file making sure not to put it inside another subroutine. I put mine in on line 1395.

sub _hdlr_entry_mapname {
    my $e = $_[0]->stash('entry')
       or return $_[0]->_no_entry_error('MTEntryMapName');
    defined $e->mapname ? $e->mapname : '';
}
sub _hdlr_entry_maplocation {
    my $e = $_[0]->stash('entry')
       or return $_[0]->_no_entry_error('MTEntryMapLocation');
    defined $e->maplocation ? $e->maplocation : '';
}
sub _hdlr_entry_mapdate {
    my $e = $_[0]->stash('entry')
       or return $_[0]->_no_entry_error('MTEntryMapDate');
    defined $e->mapdate ? $e->mapdate : '';
}
sub _hdlr_entry_maplatitude {
    my $e = $_[0]->stash('entry')
       or return $_[0]->_no_entry_error('MTEntryMapLatitude');
    defined $e->maplatitude ? $e->maplatitude : '';
}
sub _hdlr_entry_maplongitude {
    my $e = $_[0]->stash('entry')
       or return $_[0]->_no_entry_error('MTEntryMapLongitude');
    defined $e->maplongitude ? $e->maplongitude : '';
}

Okay, that’s it for changes to the MT files.

Create MT templates

In this step you’ll create two templates in your MT installation.

The first template will build the XML file containing the data for the Flash Photoblog World Map. Create a new index template. Give it a descriptive name. Mine is called “Photo Map XML Data”. (Not original, I know, but it conveys what it is.) Make sure the output file points to the directory where you placed the Flash Photoblog World Map. The output file should be called “mapxml.xml”. For my installation this value is “/map/mapxml.xml”. You could use the PHP based version if you want, just change the output file’s extension from .xml to .php (and modify the index document – see below – to use the PHP version of the map, but you don’t really need to, since the XML file will be created with the new template tags you’ve just added. Check the box labeled “Rebuild this template automatically when rebuilding index templates”, if you want the XML file to be rebuilt whenever a new entry is posted. Add the following code to the “Template Body” section:

<?xml version="1.0" encoding="UTF-8"?>
<datapacket>
<MTEntries lastn="999">
<MTIfNonEmpty tag="MTEntryMapName">
<row id="<$MTEntryID$>" name="<$MTEntryMapName$>" file="<$MTEntryID$>.jpg" url="<$MTEntryPermalink$>" location="<$MTEntryMapLocation$>" date="<$MTEntryMapDate$>" latitude="<$MTEntryMapLatitude$>" longitude="<$MTEntryMapLongitude$>" />
</MTIfNonEmpty>
</MTEntries>
</datapacket>

Save the template.

Notes on the XML document: I use the Entry IDs as row ids and as the image names. You can substitute any other names or ids you want, just make sure that the row ids for the XML file are unique and change the Template Tags where appropriate in the sample code above. Also, I’ve included an MTIfNonEmpty tag with MTEntryMapName as the attribute, so if the Name custom field is empty then no row will be added to the XML file for that post. This will allow the posting of entries that you don’t want to appear on the Flash Photoblog World Map, just keep the Name custom field blank when creating a new entry.

Now, we’ll create the index page for the map itself. Create another new index template. Give it a descriptive name. Mine is called “Photo Map”. (Again, not so creative, but well, that’s what it is.) As with the XML file, make sure the output file points to the directory where you placed the Flash Photoblog World Map. The output file can be called anything you want. For my installation this value is “/map/index.php”. You can use any kind of file you want, though, so HTML files are fine; just use whatever extension you use for your regular MT posts. I left the box labeled “Rebuild this template automatically when rebuilding index templates” unchecked, since this page is static, but if you were including other information from your blog on this page you can check it. Next, add code to build a page for your template to the “Template Body” section. You’ll probably want to copy this code from another template, like your Main Index template. Just add the following code to the template inside the body tags.

<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="600" height="318" id="map" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="map_xml.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="map_xml.swf" quality="high" bgcolor="#ffffff" width="600" height="318" name="map" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>

NOTE: If you are using the PHP based version of the Flash Map then you’ll need to change the name of the Flash file in the code above. Just replace map_xml.swf with map_php.swf in my example above. Be sure to change it in both places where it occurs.

Save the template. Then rebuild it.

Create an entry

Create an new entry to your blog adding the data for the Flash Map to it. When you save the new entry, check your map to see if your changes worked.

Download: http://robbyedwards.com/map/integrate.zip – contains these instructions and the get_gps_data.php script (for extracting the GPS data from the thumbnail images).

You can see this in action at http://robbyedwards.com/map/mt/. (Please note, I don’t use this method for my map, since I switched to WordPress, so this version is a static demo as of April 15, 2005. To see my real map, visit http://robbyedwards.com/map/.) If you use this, please trackback this post, leave a comment, or email me to let me know how it worked. Any other comments, questions or feedback are also appreciated.