Hacks, Tutorials

Modify Solspace’s Tag Module To Show a Clickable List of Links

February 23, 2009
by Ryan Masuga

Solspace’s Tag Module has an incredibly large, unused area below the tag entry field. See how to turn that area into a clickable list of links.

Large empty area in Solspace Tag ModuleThe large area at the lower left of the default Tag module layout is so barren you can hear crickets chirping.

The Tag Module is one of Solspace’s best, most useful offerings, allowing you to tag weblog entries, relate those entries that are similarly tagged, and various other things. In addition, Tag is one of the only EllisLab Certified Third-Party Modules.

As useful as Tag is with helping to categorize and relate your content, I didn’t think Tag itself was as useful as it could be. I was looking for a way to make it easier to find the existing tags. I didn’t want to have to type for every single tag, especially if the article needed numerous tags. This could be quite a time suck, especially for hacky hunt-n-peck typers like myself. I started a thread called List the Existing Tags as Links in the Space Below Search? Then Click to Add? at the Solspace forums looking to see if there was any way to modify the Tag tab, and I posted a quick mockup of what I wanted (shown below).

Original Tag mockupThis was what I originally wanted – a simple list of tags that the user could click to add to the tag field.

Eventually Solspace added the ability to search for all Tags with asterisk in Tag 2.5, but doesn’t document that anywhere for the end user (our suggestions is to add an explanation of the asterisk to the text directly above the Tag Browser field – but that’s not the point of this article). Anyway, the asterisk is a nice addition, and wasn’t available at the time I made the original request. If you want to take it a step further and list tags beneath the tag search field for easy clicking, then read on. This may seem like a difficult hack, but it’s pretty easy to implement, and easy to re-apply if you upgrade your Tag module.

There are a couple things to note first. The line numbers in this tutorial are in reference to Solspace Tag version 2.6.2, and fortunately all the modifications take place in mod.tag.php. This file is found in your system folder after you’ve installed the Tag module: system → modules → tag → mod.tag.php.

Also of note, Elwin Zuiderveld, aka Cocoaholic (EE Forums, Solspace forums) was kind enough to supply me with the original version of this tag hack, which I further modified to what you see in this article (and hence the reason many of the styles are prefixed with “ez_”). I’m also assuming that your tag list is keeping each tag on a separate line, rather than comma-delimited. I haven’t tested this on an install that stores tags in a comma-delimited way. The line numbers indicated also are only close if you make the changes below in the order in which they’re made in the tutorial.

The Desired Result

Our Desired Output: A list of tags, with the number of entries using that tag listed (subtly) next to the tag.

Basically, all I wanted to achieve was a list of tags, with the number of entries using that tag listed (subtly) next to the tag, and have them ordered by the most popular to the least. This way, the most commonly used tags are right there – no need to type them in every time. When a tag is clicked, it is added to the list on the right-hand side, and disappears from the tag list. The final look is pretty easy to manipulate with the CSS. You can also pretty easily alter the query to pull tags alphabetically, or by most common. There are a number of modifications you can do relatively easily. Let’s start by recreating what we see here.

Going In

There are three sections we have to change, alter, or update:

  1. We need to insert the queries for the tags and the markup for the tag list.
  2. We need to insert come CSS to style up this area
  3. We need to add the JavaScript (jQuery) so that clicking the links adds them to the tag list.

The SQL Queries and Markup

First we add the new SQL queries to get the existing tags, and the markup to display the tag list. At approximately line 5706, look for the following:

/**  ----------------------------------------
/** Begin tag browser results
/** ----------------------------------------*/
$b .= '<div id="tag_browse_results" style="display:none;">';
$b .= $DSP->div_c();
$b  .= '</td>';

Directly between the last two lines, you add the following:

$b .= '<div id="ez_tag_list">';
$b .= '<h5>Existing Tags</h5>';
$b .= '<div class="defaultSmall">A selection of the top existing tags. Click to add to the entry.</div>';
$b .= '<ul>';

$sub_sql = $DB->query("SELECT tag_id FROM exp_tag_entries WHERE entry_id = '".$DB->escape_str($entry_id)."'");
$ids = '0';
if ($sub_sql->num_rows > 0)
{
  foreach($sub_sql->result as $sub_row)
  {
    $ids .= ',' . $sub_row['tag_id'];
  }
}

$ez_tag_query = $DB->query("SELECT tag_name, total_entries FROM exp_tag_tags WHERE tag_id NOT IN ($ids) ORDER BY total_entries DESC, tag_name ASC LIMIT 50");
if ($ez_tag_query->num_rows > 0)
{
  foreach($ez_tag_query->result as $row)
  {
    $b .= '<li><a href="#">'.$row['tag_name'].'</a> ('.$row['total_entries'].')</li>';
  }
}

