cc licensed flickr photo shared by QualityFrog

In a previous long winding post, Dressing up and Displaying WordPress 3 Custom Post Content (a), I tried to document the first part of my WordPress 3 site I manipulated the new Custom Content types. It seems it takes longer to explain that to build, and in the end, I’m wondering if it is even explained.

Leading up to this, I began by overviewing the plans for a new web project, we set up the places to create the new content types, and introduced the benefits using child themes. Then we dove headfirst into the code to create the interface and data elements to add/edit the custom meta data for each of the three content (post) types.

Getting your content onto your site is a matter of manipulating The Loop and then writing new side loops where needed to put things on the sidebar. For this site, MIDEA, the dissected part from the last post was the construct for a gallery page of Project content types and then a display page for a single project.

In this post, I am going to describe what I did about the second Content type, Organizations, in which we track information like web site, Facebook, and twitter urls, street address– and what turned out to be neat, storing a latitude and longitude for each organization so we could generate a dynamic map showing the location of an organization.

I’m not going to detail as much on the template (org.php) that displays all the organizations – there’s not much that is different from the ground covered in the last post. You can explore this page at

(A) The main part of the page is an A-Z listing of all the organizations, using the same abbreviated format used elsewhere ion the site. Like we did do the A-Z project listing, we will use the custom mySQL call to the database so we can avoid the alphabetizing on organizations whose name starts with “The”.

(B) This block on the sidebar lists to other organization pages that will be described below- one generates a google map showing locations of all organizations, the other links provide a list of all the organizations that have Facebook or twitter presences.

(C) The rest of the sidebar is standard looping stuff for sidebars. One lists 3 blog posts from a category we call “Ideas”, the next is the 2 from “News” category, and the last is a random pul of three projects, using logic covered in the previous post in this series.

So generating the list of organizations follows directly from the work we did on projects.

< ?php 
global $wpdb;
global $post;

