// smd_tags by Stef Dawson
// TODO: Tag paging next/prev
// break out of context in tag list (show_all?)
require_plugin('smd_tags_admin');
// ------------------------
// PUBLIC TAGS
// ------------------------
// Check tags of a particular type, or those that have a specific property in context.
//TODO: Think about checking logic, eg, name="business|pleasure" (OR) name="business+pleasure" (AND)
function smd_if_tag ($atts, $thing) {
global $smd_tags, $smd_tag_type, $smd_thistag;
if (!smd_tags_table_exist()) {
trigger_error(smd_tags_gTxt('not_available'));
return;
}
extract(lAtts(array(
'type' => '',
'id' => '',
'name' => '',
'title' => '',
'parent' => '',
'count' => '',
'children' => '',
'level' => '',
'debug' => '0',
),$atts));
$ctxt = smd_tags_context();
$scope = $ctxt['scope'];
$idlist = $ctxt['id'];
$ctype = $ctxt['context'];
// Validate atts
$validTypes = array('article','image','file','link');
$ctxt = smd_tags_context();
$scope = $ctxt['scope'];
$idlist = $ctxt['id'];
$ctype = $ctxt['context'];
$type = (in_array($type, $validTypes)) ? $type : ( ($ctxt['context']) ? $ctxt['context'] : ( ($smd_tag_type) ? $smd_tag_type : $validTypes[0] ) );
$eqtest = array();
$nutest = array();
$opRE = '/(\>|\>\=|\<|\<\=|\!)([0-9a-zA-Z- ]+)/';
$num = preg_match_all($opRE, $id, $parts);
$eqtest['id'] = ($num<=0) ? $id : '';
if ($num>0) $nutest['id'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $name, $parts);
$eqtest['name'] = ($num<=0) ? $name : '';
if ($num>0) $nutest['name'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $type, $parts);
$eqtest['type'] = ($num<=0) ? $type : '';
if ($num>0) $nutest['type'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $title, $parts);
$eqtest['title'] = ($num<=0) ? $title : '';
if ($num>0) $nutest['title'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $parent, $parts);
$eqtest['parent'] = ($num<=0) ? $parent : '';
if ($num>0) $nutest['parent'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $count, $parts);
$eqtest['count'] = ($num<=0) ? $count : '';
if ($num>0) $nutest['count'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $children, $parts);
$eqtest['children'] = ($num<=0) ? $children : '';
if ($num>0) $nutest['children'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $level, $parts);
$eqtest['level'] = ($num<=0) ? $level : '';
if ($num>0) $nutest['level'] = array($parts[1][0] => $parts[2][0]);
if ($debug) {
echo "++ IF_TAG TESTS ++";
dmp($eqtest);
dmp($nutest);
}
// Init
$out = $result = $numTests = 0;
if (empty($smd_tags) && empty($smd_thistag)) {
// not in scope
} else {
// Equality comparisons
foreach ($eqtest as $tname => $tval) {
if ($tval != "") {
$numTests++;
if ($smd_thistag) {
// Local scope
if ($smd_thistag['tag_'.$tname] == $tval) {
$out++;
}
} else {
// Global scope
if ($tname == "type") {
if ($smd_tag_type == $type || $ctype == $type) {
$out++;
}
}
if (isset($smd_tags[$type]['tag_'.$tname]) && in_array($tval, $smd_tags[$type]['tag_'.$tname])) {
$out++;
}
}
}
}
// Numeric comparisons
foreach ($nutest as $tname => $tval) {
$numTests++;
$op = current(array_keys($tval));
$val = current($tval);
if ($smd_thistag) {
$comparison = $smd_thistag['tag_'.$tname];
} else {
$comparison = $smd_tags[$type]['tag_'.$tname];
}
switch ($op) {
case '>':
if (isset($comparison) && $comparison > $val) {
$out++;
}
break;
case '>=':
if (isset($comparison) && $comparison >= $val) {
$out++;
}
break;
case '<':
if (isset($comparison) && $comparison < $val) {
$out++;
}
break;
case '<=':
if (isset($comparison) && $comparison <= $val) {
$out++;
}
break;
case '!':
if (isset($comparison) && $comparison != $val) {
$out++;
}
break;
}
}
// Count how many successes there were
if ($debug) {
echo "++ NUM TESTS & RESULT++";
dmp($numTests);
dmp($out);
}
if ($numTests == $out) {
$result = 1;
}
}
return parse(EvalElse($thing, $result));
}
// ------------------------
function smd_if_tag_list ($atts, $thing) {
global $smd_tag_type;
return parse(EvalElse($thing, (!empty($smd_tag_type))));
}
// ------------------------
// Return name/title of current tag
function smd_tag_name($atts, $thing='') {
global $smd_thistag, $permlink_mode;
if (!smd_tags_table_exist()) {
trigger_error(smd_tags_gTxt('not_available'));
return;
}
extract(lAtts(array(
'title' => 0,
'link' => '',
'section' => '',
'parent' => 0, // not useful to print but good for URLs to nav back up the tree
'parentlabel' => 'Up a level',
'wraptag' => '',
'class' => __FUNCTION__,
),$atts));
$smdpref = smd_tags_pref_get(array('smd_tag_u_sec', 'smd_tag_u_pnam', 'smd_tag_u_ptyp'), 1);
$section = ($section) ? $section : $smdpref['smd_tag_u_sec']['val'];
$urlnam = $smdpref['smd_tag_u_pnam']['val'];
$urltyp = $smdpref['smd_tag_u_ptyp']['val'];
$label = ($parent) ? $parentlabel : (($title) ? $smd_thistag['tag_title'] : $smd_thistag['tag_name']);
$tname = ($parent) ? $smd_thistag['tag_parent'] : $smd_thistag['tag_name'];
$dest = ($permlink_mode == 'messy')
? pagelinkurl(array('s' => $section, $urlnam => $tname, $urltyp => $smd_thistag['tag_type']))
: '/'.$section.'/'.$smd_thistag['tag_type'].'/'.$tname;
if ($thing) {
$out = ''.parse($thing).'';
} elseif ($link) {
$out = ''.$label.'';
} else {
$out = $label;
}
return doTag($out, $wraptag, $class);
}
// ------------------------
// Return # of items associated with this tag
//TODO: think about per-section / per-category counts
function smd_tag_count($atts, $thing='') {
global $smd_thistag;
if (!smd_tags_table_exist()) {
trigger_error(smd_tags_gTxt('not_available'));
return;
}
extract(lAtts(array(
'class' => __FUNCTION__,
'wraptag' => '',
'wrapcount' => ' (:)',
'showempty' => '1',
'paramdelim' => ':',
),$atts));
$wrapcount = explode($paramdelim, $wrapcount); // do_list does a trim: don't want that
if (count($wrapcount) == 1) {
$wrapcount[1] = $wrapcount[0];
}
$out = $smd_thistag['tag_count'];
$out = ($out == 0 && !$showempty) ? '' : $wrapcount[0].$out.$wrapcount[1];
return doTag($out, $wraptag, $class);
}
// ------------------------
// Return other info about the current tag
function smd_tag_info($atts, $thing='') {
global $smd_thistag;
if (!smd_tags_table_exist()) {
trigger_error(smd_tags_gTxt('not_available'));
return;
}
extract(lAtts(array(
'item' => 'name',
'wraptag' => '',
'break' => 'br',
'class' => __FUNCTION__,
'breakclass' => '',
),$atts));
$out = array();
$availableItems = array('id','name','title','type','parent','children','level','count');
$items = do_list($item);
foreach ($items as $whatnot) {
if (in_array($whatnot, $availableItems)) {
$out[] = $smd_thistag['tag_'.$whatnot];
}
}
return doWrap($out, $wraptag, $break, $class, $breakclass);
}
// ------------------------
// Related articles/images/files/links by tag
function smd_related_tags($atts, $thing='') {
global $thisarticle, $thisfile, $thislink, $thisimage, $pretext, $prefs, $smd_tags, $smd_thistag;
if (!smd_tags_table_exist()) {
trigger_error(smd_tags_gTxt('not_available'));
return;
}
extract(lAtts(array(
'type' => '',
'section' => $pretext['s'],
'status' => '4',
'limit' => 99999,
'offset' => 0,
'form' => '',
'match' => 'tag_name',
'match_self' => 0,
'no_widow' => @$prefs['title_no_widow'],
'sort' => '',
'label' => '',
'labeltag' => '',
'wraptag' => '',
'break' => 'br',
'class' => __FUNCTION__,
'delim' => ',',
'paramdelim' => ':',
'debug' => '0',
),$atts));
// Validate atts
$validTypes = array('article','image','file','link');
$ctxt = smd_tags_context();
$scope = $ctxt['scope'];
$idlist = $ctxt['id'];
$ctype = $ctxt['context'];
$type = (in_array($type, $validTypes)) ? $type : (($ctxt['context']) ? $ctxt['context'] : $validTypes[0]);
$sectionClause = ($section) ? " AND txp.Section IN ('".join("','", doSlash(do_list($section)))."')" : '';
$status = do_list($status);
$stati = array();
foreach ($status as $stat) {
if (empty($stat)) {
continue;
} else if (is_numeric($stat)) {
$stati[] = $stat;
} else {
$stati[] = getStatusNum($stat);
}
}
$statSQL = ' AND txp.Status IN ('.join(',', $stati).')';
$out = array();
if (!$sort) {
switch ($type) {
case "article":
$sort = "Posted desc";
break;
case "image":
case "link":
$sort = "date desc";
break;
case "file":
$sort = "created desc";
break;
}
}
// Lookup table for making SQL queries
$sqlStubs = array(
"article" => array(
"select" => "*,unix_timestamp(Posted) as uPosted, unix_timestamp(LastMod) as uLastMod, unix_timestamp(Expires) as uExpires",
"table" => "textpattern",
"gtags" => $thisarticle,
"gid" => "thisid",
),
"image" => array(
"select" => "*",
"table" => "txp_image",
"gtags" => $thisimage,
"gid" => "id",
),
"file" => array(
"select" => "*",
"table" => "txp_file",
"gtags" => $thisfile,
"gid" => "id",
),
"link" => array(
"select" => "*",
"table" => "txp_link",
"gtags" => $thislink,
"gid" => "id",
),
);
// Extract stuff to match & make up query replacement variables
$match = do_list($match, $paramdelim);
$matchType = array_shift($match);
if (count($match) == 0) {
// Assume current type unless a tag is being matched (in which case, use $ctype)
$matchWith = array($matchType);
$matchType = (strpos($matchWith[0], 'tag_') !== false) ? $ctype : $type;
} else {
$matchWith = $match;
}
//dmp($matchType);
$matches = array();
foreach ($matchWith as $matchItem) {
if (strpos($matchItem, 'tag_') !== false) {
$lookin = $smd_tags[$matchType];
} else {
$lookin = $sqlStubs[$matchType]["gtags"];
}
if (isset($lookin[$matchItem])) {
$thismatch = $lookin[$matchItem];
if (is_array($thismatch)) {
foreach ($thismatch as $subID => $subItem) {
if (in_array($subID, $idlist)) {
$matches[] = $subItem;
}
}
} else {
$matches[] = $thismatch;
}
}
}
$matches = array_unique($matches);
if ($debug) {
echo "++ MATCHES ++ ";
dmp($matches);
}
// Convert above opts to SQL query clauses
$excludeClause = ($match_self) ? '' : ' AND txp.id!="'.$sqlStubs[$type]["gtags"][$sqlStubs[$type]["gid"]].'"';
$matchClause = " AND " .(($matches) ? "smt.name IN (" .join(",", doArray($matches, 'doQuote')). ")" : "smt.name = ''");
$orderBy = " ORDER BY " .$sort. " LIMIT " .$offset. "," .$limit;
switch ($type) {
case "article":
$rs = getRows("SELECT ".$sqlStubs[$type]["select"]." FROM ".safe_pfx($sqlStubs[$type]["table"])." AS txp
LEFT JOIN ".SMD_TAGU. " AS tu ON txp.id = tu.item_id
LEFT JOIN ".SMD_TAG. " AS smt ON tu.tag_id = smt.id
WHERE tu.type='article'" . $statSQL . $excludeClause . $sectionClause . $matchClause . $orderBy, $debug);
if ($rs) {
if ($debug > 1) {
echo "++ RECORD SET ++";
dmp($rs);
}
$uniqrs = array();
foreach ($rs as $row) {
if (!in_array($row['ID'], $uniqrs)) {
populateArticleData($row);
$row['Title'] = ($no_widow) ? noWidow(escape_title($row['Title'])) : escape_title($row['Title']);
$out[] = ($form) ? parse_form($form) : (($thing) ? parse($thing) : href($row['Title'], permlinkurl($row)));
$uniqrs[] = $row['ID'];
}
}
}
break;
case "image":
$rs = getRows("
SELECT txp.id, txp.name, txp.category, txp.ext, txp.w, txp.h, txp.alt, txp.caption, txp.date, txp.author, txp.thumbnail, tu.item_id, tu.tag_id, smt.id AS smtid, smt.name AS smtname, smt.type, smt.parent, smt.lft, smt.rgt, smt.title
FROM ".safe_pfx('txp_image')." AS txp
LEFT JOIN ".SMD_TAGU. " AS tu ON txp.id = tu.item_id
LEFT JOIN ".SMD_TAG. " AS smt ON tu.tag_id = smt.id
WHERE tu.type='image'" . $excludeClause . $matchClause . $orderBy, $debug);
if ($rs) {
if ($debug > 1) {
echo "++ RECORD SET ++";
dmp($rs);
}
$uniqrs = array();
foreach ($rs as $row) {
if (!in_array($row['id'], $uniqrs)) {
$thisimage = image_format_info($row);
$out[] = ($form) ? parse_form($form) : (($thing) ? parse($thing) : image(array('id' => $row['id'])));
$thisimage = '';
$uniqrs[] = $row['id'];
}
}
}
break;
case "file":
$rs = getRows("
SELECT txp.id, txp.filename, txp.category, txp.permissions, txp.description, txp.downloads, txp.status, txp.modified, txp.created, txp.size, tu.item_id, tu.tag_id, smt.id AS smtid, smt.name AS smtname, smt.type, smt.parent, smt.lft, smt.rgt, smt.title
FROM ".safe_pfx('txp_file')." AS txp
LEFT JOIN ".SMD_TAGU. " AS tu ON txp.id = tu.item_id
LEFT JOIN ".SMD_TAG. " AS smt ON tu.tag_id = smt.id
WHERE tu.type='file'" . $statSQL . $excludeClause . $matchClause . $orderBy, $debug);
if ($rs) {
if ($debug > 1) {
echo "++ RECORD SET ++";
dmp($rs);
}
$uniqrs = array();
foreach ($rs as $row) {
if (!in_array($row['id'], $uniqrs)) {
$thisfile = file_download_format_info($row);
$out[] = ($form) ? parse_form($form) : (($thing) ? parse($thing) : file_download_link(array('filename' => $row['filename']), $row['filename']));
$thisfile = '';
$uniqrs[] = $row['id'];
}
}
}
break;
case "link":
$rs = getRows("
SELECT txp.id, txp.date, txp.category, txp.url, txp.linkname, txp.linksort, txp.description, tu.item_id, tu.tag_id, smt.id AS smtid, smt.name AS smtname, smt.type, smt.parent, smt.lft, smt.rgt, smt.title
FROM ".safe_pfx('txp_link')." AS txp
LEFT JOIN ".SMD_TAGU. " AS tu ON txp.id = tu.item_id
LEFT JOIN ".SMD_TAG. " AS smt ON tu.tag_id = smt.id
WHERE tu.type='link'" . $excludeClause . $matchClause . $orderBy, $debug);
if ($rs) {
if ($debug > 1) {
echo "++ RECORD SET ++";
dmp($rs);
}
$uniqrs = array();
foreach ($rs as $row) {
if (!in_array($row['id'], $uniqrs)) {
$thislink = array(
'id' => $row['id'],
'linkname' => $row['linkname'],
'url' => $row['url'],
'description' => $row['description'],
'date' => $row['date'],
'category' => $row['category'],
);
$out[] = ($form) ? parse_form($form) : (($thing) ? parse($thing) : href($row['linkname'], $row['url']));
$thislink = '';
$uniqrs[] = $row['id'];
}
}
}
break;
}
if ($out) {
return doLabel($label, $labeltag).doWrap($out, $wraptag, $break, $class);
}
return '';
}
// ------------------------
// List tags from current context, or given type
function smd_tag_list($atts, $thing='') {
global $smd_tags, $smd_thistag;
if (!smd_tags_table_exist()) {
trigger_error(smd_tags_gTxt('not_available'));
return;
}
extract(lAtts(array(
'type' => '',
'id' => '',
'name' => '',
'exclude' => '',
'parent' => '', // Start list from here
'sublevel' => '', // Get tags from this sub-level only
'offset' => 0,
'limit' => 99999,
'form' => '',
'indent' => ' ',
'section_link' => '',
'shuffle' => 0,
'label' => '',
'labeltag' => '',
'wraptag' => 'ul',
'break' => 'li',
'class' => __FUNCTION__,
'active_class' => '', // TODO
'breakclass' => '',
'debug' => 0,
),$atts));
// Validate client side atts
$validTypes = array('article','image','file','link');
$ids = '';
$where = array();
$ctxt = smd_tags_context();
//dmp($ctxt);
$attemptMatch = (empty($name) && empty($id) && empty($exclude) && !empty($ctxt['id'])) ? false : true;
$type = (in_array($type, $validTypes)) ? $type : (($ctxt['context']) ? $ctxt['context'] : $validTypes[0]);
$ids = ($id) ? do_list($id) : (($ctxt['id']) ? $ctxt['id'] : '');
if ($name) {
$name = "name IN (" .join(',', quote_list(do_list($name))). ")";
$where[] = $name;
$ids = ''; // name trumps id; TODO: maybe offer a way of combining them?
}
if ($ids) {
$ids = "id IN (" .join(',', quote_list($ids)). ")";
$where[] = $ids;
}
if ($exclude) {
$exclude = "name NOT IN (" .join(',', quote_list(do_list($exclude))). ")";
$where[] = $exclude;
}
$where = join(' AND ', $where);
if (!$where && !empty($smd_tags)) {
// Use global tags
$rs = $smd_tags[$ctxt];
} else {
$where = ($where) ? $where : '1=1';
if ($where == "1=1" && $attemptMatch) {
$where = 'name = "smd_' .mt_rand(). '"';
}
if ($debug) {
dmp($where);
}
$sublevel = ($parent && !$sublevel) ? 1 : $sublevel;
$rs = getTree((($parent) ? $parent : 'root'), $type, $where, SMD_TAG);
}
if ($debug) {
echo "++ TREE ++";
dmp($rs);
dmp($sublevel);
}
if ($rs) {
$out = array();
$totals = array();
if ($sublevel) {
$outsub = array();
while (list($key, $row) = each($rs)) {
if ($row['level'] == $sublevel) {
$outsub[] = $row;
}
}
$rs = $outsub;
if ($debug) {
echo "++ SUBLEVEL RECORDS ++";
dmp($rs);
}
}
if ($shuffle) {
shuffle($rs);
}
$rs = array_slice($rs, $offset, $limit);
if ($debug) {
echo "++ POST-FILTER RECORDS ++";
dmp($rs);
}
foreach ($rs as $row) {
extract($row);
$row['type'] = $type;
$row['count'] = safe_count(SMD_TAGU, "type='$type' AND tag_id='$id'");
smd_tag_populate($row);
$out[] = ($thing) ? parse($thing) : (($form) ? parse_form($form) :
str_repeat($indent, $level * 1)
.smd_tag_name(array('title' => 1, 'section' => $section_link))
.smd_tag_count(array()));
$smd_thistag = array();
}
if ($out) {
return doLabel($label, $labeltag).doWrap($out, $wraptag, $break, $class, $breakclass);
}
}
return '';
}
// Cloud calculations from Ran Aroussi's txp_tags plugin
//TODO: This is a more specific version of smd_tag_list. Use that engine and this cloud logic (maybe a 'cloud' attrib to smd_tag_list)
function smd_tag_cloud($atts) {
extract(lAtts(array(
'section' => '',
'time' => 'past',
'limit' => 999,
'parent' => '',
'linktosection' => 'article',
'usemessy' => 0,
'cloudwraptag' => 'div',
'break' => ', ',
'wraptag' => 'p',
'label' => '',
'labeltag' => '',
'class' => '',
'breakclass' => '',
'sortdir' => 'asc'
),$atts));
$sections=rssBuildSctSql($section);
$time = rssBuildTimeSql($time);
$prnt = ($parent) ? " WHERE parent = '$parent' " : "";
$rsc = getRows("SELECT distinct c.id, c.name, c.title FROM ".PFX."textpattern_category as tc LEFT JOIN ".PFX."txp_category as c ON tc.category_id = c.id ".$prnt."ORDER BY c.name " .$sortdir);
if ($rsc) {
$rst = getRows("SELECT * FROM ".PFX."textpattern_category");
$all = count($rst);
$allcats="";
foreach($rst as $a) {
$allcats.= $a['category_id']." ";
}
$catarr = explode(" ", substr($allcats, 0, -1));
$catcounts = array_count_values($catarr);
if ($limit != 999) arsort($catcounts);
$max = max($catcounts);
$min = min($catcounts);
$x = 200; $y = 100; // 200%, 100%
$stepvalue = ($max - $min != 0) ? ($max - $min) / ($x - $y) : 1;
// rekey array by number of posts per category
$reorder = array();
foreach ($rsc as $a=>$b) {
$reorder[$b['id']] = $b;
}
$i = 0;
$row = array();
foreach ($catcounts as $a=>$b) {
if ($i < $limit) {
$aq= "SELECT * FROM ".PFX."textpattern as t LEFT JOIN ".PFX."textpattern_category as c ON t.ID = c.article_id WHERE ".$sections." c.category_id = '".$reorder[$a]['id']."' and t.Status = 4 ".$time;
$rsa = getRows($aq);
if ($rsa) {
$num = count($rsa);
$size = (($num / $all) * 100) + 100;
$weight = $y + round(($num-$min) / $stepvalue);
$style = ($weight > $y) ? ' style="font-size:'. $weight . '%;"' : '';
$path = ($usemessy) ? hu."?s=".$linktosection."&c=".strtolower($reorder[$a]['name']) : hu.$linktosection."/".strtolower($reorder[$a]['name']);
$row[]=tag(htmlspecialchars($reorder[$a]['title']),'a',' href="'.$path.'"'.$style.' title="'.htmlspecialchars($reorder[$a]['title']).'"').n;
}
$i++;
}
}
return doTag(doLabel($label, $labeltag).doWrap($row, $wraptag, $break, $class, $breakclass), $cloudwraptag).n;
}
}