$b .= '</ul>';
$b .= '<br style="clear:both;" />';
$b .= $DSP->div_c();

What in the world did we just do? Glad you asked, because we’re going to tell you. You’re here to learn something, not just copy n’paste, remember?

First, we pasted the markup for the tag list after the closing </div> tag of the tag_browse_results div, yet still inside the table cell. The $DSP->div_c(); is one way developers can use ExpressionEngine’s Display Class to close a div (also see Display Class reference).

There are two queries here. The first query grabs the ids of all the tags that already belong to this entry. Those are determined first, because we need to exclude those tags from our list of tags. It doesn’t make sense to have tags that are already assigned to the entry show in the tag list.

The second query says “Grab the top 50 tags and their counts that aren’t already picked for this entry, list by total entry counts high to low, then order by title ascending.” You can alter this to grab the top 100 (by changing the “LIMIT”) or even simply order alphabetically by losing the “total_entries DESC” part of the query. You may be able to take the query even further yet, and check for the top X tags but only from the weblog your current entry is in. That hasn’t been tested, but in looking at the database tables it looks like this should be possible.

The CSS

Next we add the CSS styles. At approximately line 5383, you’ll see the closing </style> tag. We need to add our CSS within this style block. Use the following CSS:

#ez_tag_list {
  padding:0;
  margin: 10px 0 0 0;
  width:100%;
  background:#fff;
  }

#tagListWrap {
  height:220px;
  padding: 3px 2px 10px 5px;
  overflow:auto;
  border: 1px solid #959595;
  }

#ez_tag_list ul,
#ez_tag_list ul li {
  list-style:none;
  margin:0;
  padding:0;
  float:left;
  }

#ez_tag_list ul li {
  display:inline;
  color: #ccc;
  line-height: 1.5em;
  margin: 0 10px 0 0;
  }

#ez_tag_list a {background:#fff;padding: 0px;}
#ez_tag_list a:hover,
#ez_tag_list a:focus,
#ez_tag_list a:active { color: #444; text-decoration:underline;}

We’ll explain how to modify the CSS later for a different look, if desired.

A Dash of jQuery

Finally we need to add just a bit of JS to the jQuery. In your modified file, you should now look for line 6216 (approximately) which is only: jQuery.fn.extend({. Directly before that, add this bit of jQuery:

$('#ez_tag_list a').click(function(event) {
  add_tag(this, event);
});

That ties the “add_tag” function to the links in our new list, so that clicking them sends them to the list on the right. That’s the same function that allows you to hit return and have your tag added from the Tag Browser to the tag list on the right side

That’s all there is to adding items from this list of tags to that cavernous, empty area in the tag module. There may be occasion where you want to either have well more than 50-100 tags there, or you want to maybe spruce up how those links look, because you just love little icons and things of that nature.

Cocoaholic’s Original Version

Elwin Zuiderveld's Tag Module hackCocoaholic’s original Tag module hack.

This is the original version of the hack (which you may prefer) that Elwin supplied to me. This is similar to the asterisk search ability that Solspace added in version 2.5+, the difference being that the list is always there so that your user doesn’t have to type anything to show a selection of tags. This version includes a list that clips at a certain height with each tag entry on a single line, orders them alphabetically, doesn’t show entry counts, includes a tag icon (utilizing the one that is already in the “tag_themes” folder), and doesn’t show any descriptive text above the tag list.

If you want to achieve this, here are the following code changes (you can figure out the line numbers based on what we already covered).

The JavaScript/jQuery

$('#ez_tag_list a').click(function(event) {
    add_tag(this, event);
  });

The CSS

#ez_tag_list {
  padding:0;
  margin:16px 0 0 0;
  width:100%;
  height: 202px !important;
  background:#fff;
  border:1px solid #979AC2;
  overflow:auto;
}

#ez_tag_list ul,
#ez_tag_list ul li {
  list-style:none;
  margin:0;
  padding:0;
}

#ez_tag_list a {
  display:block;
  background:#eee;
  height: 17px;
  padding: 5px 0 0 26px;
  border-bottom: 1px solid #fff;
}

#ez_tag_list a {
  background: #eee url({THEME_FOLDER}tag_themes/default/images/tag.gif) no-repeat 5px 50%;
}

#ez_tag_list a:hover,
#ez_tag_list a:focus,
#ez_tag_list a:active { background:#fff url({THEME_FOLDER}tag_themes/default/images/tag.gif) no-repeat 5px 50%; }

The SQL Queries and Markup

$b .= '<div id="ez_tag_list" style="height:200px;overflow:auto;">';
$b .= '<ul>';

$sub_sql = $DB->query("SELECT tag_id FROM exp_tag_entries WHERE entry_id = '".$DB->escape_str($entry_id)."'");
$ids = '0';
if ($sub_sql->num_rows > 0)
{
  foreach($sub_sql->result as $sub_row)
  {
    $ids .= ',' . $sub_row['tag_id'];
  }
}

