The code possibly requires testing - it is included below. Kyle, as the snippet author would you like to review it, and if satisfied, add it to the repository?
<?php
/****************************************************
* Name: TvTagCloud
* Version: 2.1
* Desc: Displays the tags from a documents tvar in a "tag cloud"
* Created by Kyle Jaebker and optimized by Mark Kaplan
* style code added by Garry Nutting
* large parts refactored by Nick Crossland
* Based of code by Marc Hinse, [email protected], www.modxcms.de (original TagCloud creator)
*
* Usage: [!TvTagCloud? &parent=`1` &landing=`22` &tvTags=`docTags` &showCount=`1`!]
*
* Parameters:
* parent: folder that conatins the documents which are to be counted. Can take a comma separated list of parents
* depth: the number of levels down we should search for child documents
* days: the number of days to look back over to find tags. Leave as 0 for all time
* min: minimum number of tag occurances for tag to show, 0 for all
* sort: how should the results be ordered? By default they will be sorted by document ID. Values can be "asc", "desc" or "random"
* landing: id of document to display ditto listings
* tvTags: the template variable that has the tags
* tagDelim: delimiter between tags in tvar
* displayType: [cloud | list] output as a tag cloud or an unordered list
* showCount: if you want the number of occurances to be displayed set to 1
* caseSensitive: if you want tag duplicate removal to be case sensitive set to 1
* steps: the numeric intervals to determine the size of the tag class
* styles: class names that will be applied to each of the size intervals in "steps"
* tooltip: Template for the tooltip that will appear as the title attribute for each tag link. Takes placeholders [+count+] and
* [+tag+] which will be replaced with appropriate values
*
*Landing Page setup:
* the landing page should have a call to ditto (the latest version) with at
* minimum the following tagging parameters set:
*
* [!ditto? &startID=`1` &tagData=`tvdocTags` &tagDelimiter=`,`!]
*
* ***************************************************/
$parent = isset ($parent) ? $parent : "0";
$depth = isset ($depth) ? $depth : 10;
$days = isset ($days) ? $days : 0;
$min = isset ($min) ? $min : "0";
$sort = isset ($sort) ? strtolower($sort) : false; // asc, desc, or random
$landing = isset ($landing) ? $landing : "[~[*id*]~]";
$tvTags = isset ($tvTags) ? $tvTags : 'repo_tags';
$tagDelim = isset ($tagDelim) ? $tagDelim : ',';
$displayType = isset ($displayType) ? $displayType : 'cloud';
$showCount = isset ($showCount) ? $showCount : 0;
$caseSensitive = isset ($caseSensitive) ? $caseSensitive : 0;
$steps = isset($steps) ? $steps : '14,25,34,51,100';
$styles = isset($styles) ? $styles : 's5,s4,s3,s2,s1';
$tooltip = isset($tooltip) ? $tooltip : 'Click for stories about "[+tag+]" ([+count+])';
//-- styles for different sizes in the tag cloud (smallest to largest)
$steps = explode(',', $steps);
$styles = explode(',', $styles);
/* -------------------------------- Functions -------------------------------- */
/* ---------------- getTags ---------------- */
if (!function_exists('getTags')) {
function getTags($cIDs, $tvTags, $days) {
global $modx, $parent;
$docTags = array ();
$baspath= $modx->config["base_path"] . "manager/includes";
include_once $baspath . "/tmplvars.format.inc.php";
include_once $baspath . "/tmplvars.commands.inc.php";
if ($days > 0) {
$pub_date = mktime() - $days*24*60*60;
} else {
$pub_date = 0;
}
$tb1 = $modx->getFullTableName("site_tmplvar_contentvalues");
$tb2 = $modx->getFullTableName("site_tmplvars");
$tb_content = $modx->getFullTableName("site_content");
$query = "SELECT stv.name,stc.tmplvarid,stc.contentid,stv.type,stv.display,stv.display_params,stc.value";
$query .= " FROM ".$tb1." stc LEFT JOIN ".$tb2." stv ON stv.id=stc.tmplvarid ";
$query .= " LEFT JOIN $tb_content tb_content ON stc.contentid=tb_content.id ";
$query .= " WHERE stv.name='".$tvTags."' AND stc.contentid IN (".implode($cIDs,",").") ";
$query .= " AND tb_content.pub_date >= '$pub_date'";
$query .= " ORDER BY stc.contentid ASC;";
$rs = $modx->db->query($query);
$tot = $modx->db->getRecordCount($rs);
$resourceArray = array();
for($i=0;$i<$tot;$i++) {
$row = @$modx->fetchRow($rs);
$docTags[$row['contentid']]['tags'] = getTVDisplayFormat($row['name'], $row['value'], $row['display'], $row['display_params'], $row['type'],$row['contentid']);
}
if ($tot != count($cIDs)) {
$query = "SELECT name,type,display,display_params,default_text";
$query .= " FROM $tb2";
$query .= " WHERE name='".$tvTags."' LIMIT 1";
$rs = $modx->db->query($query);
$row = @$modx->fetchRow($rs);
$defaultOutput = getTVDisplayFormat($row['name'], $row['default_text'], $row['display'], $row['display_params'], $row['type'],$row['contentid']);
foreach ($cIDs as $id) {
if (!isset($docTags[$id]['tags'])) {
$docTags[$id]['tags'] = $defaultOutput;
}
}
}
return $docTags;
}
}
/* -------------------------------- Main section -------------------------------- */
// Get the subentities of the supplied parent
$parents = explode(',',$parent);
$subEntities = $parents;
foreach ($parents as $parentDoc) {
$subDocs = $modx->getChildIds($parentDoc, 3);
$subEntities = array_merge($subDocs,$subEntities);
}
$docTags = getTags(array_unique($subEntities), $tvTags, $days);
// docTags now contains an array indexed by docId, with each value containing an array of tags, e.g.:
// go through each document, split the works into an array, and add them to a
// master array with the word as the key, and a value containing an array consisting of count and style name
$tags = array();
$tag_count = 0;
foreach ($docTags as $n => $v) {
if (is_array($v)){ // We should have an array containing only one key (tags)
$this_tags = explode($tagDelim,$v['tags']); // Split it by the tag delimiter
foreach ($this_tags as $tag) { // Each of the new found tags
$tag = trim($tag); // Remove any whitespace
$tag = ($caseSensitive) ? $tag : strtolower($tag); // If not case sensitive, lower-case it
if ($tag != '') { // if its not empty after all this
$tag_count++;
if (isset($tags[$tag]['count'])) { // if we've already met this tag, increment its counter
$tags[$tag]['count']++;
} else { // Otherwise create a counter for it
$tags[$tag]['count'] = 1;
}
}
}
}
}
// Now we've totalled up how often each word appears, determine what style we should apply
foreach ($tags as $tag => $data) {
$chosen_style = 0; // a default style
$step = (100 / $data['count']); // get percentage
for ($i=0;$i<count($steps);$i++) {
if ($step <= $steps[$i]) {
$tags[$tag]['style'] = $styles[$i];
break;
}
}
// Left in for debugging
/* echo "-------------- $tag \n";
echo "total tag_count: $tag_count \n";
echo "this tag count: ".$data['count']." \n";
echo "step: $step \n";
echo "chosen_style: ".$tags[$tag]['style']." \n";*/
}
// How do we want the words sorted?
switch ($sort) {
case 'asc':
ksort($tags); //sort them alphabetically
break;
case 'desc':
krsort($tags); //sort them reverse alphabetically
break;
case 'random': // Sort them randomly
$keys = array_keys($tags);
shuffle($keys);
$random_words = array();
foreach ($keys as $key) {
$random_words[$key] = $tags[$key];
}
$tags = $random_words;
break;
default:
break;
}
/* ------------- Output ------------- */
if ($displayType == 'cloud') {
$output = '<div class="tagcloud">';
} else {
$output .= '<ul>';
$output .= "\r\n";
}
foreach ($tags as $tag => $value) {
$count = $value['count'];
$this_style = $value['style'];
$tag = trim($tag);
// Prepare the tooltip
$this_tooltip = str_replace('[+count+]',$count,$tooltip);
$this_tooltip = str_replace('[+tag+]',$tag,$this_tooltip);
$this_tooltip = htmlentities($this_tooltip);
if ($showCount) {
$countvalues = '(' . $count . ')';
}
if ($displayType == 'cloud') {
$output .= '<span><a class="' . $this_style . '" href="[~' . $landing . '~]&tags=' . urlencode($tag) . '" title="'.$this_tooltip.'">' . $tag . $countvalues . '</a></span>';
$output .= "\r\n";
} else {
$output .= '<li><a class="' . $this_style . '" href="[~' . $landing . '~]&tags=' . urlencode($tag) . '" title="'.$this_tooltip.'">' . $tag . ' ' . $countvalues . '</a></li>';
$output .= "\r\n";
}
}
if ($displayType == 'cloud') {
$output .= '</div>';
} else {
$output .= '</ul>';
$output .= "\r\n";
}
return $output;
?>