// custom query to get posts of typer org order by trimming the leading "The " in a title
$orgposts = $wpdb->get_results("SELECT wposts.* 
FROM $wpdb->posts as wposts
WHERE wposts.post_type = 'org' and wposts.post_status='publish' 
ORDER BY TRIM(LEADING 'The ' FROM wposts.post_title)", OBJECT);

< ?php if ($orgposts): ?>
		< ?php foreach ($orgposts as $post): ?>
		< ?php setup_postdata($post); ?>
<h2><a href="<?php the_permalink() ?>" rel="bookmark" title="< ?php printf(__('Permanent Link to %s','gpp_i18n'),the_title_attribute('echo=0')); ?>">< ?php the_title() ?></a></h2>
< ?php get_the_image(); ?>
< ?php the_excerpt(); ?><a href="<?php the_permalink() ?>" rel="bookmark" title="< ?php printf(__('Permanent Link to %s','gpp_i18n'),the_title_attribute('echo=0')); ?>">Learn more about < ?php the_title() ?>...</a>
<div class="clear"></div>
 < ?php endforeach; ?>
< ?php endif; ?>

Lines (05-09) set up the database query to select all organization content, and do the trick to order it by the title after trimming any leading “The” in the title. We then walk through all the results, and lines (17-20) output the content.

Now we turn to making a template to display a single organization, using as an example, the Amon Carter Museum from Fort Worth, Texas.

(A) This is the main “post” for this content type, whatever we put in the blog editing box. It comes with the built in Loop, so we dont have to doctor it up at all.

(B) As an addition to the main content, we’ll do a second Loop to list all the projects from this museum.

(C) The top end of the sidebar displays the icon associated with the organization/post (using the built-in in WordPress “featured image” functionality). This is followed by links to the organization’s web site, Facebook page, and twitter account, if any of these data fields are present (they are optional, some of the organizations don’t have them all).

(D) Here is the special stuff! For each organization, we have meta data fields for latitude/longitude, and we can generate a google map. The details follow!

First, once we have output the descriptive info for this organization (A), we have some left over information we can use- essentially all the variables used in the last loop.

<h3>< ?php the_title() ?> Projects</h3>

< ?php $proj_query = new WP_Query("post_type=proj&posts_per_page=-1&orderby=title&order=ASC&meta_key=midea-proj-org&meta_value=" . get_the_id()); ?>

< ?php if ($proj_query->have_posts()) : while ($proj_query->have_posts()) : $proj_query->the_post(); ?>
< ?php get_the_image(); ?>
<h6><a href="<?php the_permalink() ?>">< ?php the_title() ?></a></h6>
< ?php the_excerpt(); ?><a href="<?php the_permalink() ?>">Learn more about < ?php the_title() ?>...</a>
<div class="clear"></div>
<hr />

< ?php endwhile; endif; wp_reset_query(); ?>

So for example, in line (01) we can use the_title to create the header for projects that includes the organization’s name. It is in line (03) that we set up the logic to get the projects we want- all of the ones assocated with this organization. So to follow out the query string, we want posts that are of type proj, and we want to list all we find (posts_per_page= -1). They are listed in alphabetical order. And the selection is defined by the projects whose meta data field for the organization id matches the id used to generate the content in (A)– the post_id for this content is the same as the meta-date values we need to match.

We then just crank up our Loop structure to put the output on the screen.

Hey, that was easy, right?

Now let’s dig into the sidebar; which again we call in a custom sidebar via

< ?php get_sidebar('single-org') ?>

meaning the template lives in sidebar-single-org.php For the organization info portion (C) we use:

global $post;

$my_fields = midea_org_fields(); // fields for this content type
foreach ($my_fields as $key => $value) {
	// load values into our array
	$my_fields[$key] = get_post_meta($post->ID, 'midea-org-' . $key, true);

< ?php get_the_image( array( 'link_to_post' => false, 'class' => 'thumbnail' ) ); ?>

<h4>< ?php the_title() ?> Online</h4>
< ?php 
if ($my_fields['web'] != '') echo '<p class="biglabel"><a href="' . $my_fields['web'] . '" target="_blank"><img src="' . get_bloginfo('url') .  '/images/world.jpg" width="40" height="40" align="middle" alt=" " /></a> <a href="' . $my_fields['web'] . '" target="_blank">web site</a>';

if ($my_fields['facebook'] != '') echo '<p class="biglabel"><a href="' . $my_fields['facebook'] . '" target="_blank"><img src="' . get_bloginfo('url') .  '/images/facebook.jpg" width="40" height="40" align="middle"  alt=" " /></a> <a href="' . $my_fields['facebook'] . '" target="_blank">facebook</a></p>';

if ($my_fields['twitter'] != '') echo '<p class="biglabel"><a href="' . $my_fields['twitter'] . '" target="_blank"><img src="' . get_bloginfo('url') .  '/images/twitter.jpg" width="40" height="40" align="middle"  alt=" " /></a> <a href="' . $my_fields['twitter'] . '" target="_blank">twitter</a></p>';	

Lines (03-07) calls one of our custom functions to set up an array to store the metadata for this organization, and we then use get_post_meta() to load the values from the database into the array we are using. Take a close look at the strategy. My meta data for organizations I refer to as “url”, “facebook”, “lat”, these are the keys to the array I am creating. Inside the meta-data structure of wordpress, they are named “midea-org-“url”, “midea-org-facebook”, “midea-org-lat”.

Lines 10 and 12 then output the icon and a title, again using the still available data items from the original Loop. Lines (14), (16), and (18) then output the organization URL, facebook URL, and twitter URL but only if there are values for the item. Note for facebook, we store a full URL in the database, but for twitter, we only need the username as the twitter URLs follow from a standard pattern.

Now it is map time. Our organization meta-date includes fields for a latitude and longitude. I looked at a number of map plugins, and found that the Inline Google Map one did the trick even though it was last updated at the end of 2008.

To get map to display, all our template has to produce is the data in the form of an html definition list <dl>….</dl> that includes not only the latitude.longitude to pin the center point of the map, with extra <dt><dd> pairings, we can also add pins and labels to the map. And we can use our other metadata to populate the bubble:

Time to see the code!

< ?php if ($my_fields['lat'] !== '') : ?>
<h4>< ?php the_title() ?> on the Map</h4>

<div id="orgmap">
<dl style="margin-bottom:20px;" title="googlemap;w:100%;h:300">
<dt class="map"><a href="<?php echo $my_fields['lat']?>,< ?php echo  $my_fields['long']?>&om=1"><img src="/images/map-loader.gif" alt="" width="126" height="22" align="middle"/>(map loading...)</a></dt>
<dt><a href="<?php echo  $my_fields['lat']?>,< ?php echo  $my_fields['long']?>" title="marker">map</a></dt>
<dd><h4>< ?php the_title(); ?></h4>< ?php get_the_image(array( 'height' => 60, 'width' => 60,))?>< ?php echo nl2br($my_fields['address'])?>
<a href="<?php echo $my_fields['web']?>">web site</a></dd>
< ?php endif?>

< ?php if ($my_fields['address'] !='') echo '<p><strong>Address</strong>' . nl2br($my_fields['address']) . ''?>	

We test in line (01) for a value in out latitude variable to make sure we should eb drawing a map (no coordinates, no map for you!). The structure in line (05) is what the plugin expects to know this is a <dl> list for a map, as defined by the title attribute. I’ve set the map up here to fill 100% of the space it has (the sidebar) and to be 300 pixels high. You get to pick the dimensions of the map here.

The first <dt> with a class=map in line (06) is special to define the parameters for Google maps, meaning the zoom level (14) and the location of the center of the map ll as fed by the values of our latitude, longitude.

The embedded image and “map loading” text is a nice feature because this is what the page displays while the map takes a few seconds to be rendered. Instead of a blank box, this little bit of code will show a progress bar animation:

which disappears once the plugin places the map. Any other subsequent <dt> <dd> pairs define the map pins- <dt> contains the coordinates for the pin with the google map notation, and <dd> defines what is displayed in the bubble for that pin (you could place many pins on a map– we will do that later) – see line (08). We put a small sized version of the organization’s icon, its physical address (we use nl2br because in the metadata this is a multi line entry with carriage returns this function makes sure we use <br /> tags instead on output).

Line (09) ends the map pin bubble with a hyperlink to the organization’s web site– I see here in my code I should have put an if statement there to make sure we dont do output if there is no URL.

After closing out the map data structure in line (10), we also append the physical address below the map in line (14).

I was rather stoked to get this map part working, because it is automatic. One a previous project, I was making google maps by hand and embedding the code into pages. Here it just happens as long as I feed it some coordinates.

But more bells went off as I saw the structure for setting up the map- I could create a dynamic map that put all the organizations on a map– like this

Not only do I have a map, but using similar approaches to before, on the right, I have a sidebar with an A-Z listgin of all organizations, and since there was empty space below the map, I added some code that would output the info for one organization chosen at random each time the page loads.

Now I could have used the same approach on the big map as the single organization map listed above- to build the <dl> list by hitting the database for all organizations and looping through to find the latitude/longitude, but this seemed a lot of database processing on each page load.

I saw a clever end around- I could generate this content as an external text file, and then I just need to use a PHP include statement to insert the map data into the page for the plugin to process.

This is created as a WordPress Page content type, and I made a special template for it, making it one of the simplest looking Loops

< ?php if (have_posts()) : while (have_posts()) : the_post(); ?>
  <h2>< ?php the_title(); ?></h2>
  < ?php include TEMPLATEPATH . '/data/'?>
  < ?php the_content(); ?>

< ?php endwhile; endif; ?>

Actually the only “content” on the age is the one sentence that is below the map. The trick was realizing I have code in my functions.php file midea_save_post() that is triggered after any content is saved, namely for my custom content types so it can know to save any meta data added– we did that in the post n=on adding the meta data field editing boxes.

So I only have to update this map data file if there has been an edit to the org (organization) content type- menaning someone may have added or updated thee latitude longitude.

In that function, we just add a new temp variable, $midea_update_map_flag, to track of an org content type was updated:

$midea_update_map_flag = false;

if ($post->post_type == "org") {

	$my_fields = midea_org_fields(); // fields for this content type
	$post_flag = 'org';
	$midea_update_map_flag = true;

And after the code for updating all metadata, we trigger a call to a new function, but only if we need to change the map data. This had to come after the meta-date updates in case one of the updates for an org content type was the latitude/longitude.

// now update the cache file for the map data of all orgs
if ($midea_update_map_flag) {

The function that updates the map data is going to run its own Loop, keep a running variable with the contents we need to save to our static file, and then write to the file.

It is almost identical to our code above, except we are going to loop through all the data for all organizations in the database.

function midea_update_map_data($zoom=3,$lat=30.2632,$long=-97.7406) {
	/* called on saving of an org post type tp create the map data to chart 
	   all museum organization. Default settings are centered on Austin

	$my_query = 'post_type=org&post_status=publish&posts_per_page=-1';

	$getOrgs = new WP_Query();
	$map_data = '<dl style="visibility: hidden;" title="googlemap;w:590;h:400">
        <dt><a href="' . $zoom . '&ll=' . $lat . ',' . $long . '&om=1"><img src="/images/map-loader.gif" alt="" width="126" height="22" align="middle"/>(map loading...)</a></dt>' . "\n";
	while ($getOrgs->have_posts()) : $getOrgs->the_post(); 
		$id = get_the_ID();
		$my_fields = midea_org_fields(); // fields for this content type
		foreach ($my_fields as $key => $value) {
			// load values into our array
			$my_fields[$key] = get_post_meta($id, 'midea-org-' . $key, true);
		if ($my_fields['lat'] != '') {
			 $map_data .= '<dt><a href="' .  $my_fields['lat'] . ',' . $my_fields['long'] . '" title="marker">map</a></dt>' . "\n" . '<dd><h4>' . get_the_title($id) . '</h4>' . get_the_image(array( 'echo' => false, 'height' => 60, 'width' => 60,)) .   nl2br($my_fields['address']) . '<a href="' . $my_fields['web'] . '">web site</a><a href="' . get_permalink($id) . '">MIDEA profile</a></dd>' . "\n";
	$map_data .= '</dl>';
	// update the file with the code for the map
	$DATAFILE = fopen(TEMPLATEPATH . '/data/',"w+");
	fwrite($DATAFILE, $map_data) or die(" failed writing $DATAFILE");

Since the organizations are not going to be updated all that frequently, this ought to reduce some small amount of load on the database, since we only have to do this work when there has been a change in any of the location data.

So that is a taste of the ways I have used WordPress Content types for our organization data. Again, you can attach whatever kinds of data you want/need, and pull it as needed for output (or filter in it using the query structure). I was able to easily make a page that lists all the organizations that have twitter accounts and ones that have facebook pages (and links to them). This pages run a loop to fetch all organizations, but on looping through them, it skips any that lack the data being outputted.

These content types open the skies on what is possible, but as you can see, it take some wrangling to sort out the queries and loops. It ain’t for the feint hearted, and I dont expect anyone to follow my logic or method– but this was really meant to show what is possible.

We can map anything to anything.

cc licensed flickr photo shared by rafolio

The last post would be looking at our event content type, and see how we work with and around date and time. You can find all the posts in this series at

Keep on pressing words!

The post "Pinning WordPress 3 Custom Content to the Map (b)" was originally pulled charred and crispy from a smoky charred oven at CogDogBlog ( on July 22, 2010.

No comments yet.

Leave a Comment

All fields are required. Your email address will not be published.