$ez_tag_query = $DB->query("SELECT tag_name FROM exp_tag_tags WHERE tag_id NOT IN ($ids) ORDER BY tag_name ASC");
if ($ez_tag_query->num_rows > 0)
{
  foreach($ez_tag_query->result as $row)
  {
    $b .= '<li><a href="#">'.$row['tag_name'].'</a></li>';
  }
}

$b .= '</ul>';
$b .= $DSP->div_c();

One Other Tag List Style

Tag Module hackExactly the same as Cocoaholic’s hack, showing the exact same tags, with different CSS.

This style is similar to the main example in this article, with the addition of tag icons. Notice that you can pack a lot more tags into this area using this style.

The JavaScript/jQuery

This is exactly the same as all previous versions.

The CSS

#ez_tag_list {
  padding:0;
  margin: 10px 0 0 0;
  width:100%;
  background:#fff;
}

#ez_tag_list ul,
#ez_tag_list ul li {
  list-style:none;
  margin:0;
  padding:0;
  float:left;
}

#ez_tag_list ul li {
  display:inline;
  color: #ccc;
  line-height: 22px;
  margin: 0 5px 0 0;
}

#ez_tag_list a {
  background:#eee;
  height: 12px;
  padding: 5px 0px 5px 23px;
}

#ez_tag_list a {
  background: transparent url({THEME_FOLDER}tag_themes/default/images/tag.gif) no-repeat 5px 50%;
}

#ez_tag_list a:hover,
#ez_tag_list a:focus,
#ez_tag_list a:active { background:#fff url({THEME_FOLDER}tag_themes/default/images/tag.gif) no-repeat 5px 50%; color: #444; text-decoration:underline;}

The SQL Queries and Markup

Exactly the same as Elwin’s original markup, above.

What happens when this list gets very long? In Elwin’s version of the markup there is an inline style that sets height of 200 pixels with overflow of “auto”, which will add a scroll bar:

<div id="ez_tag_list" style="height:200px;overflow:auto;">

In the first example in this article the height and overflow control is achieved through the CSS, rather than inline:

#tagListWrap {
  height:220px;
  padding: 3px 2px 10px 5px;
  overflow:auto;
  border: 1px solid #959595;
}

Either way works fine.

Tag Module hack with overflowWhen you have many tags, the set height and overflow:auto kick in so the list doesn’t go down the page.

I hope this article enabled you to easily hack the Tag module to get a list of existing tags, and explained well enough why we did what we did and how it all works. Just remember that if you hack your add-ons, you may have trouble upgrading your add-ons in the future unless your hacks and additions are well documented. Fortunately, this hack to the Tag module is non-destructive. If you upgrade the Tag module and overwrite your hacks, then no list will appear below the Tag Browser. All you need to do is re-apply the CSS, JS, and SQL/markup and you’ll be good to go again – but keep in mind that you may need to add these things at different line numbers if changes have been made to the add-on by the developer.

If you have questions or tweaks of your own, we’d love to hear it in the comments.

7 Comments:

Erwin Heiser (aka e-man) 02.23.09

Awesome article, Ryan, really enjoyed it.

Tristan Bailey 05.01.09

I thought this was quite nice too. could you use it in an article for a tag cloud too?

Michael Swanson 05.05.09

Michael Swanson

Hey Ryan,

Great article.  This really is the missing piece of the tag module.  I think that you have a syntax error in the sql code.  This line is causing an error:

bc..$b .= ‘<div id=“ez_tag_list”>‘;

deleting the “bc..” removes the error and everything seems to be working now.  Thanks again for your work on this!

Ryan Masuga 05.05.09

Ryan Masuga

@Michael: That was actually a Textile error (that’s the blockcode tag) – that is supposed to be 2 separate code areas. I fixed it just now. Thanks for pointing that out.

Michael Swanson 05.05.09

Michael Swanson

@Ryan:  No Problem!  Glad to help.  I forgot to mention that I am using the newest release of the Tag Module 2.6.3.  Seems to be working without a hitch.

Ryan Masuga 05.05.09

Ryan Masuga

Admittedly, this is one of the more tedious hacks to re-apply if you upgrade the Tag module, but on some builds it can be helpful.

Brian Fidler 05.23.09

great, I’ve been wanting this exact functionality!

Tristan, you could create a tag cloud pretty easily by wrapping words with tags that you choose based on the number of times the tag has been used. So for instance you could say any tag used more than 50 times gets wrapped in an h2 tag, any tags used 40-49 times get wrapped in an h3 tag, etc. This will appropriately weight your tags and allow you to choose the visual effect.

You must be registered member to comment. If you're already a member, log in now, and if not go register (it's free and easy!).