// smd_tags by Stef Dawson
// TODO: Global tags
// TODO: Feed handler prefs to inject tags into RSS+Atom (like tru_tags: code at bottom of this plugin)
// TODO: Add option to display tag lists AND TextArea(+) on list pages
// TODO: See if rapid tagging can be made more intuitive wrt Create/Save to avoid clobbering already created tags (from pieman)
// TODO: When refreshing the Manage tab, FF remembers the Title and tries to add it on page load, throwing a msgarea warning about the tag already exists (or that name is empty)
// TODO: On list pages:
// * AJAX fetch of the tag set for TextArea and TextArea+ modes to improve page load time (plus you then only grab the ones in the current category hierarchy)
// * Option for bi-directional lookup of tags from the current category, i.e. grandparents and children (from aswihart)
// * Resolve what happens if there are no categories defined (from jpdupont)
// * Option to display/hide the top-level (group) tag in lists (from pieman)
// * Add autocomplete options to smd_tags prefs so autofill etc can be configured (from aswihart)
// * Check what happens when deleting content (e.g. images): it sometimes triggers " '' is not an Integer " assertion (from curiouz)
// * Perhaps offer sort option for List mode to a) retain hierarchy, b) strict alphabetical, c) group them somehow to show how sets of tags are related (from pieman)
// TODO: Possibility of section tags? (from jakob)
// ------------------------
// ADMIN SIDE CODE
if (@txpinterface == 'admin') {
add_privs('smd_tags','1,2');
add_privs('smd_tags_users','1,2,3,4,5,6');
add_privs('plugin_prefs.smd_tags','1,2');
// Extensions tab
register_tab('extensions', 'smd_tags', smd_tags_gTxt('smd_tags'));
register_callback('smd_tags', 'smd_tags');
register_callback('smd_tags_users', 'smd_tags_users');
// Note the callbacks are ordered with 'save' first
register_callback('smd_tags_multi_edit', 'list', 'list_multi_edit');
register_callback('smd_tags_savelist', 'article', 'create');
register_callback('smd_tags_savelist', 'article', 'edit');
register_callback('smd_tags_loadlist', 'article');
register_callback('smd_tags_multi_edit', 'image', 'image_delete');
register_callback('smd_tags_savelist', 'image', 'image_save');
register_callback('smd_tags_loadlist', 'image');
register_callback('smd_tags_multi_edit', 'file', 'file_delete');
register_callback('smd_tags_savelist', 'file', 'file_save');
register_callback('smd_tags_loadlist', 'file');
register_callback('smd_tags_multi_edit', 'link', 'link_multi_edit');
register_callback('smd_tags_savelist', 'link', 'link_post');
register_callback('smd_tags_savelist', 'link', 'link_save');
register_callback('smd_tags_loadlist', 'link');
register_callback('smd_tags_setup', 'plugin_prefs.smd_tags');
register_callback('smd_tags_welcome', 'plugin_lifecycle.smd_tags');
global $smd_tag_prefs, $smd_tags_styles;
// Triple array, output in alphabetical order, sequentially numbered
// 0: list of current prefs + default
// 1: list of legacy prefs that may need removing (default should all be empty)
// 2: position that a heading should be inserted + heading itself
$smd_tag_prefs = array(
array(
'smd_tag_p_enable' => '1111', // one digit for article/image/file/link. 1=enabled; 0=disabled
'smd_tag_p_input' => '0', // 0=select, 1=links, 2=textarea, 3=textarea+
'smd_tag_p_linkcat' => '0',
'smd_tag_p_qtag' => '0', // 0=no autocomplete, 1=std a/c, 2=strict a/c
'smd_tag_p_qtpath' => '', // Dir to js file(s) relative to textpattern dir
'smd_tag_p_qtstyl' => '', // Dir to css file(s)
'smd_tag_p_size' => '6',
'smd_tag_t_astart' => '0',
'smd_tag_t_auto' => '1',
'smd_tag_t_cols' => '1',
'smd_tag_t_colsord' => '0',
'smd_tag_t_count' => '1',
'smd_tag_t_deltree' => '0',
'smd_tag_t_delused' => '0',
'smd_tag_t_enrep' => '1',
'smd_tag_t_hilite' => '80551',
'smd_tag_t_hover' => 'aaa',
'smd_tag_t_indent' => '—',
'smd_tag_t_mdelim' => ',',
'smd_tag_u_pnam' => 'smd_tag',
'smd_tag_u_ptyp' => 'smd_tagtype',
'smd_tag_u_sec' => 'smd_tags',
),
array(
'smd_tag_p_first' => '1',
),
array(
1 => smd_tags_gTxt('prefs_p'),
8 => smd_tags_gTxt('prefs_t'),
20 => smd_tags_gTxt('prefs_u'),
)
);
// CSS definitions: hopefully kind to themers
$smd_tags_styles = array(
'tag_manager' =>
'.smd_hidden { display:none; }
.smd_hover { cursor:pointer; background:#{hov}; }
.smd_current { font-weight:bold; background:#{cur}; }
.smd_tags_showpars, .smd_tags_linkcat { width:12em; }
#smd_tag_filt { font-size:90%; float:left; margin:-35px 0 12px 10px; padding:7px 3px; }
#smd_tag_multisel { margin:10px 0 0; }',
'list_pages' =>
'.smd_hidden { display:none; }
.smd_fakebtn, #smd_tags_bylink span { cursor:pointer; }
.smd_tagip { margin:.4em 0 .8em; max-width:400px; }
.smd_sel { font-weight:bold; }',
'report' =>
'#smd_tag_report_pane { display:none; position:absolute; left:200px; max-width:500px; border:3px ridge #999; opacity:.92; filter:alpha(opacity:92); padding:15px 20px; background-color:#e2dfce; color:#80551e; }
#smd_tag_report_pane .publish { float:right; }',
);
}
if (!defined('SMD_TAG')) {
define("SMD_TAG", 'smd_tags');
}
if (!defined('SMD_TAGC')) {
define("SMD_TAGC", 'smd_tags_cat');
}
if (!defined('SMD_TAGU')) {
define("SMD_TAGU", 'smd_tags_used');
}
register_callback('smd_tags_url_handler', 'pretext');
// -------------------------------------------------------------
function smd_tags_welcome($event, $step) {
$msg = '';
switch ($step) {
case 'installed':
smd_tags_table_install('', 0);
$msg = 'Thanks for installing smd_tags. Happy tagging!';
break;
case 'deleted':
smd_tags_prefs_remove('', 0);
break;
}
return $msg;
}
// ------------------------
function smd_tags($event, $step) {
global $smd_tag_prefs;
if ($step == 'save_pane_state') {
smd_tags_save_pane_state();
} else {
if(!$step or !in_array($step, array(
'smd_tag_create',
'smd_tags_delete',
'smd_tag_save',
'smd_tag_catlist',
'smd_tag_parentlist',
'smd_tags_manage',
'smd_tags_sync',
'smd_tags_import_one',
'smd_tags_multi_set_parent',
'smd_tags_multi_catlink',
'smd_tags_import',
'smd_tags_table_install',
'smd_tags_table_remove',
'smd_tags_table_rebuild',
'smd_tags_prefs_show',
'smd_tags_prefs_install',
'smd_tags_prefs_remove',
'smd_tags_prefs_update',
))) {
$pref = smd_tags_pref_get('smd_tag_t_astart');
if ($pref) {
switch ($pref['smd_tag_t_astart']['val']) {
case 1:
smd_tags_manage('');
break;
case 0:
default:
smd_tags_prefs_show('');
}
} else {
smd_tags_prefs_show('');
}
} else $step();
}
}
// ------------------------
function smd_tags_users($event, $step) {
if(!$step or !in_array($step, array(
'smd_tag_catlist',
'smd_tag_parentlist',
))) {
// Do nothing
} else $step();
}
// ------------------------
// Create dropdown and populate it with tags of the relevant type, highlighting any $sel tags
function smd_multiTreeSelectInput($select_name = '', $tree = '', $sel = '') {
$pref = smd_tags_pref_get('smd_tag_p_size', 1);
$rows = $pref['smd_tag_p_size']['val'];
$out[] = '';
return join('',$out);
}
// ------------------------
// Called whenever one of the 4 main tabs are used on the admin side. Inserts the input method if required
function smd_tags_loadlist($event, $step) {
global $smd_tags_styles;
if (smd_tags_table_exist()) {
$ctrls = smd_tags_pref_get(array('smd_tag_p_enable', 'smd_tag_p_input', 'smd_tag_p_size', 'smd_tag_p_qtag', 'smd_tag_p_qtpath', 'smd_tag_p_qtstyl', 'smd_tag_p_linkcat'), 1);
$onoff = smd_tags_pref_explode($ctrls['smd_tag_p_enable']['val']);
$iptyp = $ctrls['smd_tag_p_input']['val'];
$selsz = $ctrls['smd_tag_p_size']['val'];
$quick = $ctrls['smd_tag_p_qtag']['val'];
$jsdir = $ctrls['smd_tag_p_qtpath']['val'];
$csdir = $ctrls['smd_tag_p_qtstyl']['val'];
$clink = $ctrls['smd_tag_p_linkcat']['val'];
$jstail = ($jsdir) ? '/' : '';
$cstail = ($csdir) ? '/' : '';
$addIt = false;
$itemID = smd_getID();
$edtBtn = has_privs('smd_tags') ? ' ['.eLink('smd_tags', 'smd_tags_manage', 'smd_tag_type', $event, gTxt('edit')).'] ' : '';
$tagtop = '
'.smd_tags_gTxt('title').$edtBtn.'
';
$selist = '';
$selhid = '';
switch ($iptyp) {
case 0:
$ipmeth = $selist;
break;
case 1:
$ipmeth = ''.$selhid;
break;
case 2:
case 3:
$ipmeth = ''.$selhid;
break;
}
switch ($event) {
case "article":
$addIt = ($onoff[gTxt('tab_list')] == 1) ? true : false;
if ($addIt) {
extract(gpsa(array('view','from_view')));
$view = ($view) ? $view : "text";
$grabcats = 'jQuery("#category-1 option:selected, #category-2 option:selected")';
$jsElem = 'jQuery("#category-2")';
$trigger = 'jQuery("#category-1, #category-2")';
if ($view != 'text') {
$tagtop = $ipmeth = '';
}
}
break;
case "image":
if ($step == "image_edit") {
$addIt = ($onoff[gTxt('tab_image')] == 1) ? true : false;
if ($addIt) {
$grabcats = 'jQuery("#image-category option:selected")';
$jsElem = $trigger = "jQuery('#image-category')";
}
}
break;
case "file":
if ($step == "file_edit") {
$addIt = ($onoff[gTxt('tab_file')] == 1) ? true : false;
if ($addIt) {
$grabcats = 'jQuery("select[name=\'category\'] option:selected")';
$jsElem = $trigger = 'jQuery("select[name=\'category\']")';
}
}
break;
case "link":
$addIt = ($onoff[gTxt('tab_link')] == 1) ? true : false;
if ($addIt) {
$tagtop = '';
$ipmeth = tr(fLabelCell(smd_tags_gTxt('title'), '', 'smd_tags') . tda($edtBtn.br.$ipmeth));
$grabcats = 'jQuery("#link-category option:selected")';
$jsElem = $trigger = "jQuery('#link-category').parent()";
}
break;
}
// Add the dropdown and js toggle/clear options if required
if ($addIt) {
$qs = array(
"event" => "smd_tags_users",
);
$qsVars = "index.php".join_qs($qs);
if ($quick > 0) {
echo <<
EOJS;
}
// Inject styles
echo '';
echo <<
jQuery(function() {
{$jsElem}.parent().after('{$tagtop}{$ipmeth}');
function smd_tagList(typ) {
var grabcats = [];
{$grabcats}.each(function() {
if (jQuery(this).val() != '') {
grabcats.push(jQuery(this).val());
}
});
grabcats = grabcats.join(",");
var smd_clr = (('{$clink}'=='0') ? '2' : ((grabcats=='') ? 1 : ''));
jQuery.post('{$qsVars}', { step: "smd_tag_parentlist", name: 'smd_tag_parent', type: typ, cat: grabcats, clr: smd_clr, itemid: "{$itemID}" },
function(data) {
jQuery("#smd_tags").html(data);
jQuery("#smd_tags_bylink").empty();
var smd_tagpool = [];
var smd_tagsel = [];
var smd_taglinks = [];
jQuery('#smd_tags select option').each(function() {
var curr = jQuery(this);
var txt = jQuery.trim(curr.text());
smd_tagpool.push(txt);
if (jQuery("#smd_tags_bylink").text() != "") {
jQuery("#smd_tags_bylink").append(" · ");
}
if (curr.attr("selected") == true) {
smd_tagsel.push(txt);
jQuery("#smd_tags_bylink").append(''+txt+'');
} else {
jQuery("#smd_tags_bylink").append(''+txt+'');
}
});
jQuery("#smd_tags_bylink span").click(function() {
jQuery(this).toggleClass("smd_sel");
var ltxt = jQuery(this).text();
jQuery("#smd_tags select option").each(function() {
var curr = jQuery(this);
if (jQuery.trim(curr.text()) == ltxt) {
this.selected = !this.selected;
}
});
});
jQuery("textarea[name='smd_tags_bytext']").val(smd_tagsel.join(", "));
if ({$quick} > 0) {
jQuery("textarea[name='smd_tags_bytext']").autocomplete(smd_tagpool, {
multiple: true,
mustMatch: (($quick==2)?true:false),
autoFill: true
});
}
jQuery("textarea[name='smd_tags_bytext']").keyup(function() {
smd_tagsResyncSelect({$quick});
});
jQuery('#smd_clr').click(function() {
jQuery('#smd_tags select option').attr("selected", "");
});
jQuery('#smd_tog').click(function() {
jQuery('#smd_tags select option').each(function() {
this.selected = !this.selected;
});
});
}
);
}
smd_tagList('{$event}');
if ('{$clink}'=='1') {
{$trigger}.change(function() {
smd_tagList('{$event}');
});
}
function smd_tagsResyncSelect(lvl) {
var tbTags = jQuery("textarea[name='smd_tags_bytext']").val().split(/,\s+?/);
jQuery("#smd_tags select option").each(function() {
var curr = jQuery(this);
if(jQuery.inArray(jQuery.trim(curr.text()), tbTags) == -1) {
curr.attr("selected", "");
} else {
curr.attr("selected", true);
}
});
}
});
EOJS;
}
}
}
// ------------------------
// Update the tags used table if some tags have been added/saved
function smd_tags_savelist($event, $step) {
if (smd_tags_table_exist()) {
$ctrls = smd_tags_pref_get(array('smd_tag_p_qtag', 'smd_tag_p_input', 'smd_tag_p_linkcat'), 1);
$quick = $ctrls['smd_tag_p_qtag']['val'];
$iptyp = $ctrls['smd_tag_p_input']['val'];
$clink = $ctrls['smd_tag_p_linkcat']['val'];
$itemID = smd_getID();
if ($itemID) {
$saveSteps = array('image_save','file_save','link_save','link_post');
if (ps('save') || in_array($step, $saveSteps))
safe_delete(SMD_TAGU, 'item_id="'.$itemID.'" AND type="'.$event.'"');
$tags = ps('smd_tag_parent') ? ps('smd_tag_parent') : array();
// Validate the incoming tags to those in the currently selected category|ies
if ($clink) {
$usedCats = ($event=="article") ? array(gps('Category1'), gps('Category2')) : array(gps('category'));
$catids = safe_column('id', 'txp_category', "name IN (" .join(',', quote_list($usedCats)). ") AND type='$event'");
$validTags = safe_column('tag_id', SMD_TAGC, "cat_id IN (" .join(',', quote_list($catids)). ")");
$allowedTags = array();
foreach ($validTags as $validTag) {
$rows = getTree(safe_field('name',SMD_TAG,"id='".doSlash($validTag)."'"), $event, '1=1', SMD_TAG);
foreach ($rows as $row) {
$allowedTags[] = $row['id'];
}
}
$tags = array_intersect($allowedTags, $tags);
}
$vals = array();
foreach($tags as $val){
safe_insert(SMD_TAGU, 'tag_id = "'.$val.'", item_id="'.$itemID.'", type="'.$event.'"');
$vals[] = $val;
}
$tagsText = preg_split("/,\s+?/", ps('smd_tags_bytext'));
// If we're in non-STRICT mode and there are any typed tags that are not in the select list, check they exist and make links to them
if ($quick < 2) {
$added = ($vals) ? safe_column("title", SMD_TAG, "id IN ('".join("','",$vals)."') AND type='$event'") : array();
$tagsText = array_diff($tagsText, $added);
// Only loop over tags that don't appear to be already defined
foreach($tagsText as $tag) {
if ($tag == "") continue;
$exists = safe_field('id', SMD_TAG, "name = '$tag' AND type = '$event'"); // 1st check
// Textarea+ input system can add (linked) tags with impunity
if ($iptyp == 3 && !$exists) {
$tagBits = explode('-->', $tag);
if (count($tagBits) == 2) {
$tagparent = strtolower(sanitizeForUrl($tagBits[0]));
$tagparent = safe_field('name', SMD_TAG, "name = '$tagparent' AND type = '$event'"); // Check parent exists for this type
$tagparent = ($tagparent === false) ? 'root' : $tagparent;
$tagchild = $tagBits[1];
} else {
$tagparent = 'root';
$tagchild = $tagBits[0];
}
$sanitag = strtolower(sanitizeForUrl($tagchild));
$already = safe_field('id', SMD_TAG, "name = '$sanitag' AND type = '$event'"); // Check again because parent-->child will fail 1st check
if ($already === false) {
$exists = safe_insert(SMD_TAG, "name='".$sanitag."', title='$tagchild', parent='$tagparent', type='$event'");
}
$catfield = ($event=="article") ? gps('Category1') : gps('category');
if ($clink && $catfield && $exists) {
// Assign new tag to current category (cat1 for articles)
$catid = safe_insert(SMD_TAGC,"tag_id='$exists', cat_id=(SELECT id FROM ".PFX."txp_category WHERE name='$catfield' AND type='$event')");
}
rebuild_tree_full($event, SMD_TAG);
}
if ($exists) {
safe_insert(SMD_TAGU, 'tag_id = "'.$exists.'", item_id="'.$itemID.'", type="'.$event.'"');
}
}
}
}
}
}
// ------------------------
// Handle changes to the tags table during multi-item edits
function smd_tags_multi_edit($event, $step) {
// In TXP 4.0.6 the only tabs to allow multi-edits are the Article (list) and Link tabs. Cheat for now
if (smd_tags_table_exist()) {
switch ($event) {
case "list":
case "link":
$method = ps('edit_method');
$selected = ps('selected');
break;
default:
$method = "delete";
$selected = array(ps('id'));
break;
}
// 'list' is a special case that equates to articles
$type = ($event == "list") ? "article" : $event;
if ($selected) {
switch ($method) {
case "delete":
$ids = array();
foreach ($selected as $id) {
$id = assert_int($id);
if (safe_delete(SMD_TAGU, 'item_id = '.$id.' AND type="'.$type.'"')) {
$ids[] = $id;
}
}
return join(', ', $ids);
break;
}
}
return '';
}
}
// ------------------------
// Grab the current article/image/file/link ID
function smd_getID() {
if(!empty($GLOBALS['ID'])) { // newly-saved item
$itemID = intval($GLOBALS['ID']);
} else {
$itemID = (gps('ID')) ? gps('ID') : gps('id');
}
return $itemID;
}
// ------------------------
function smd_tags_table_exist($all='') {
if ($all) {
$tbls = array(SMD_TAG => 7, SMD_TAGC => 2, SMD_TAGU => 3);
$out = count($tbls);
foreach ($tbls as $tbl => $cols) {
if (gps('debug')) {
echo "++ TABLE ".$tbl." HAS ".count(@safe_show('columns', $tbl))." COLUMNS; REQUIRES ".$cols." ++".br;
}
if (count(@safe_show('columns', $tbl)) == $cols) {
$out--;
}
}
return ($out===0) ? 1 : 0;
} else {
if (gps('debug')) {
echo "++ TABLE ".SMD_TAG." HAS ".count(@safe_show('columns', SMD_TAG))." COLUMNS;";
}
return(@safe_show('columns', SMD_TAG));
}
}
// ------------------------
// Add tag tables if not already installed
function smd_tags_table_install($showpane='1') {
global $DB;
$version = mysql_get_server_info();
$GLOBALS['txp_err_count'] = 0;
if ($version < "4.1.2") {
$GLOBALS['txp_err_count']++;
trigger_error("smd_tags requires MySQL v4.1.2 or greater.");
} else {
$ret = '';
$sql = array();
$sql[] = "CREATE TABLE IF NOT EXISTS `".PFX.SMD_TAG."` (
`id` int(6) NOT NULL auto_increment,
`name` varchar(64) NOT NULL default '' COLLATE utf8_general_ci,
`type` varchar(64) NOT NULL default '' COLLATE utf8_general_ci,
`parent` varchar(64) NOT NULL default '' COLLATE utf8_general_ci,
`lft` int(6) NOT NULL default '0',
`rgt` int(6) NOT NULL default '0',
`title` varchar(255) NOT NULL default '' COLLATE utf8_general_ci,
PRIMARY KEY (`id`)
) ENGINE=MyISAM PACK_KEYS=1 AUTO_INCREMENT=5 ";
if (!smd_tags_table_exist()) {
$sql[] = "INSERT INTO `".PFX.SMD_TAG."` VALUES (1, 'root', 'article', '', 1, 2, 'root')";
$sql[] = "INSERT INTO `".PFX.SMD_TAG."` VALUES (2, 'root', 'link', '', 1, 2, 'root')";
$sql[] = "INSERT INTO `".PFX.SMD_TAG."` VALUES (3, 'root', 'image', '', 1, 2, 'root')";
$sql[] = "INSERT INTO `".PFX.SMD_TAG."` VALUES (4, 'root', 'file', '', 1, 2, 'root')";
}
$sql[] = "CREATE TABLE IF NOT EXISTS `".PFX.SMD_TAGC."` (
`tag_id` int(6) NOT NULL default '0',
`cat_id` int(6) NOT NULL default '0',
PRIMARY KEY (`tag_id`,`cat_id`)
) ENGINE=MyISAM; ";
$sql[] = "CREATE TABLE IF NOT EXISTS `".PFX.SMD_TAGU."` (
`item_id` int(11) NOT NULL default '0',
`tag_id` int(6) NOT NULL default '0',
`type` varchar(64) NOT NULL default '' COLLATE utf8_general_ci,
PRIMARY KEY (`item_id`,`tag_id`)
) ENGINE=MyISAM; ";
if(gps('debug')) {
dmp($sql);
}
foreach ($sql as $qry) {
$ret = safe_query($qry);
if ($ret===false) {
$GLOBALS['txp_err_count']++;
echo "".$GLOBALS['txp_err_count'].". ".mysql_error()."
\n";
echo "\n";
}
}
// Upgrade table collation if necessary
$ret = safe_field("COLLATION_NAME", "INFORMATION_SCHEMA.COLUMNS", "table_name = '".PFX.SMD_TAG."' AND table_schema = '" . $DB->db . "' AND column_name = 'name'");
if ($ret != 'utf8_general_ci') {
$ret = safe_alter(SMD_TAG, 'CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci');
$ret = safe_alter(SMD_TAGU, 'CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci');
}
}
if ($GLOBALS['txp_err_count'] == 0) {
$message = smd_tags_gTxt('tbl_installed');
smd_tags_prefs_install($message, $showpane);
} else {
if ($showpane) {
$message = smd_tags_gTxt('tbl_not_installed');
smd_tags_prefs_show($message);
}
}
}
// ------------------------
// Drop tag tables if in database
function smd_tags_table_remove() {
$ret = '';
$sql = array();
$GLOBALS['txp_err_count'] = 0;
if (smd_tags_table_exist()) {
$sql[] = "DROP TABLE IF EXISTS " .PFX.SMD_TAG. "; ";
$sql[] = "DROP TABLE IF EXISTS " .PFX.SMD_TAGU. "; ";
$sql[] = "DROP TABLE IF EXISTS " .PFX.SMD_TAGC. "; ";
if(gps('debug')) {
dmp($sql);
}
foreach ($sql as $qry) {
$ret = safe_query($qry);
if ($ret===false) {
$GLOBALS['txp_err_count']++;
echo "".$GLOBALS['txp_err_count'].". ".mysql_error()."
\n";
echo "\n";
}
}
}
if ($GLOBALS['txp_err_count'] == 0) {
$message = smd_tags_gTxt('tbl_removed');
smd_tags_prefs_remove($message);
} else {
$message = smd_tags_gTxt('tbl_not_removed');
smd_tags_prefs_show($message);
}
}
// ------------------------
// Rebuild modified preorder tag links
function smd_tags_table_rebuild() {
$types = array('article', 'image', 'file', 'link');
foreach ($types as $tag_type) {
rebuild_tree_full($tag_type, SMD_TAG);
}
$message = smd_tags_gTxt('tbl_rebuilt');
smd_tags_prefs_show($message);
}
// ------------------------
// Add plugin preferences to prefs
function smd_tags_prefs_install($message='', $showpane='1') {
global $smd_tag_prefs;
$ctr = safe_count('txp_prefs', 'event="smd_tags"') + 1;
foreach ($smd_tag_prefs[0] as $pref => $dflt) {
if (!fetch('name','txp_prefs','name',$pref)) {
$id = safe_insert('txp_prefs','prefs_id=1, name='.doQuote(doSlash($pref)).', val='.doQuote(doSlash($dflt)).', event="smd_tags", position="'.$ctr.'"');
$ctr++;
}
}
// Tidy up any legacy prefs
foreach ($smd_tag_prefs[1] as $pref => $dflt) {
if (fetch('name','txp_prefs','name',$pref)) {
$id = safe_delete('txp_prefs','name='.doQuote(doSlash($pref)));
}
}
if ($showpane) {
$message .= smd_tags_gTxt('prefs_installed');
smd_tags_prefs_show($message);
}
}
// ------------------------
// Remove plugin preferences from prefs table.
// Note: both parts of smd_tags_prefs array are iterated over to tidy up any legacy settings
function smd_tags_prefs_remove($message='', $showpane='1') {
global $smd_tag_prefs;
foreach (array_merge($smd_tag_prefs[0],$smd_tag_prefs[1]) as $pref => $dflt) {
if (fetch('name','txp_prefs','name',$pref)) {
$id = safe_delete('txp_prefs','name='.doQuote(doSlash($pref)));
}
}
if ($showpane) {
$message .= smd_tags_gTxt('prefs_removed');
smd_tags_prefs_show($message);
}
}
// ------------------------
// Saves plugin preferences
function smd_tags_prefs_update($message = '') {
global $smd_tag_prefs;
$post = doSlash(stripPost());
foreach ($smd_tag_prefs[0] as $pref => $dflt) {
if (isset($post[$pref])) {
safe_update("txp_prefs", "val = '".$post[$pref]."'", "name = '".doSlash($pref)."' and prefs_id = 1");
}
}
$message .= gTxt('preferences_saved');
smd_tags_prefs_show($message);
}
// ------------------------
// Split a multi-char preference value into an array of keys/values.
// Note: value is passed in and not read directly from the prefs array in this function - intentionally
function smd_tags_pref_explode($val) {
$order = array_values(array(gTxt('tab_list'),gTxt('tab_image'),gTxt('tab_file'),gTxt('tab_link')));
$onoff = array_values(preg_split('//', $val, -1, PREG_SPLIT_NO_EMPTY));
$out = array();
foreach($order as $key1 => $value1) {
$out[(string)$value1] = $onoff[$key1];
}
return $out;
}
// ------------------------
// Get values from prefs - pass either a single key or an array.
// If 2nd arg is set and the pref doesn't exist, read it from the plugin default
function smd_tags_pref_get($keys, $dflt=0) {
global $smd_tag_prefs;
if (!is_array($keys)) {
$keys = array($keys);
}
$prefkeys = doQuote(implode("','",doSlash($keys)));
$rs = safe_rows('name,val,html','txp_prefs','name IN ('.$prefkeys.') ORDER BY name');
$prefout = array();
foreach($rs as $pref) {
$prefout[$pref['name']] = array_slice($pref,1);
}
if ($dflt) {
foreach ($keys as $pref) {
if (!isset($prefout[$pref])) {
$prefout[$pref] = $smd_tag_prefs[0][$pref];
}
}
}
return $prefout;
}
// ------------------------
// Common buttons on the admin panel
function smd_tags_prefs_buttons() {
$ret = array (
'btnPrefsSave' => fInput('submit', 'submit', gTxt('save'), 'publish'),
'btnInstallTbl' => '',
'btnRemoveTbl' => '',
'btnRebuildTbl' => '',
'btnInstall' => '',
'btnRemove' => '',
'btnPrefs' => '',
'btnManage' => '',
'btnSync' => '',
'btnSyncGo' => fInput('submit', 'smd_tags_do_import', gTxt('go'), 'publish'),
'btnHelp' => '('. gTxt('help') .')',
'btnCreate' => fInput('submit', 'smd_tag_create', gTxt('create'), 'smallerbox', '', 'smd_tags_step=this.name;smd_presub()'),
'btnSave' => fInput('submit', 'smd_tag_save', gTxt('save'), 'smallerbox', '', 'smd_tags_step=this.name;smd_presub()'),
'btnDelete' => fInput('submit', 'smd_tags_delete', '×', 'smallerbox', '', 'smd_tags_step=this.name;smd_presub()'),
'btnStyle' => ' style="border:0;height:25px"',
);
return $ret;
}
// ------------------------
// A stub that can be called without a $message
function smd_tags_setup($event='', $step='', $message='') {
smd_tags_prefs_show();
}
// ------------------------
// Display the prefs panel
function smd_tags_prefs_show($message='') {
global $smd_tag_prefs;
pagetop(smd_tags_gTxt('prefs_title'),$message);
extract(smd_tags_prefs_buttons());
// Prefs check
$prefset = smd_tags_pref_get(array_keys($smd_tag_prefs[0]));
$numReqPrefs = count($smd_tag_prefs[0]);
$numPrefs = count($prefset);
$btnRebuild = tda($btnRebuildTbl, $btnStyle);
echo <<
var smd_tags_specialmode = 0;
// Concatenate checkbox options for storage
function smd_presub() {
var smd_out = "";
jQuery(":checkbox").each(function() {
smd_out += (this.checked) ? 1 : 0;
});
jQuery("#smd_tag_p_enable").val(smd_out);
return true;
}
// Once page fully loaded, check
function smd_tags_keycheckend() {
jQuery(window).unbind('keydown.smd_tags_specialmode');
if (smd_tags_specialmode == 1) {
jQuery('.smd_tags_buttonrow').append('{$btnRebuild}');
}
}
jQuery(function() {
// Check if shift key pressed during page load to unlock advanced options
jQuery(window).bind('load', smd_tags_keycheckend);
jQuery(window).bind('keydown.smd_tags_specialmode', function(ev) {
if(ev.shiftKey) {
smd_tags_specialmode = 1;
// Shut off the handler so we don't flood the event system with key repeats
jQuery(window).unbind('keydown.smd_tags_specialmode');
}
});
});
EOJS;
echo startTable('list');
if (smd_tags_table_exist(1)) {
// Tables installed
if ($numPrefs == $numReqPrefs) {
// Prefs all installed
echo tr(tda(strong(smd_tags_gTxt('prefs_title')).' '.$btnHelp, ' colspan="5"'));
echo tr(
fLabelCell(smd_tags_gTxt('control').':')
. tda($btnRemove, $btnStyle)
. tda($btnRemoveTbl, $btnStyle)
. tda($btnSync, $btnStyle)
. tda($btnManage, $btnStyle)
, ' class="smd_tags_buttonrow"');
echo '';
} else if ($numPrefs > 0 && $numPrefs < $numReqPrefs) {
// Prefs possibly corrupt, or plugin updated
echo tr(tda(strong(smd_tags_gTxt('prefs_some')).br.br
.smd_tags_gTxt('prefs_some_explain').br.br
.smd_tags_gTxt('prefs_some_opts1'), ' colspan="2"')
);
echo tr(
tda($btnRemove,$btnStyle)
. tda($btnInstall, $btnStyle)
);
} else {
// Prefs not installed
echo tr(tda(smd_tags_gTxt('prefs_not_installed'), ' colspan="2"'));
echo tr(tda($btnInstall, $btnStyle));
}
} else {
// Tables not installed
echo tr(tda(strong(smd_tags_gTxt('prefs_some_tbl')).br.br
.smd_tags_gTxt('prefs_some_explain').br.br
.smd_tags_gTxt('prefs_some_opts2'), ' colspan="2"')
);
echo tr(tda($btnInstallTbl, $btnStyle));
}
echo endTable();
}
// ------------------------
// Manage tags in a similar way to the categories tab
function smd_tags_manage($message='', $report='') {
global $smd_tag_prefs, $smd_tags_styles;
extract(doSlash(gpsa(array('smd_tag_name', 'smd_tag_title', 'smd_tag_parent', 'smd_tag_cat', 'smd_tag_type', 'smd_tag_id'))));
pagetop(smd_tags_gTxt('manage_lbl'),$message);
extract(smd_tags_prefs_buttons());
// Prefs check
$prefset = smd_tags_pref_get(array_keys($smd_tag_prefs[0]));
$numReqPrefs = count($smd_tag_prefs[0]);
$numPrefs = count($prefset);
$stylereps = array(
'{hov}' => $prefset['smd_tag_t_hover']['val'],
'{cur}' => $prefset['smd_tag_t_hilite']['val'],
);
$types = array(
'article' => ucfirst(gtxt('article')),
'image' => gtxt('tag_image'),
'file' => gtxt('file'),
'link' => gtxt('tag_link'),
);
// Make up the radio buttons
$smd_tag_type = (!empty($smd_tag_type)) ? $smd_tag_type : 'article';
$radios = array();
foreach ($types as $key => $val) {
$id = 'smd_tags_type-'.$key;
$radios[] = n.t.radio('smd_tags_type', $key, ($smd_tag_type == $key) ? 1 : 0, $id).
'';
}
$radios = join(sp.sp, $radios).n;
// Create all the lists but hide them with CSS. jQuery takes over and switches between them on demand
if (smd_tags_table_exist(1) && $numPrefs == $numReqPrefs) {
// Inject styles
echo '';
// Sanitize the multi-tag delimiter
$mdelim = substr($prefset['smd_tag_t_mdelim']['val'], 0, 1);
$mdelim = ($mdelim == '') ? '' : '\\'.$mdelim;
$colOpts = do_list($prefset['smd_tag_t_cols']['val'], ':');
$layout = ($colOpts[0] == '0' || $colOpts[0] == 'list') ? 'list' : (($colOpts[0] == 'group') ? 'listgrp' : 'table');
$colOrder = ($prefset['smd_tag_t_colsord']['val'] == 0) ? 'bycol' : 'byrow';
$wraptag = $layout.':';
if ($layout == 'table') {
$tagCols = (is_numeric($colOpts[0]) && $colOpts[0] > 0) ? $colOpts[0] : 1;
$wraptag .= $tagCols;
$sel = 'td';
} else {
$wraptag .= (isset($colOpts[1])) ? $colOpts[1] : '0';
$sel = 'li';
}
$wraptag .= ':cols:'.$colOrder;
$counts = $prefset['smd_tag_t_count']['val'];
$clink = $prefset['smd_tag_p_linkcat']['val'];
$showrep = $report && $prefset['smd_tag_t_enrep']['val'];
foreach (array_keys($types) as $type) {
$divtagid = "smd_tags_grp_".$type;
$taglist = "tags_".$type;
$$taglist = smd_tag_list_adm(
array(
"type" => $type,
"wraptag" => $wraptag,
"count" => $counts,
"indent" => $prefset['smd_tag_t_indent']['val']
)
);
//TODO: Find a way to add the divtagid only when needed. At the moment it appears twice on the page (illegal DOM)
$tagout[$type] = ''.$$taglist.'
';
echo ''.$tagout[$type].'
';
}
$qs = array(
"event" => "smd_tags",
);
$qsVars = "index.php".join_qs($qs);
$count_lbl = smd_tags_gTxt('count_lbl');
echo <<
var smd_tags_step = '';
// Page initialisation
jQuery(function() {
// Indicate parent list is loading
jQuery(".smd_tags_showpars").ajaxStart(function() {
jQuery(this).fadeTo("normal", 0.33);
}).ajaxStop(function() {
jQuery(this).fadeTo("normal", 1);
});
// Switch between types
jQuery("input[name='smd_tags_type']").change(function () {
var smd_newcontent = jQuery("#smd_tags_grp_"+jQuery(this).val()).html();
jQuery("input[name='smd_tag_id']").val(''); // Changing type forces any 'save' to trigger the 'create' behaviour
jQuery(".smd_tags_showlist").html(smd_newcontent);
var nam = jQuery("input[name='smd_tag_oname']").val();
var tid = jQuery("input[name='smd_tag_id']").val();
var typ = jQuery("input[name='smd_tags_type']:checked").val();
var par = 'root';
var cat = '';
jQuery(".smd_tags_showlist {$sel} span[name='smd_tagnam']").each(function() {
if (jQuery(this).text() == nam) {
jQuery(this).parent().addClass('smd_current');
par = jQuery(this).siblings(":eq(2)").text();
cat = jQuery(this).siblings(":eq(3)").text();
tid = (tid == '') ? jQuery(this).siblings(":eq(0)").text() : tid;
} else {
jQuery(this).parent().removeClass('smd_current');
}
});
// Re-insert the ID if this named item exists in the new list
jQuery("input[name='smd_tag_id']").val(tid);
// Update the UI
smd_parentHandler(nam, typ, tid, par);
smd_catHandler(cat, typ);
jQuery(".smd_tags_linkcat select option[value='"+cat+"']").attr("selected", true);
smd_autofocus();
smd_cellHandler();
jQuery("input[name='smd_tags_type']").each(function () {
if (jQuery(this).val() == typ) {
jQuery(this).next().addClass('smd_current');
} else {
jQuery(this).next().removeClass('smd_current');
}
});
});
// Call the onchange handler for the current type
jQuery("input[name='smd_tags_type']:checked").change();
// Autotag - TODO: foreign character dumbdown for URI
if ({$prefset['smd_tag_t_auto']['val']}) {
spcRE = /\s/g;
badRE = /[^a-zA-Z0-9_\-{$mdelim}]/g;
jQuery("input[name='smd_tags_newtitle']").keyup(function() {
tagname = jQuery(this).val().replace(spcRE, '-').replace(badRE, '').toLowerCase();
jQuery("input[name='smd_tags_newname']").val(tagname);
});
}
// Bind Enter key to create
jQuery("input[name^='smd_tag']").keyup(function(ev) {
if (ev.keyCode == 13) {
jQuery("input[name='smd_tag_create']").click();
}
});
// Prepare the live filter
jQuery('.smd_tag_list_adm {$sel}').addClass('visible');
jQuery('#smd_tagfilter').keyup(function(event) {
// if esc is pressed or nothing is entered
if (event.keyCode == 27 || jQuery(this).val() == '') {
jQuery(this).val('');
jQuery('.smd_tag_list_adm {$sel}').removeClass('visible').show().addClass('visible');
jQuery("#smd_tag_filter_count").empty();
} else {
currOpt = jQuery('#smd_tag_filt_radios :radio:checked').val();
subfilt = (currOpt == 'smd_all') ? '' : currOpt;
smd_tag_filter('.smd_tag_list_adm {$sel}', jQuery(this).val(), subfilt);
}
});
// Trigger the filter if the radio buttons are clicked
jQuery('#smd_tag_filt_radios :radio').change(function() {
jQuery('#smd_tagfilter').keyup().focus();
});
if ('{$showrep}') {
smd_tags_toggle_report();
}
});
// Keep track of which tag has been clicked so it can be edited/updated
function smd_cellHandler() {
// Cell highlights
jQuery(".smd_tags_showlist {$sel}").each(function() {
if (jQuery.trim(jQuery(this).text()) != "") {
jQuery(this).hover(
function () {
jQuery(this).addClass('smd_hover');
},
function () {
jQuery(this).removeClass('smd_hover');
}
)
.click(function() {
var nam = jQuery(this).children(":eq(0)").text();
var tid = jQuery(this).children(":eq(1)").text();
var ttl = jQuery(this).children(":eq(2)").text();
var par = jQuery(this).children(":eq(3)").text();
var cat = jQuery(this).children(":eq(4)").text();
var typ = jQuery("input[name='smd_tags_type']:checked").val()
jQuery("input[name='smd_tag_oname']").val(nam);
jQuery("input[name='smd_tags_newname']").val(nam);
jQuery("input[name='smd_tag_id']").val(tid);
jQuery("input[name='smd_tags_newtitle']").val(ttl);
jQuery(".smd_tags_linkcat select option[value='"+cat+"']").attr("selected", true);
smd_parentHandler(nam, typ, tid, par);
jQuery(".smd_tags_showlist ${sel}").removeClass('smd_current');
jQuery(this).addClass('smd_current');
smd_autofocus();
});
}
});
}
// Ask TXP for the parent dropdown without child entries
function smd_parentHandler(nam, typ, tid, par) {
jQuery.post('{$qsVars}', { step: "smd_tag_parentlist", name: nam, type: typ, id: tid, clr: '2' },
function(data) {
jQuery(".smd_tags_showpars").html(data);
if (par == "root" || par == "") {
jQuery(".smd_tags_showpars select option:first").attr("selected", true);
} else {
jQuery(".smd_tags_showpars select option[value='"+par+"']").attr("selected", true);
}
}
);
}
// Ask TXP for the category dropdown
function smd_catHandler(cat, typ) {
jQuery(".smd_tags_linkcat").fadeTo("normal", 0.33);
jQuery.post('{$qsVars}', { step: "smd_tag_catlist", name: cat, type: typ },
function(data) {
jQuery(".smd_tags_linkcat").html(data);
if (cat == "root" || cat == "") {
jQuery(".smd_tags_linkcat select option:first").attr("selected", true);
} else {
jQuery(".smd_tags_linkcat select option[value='"+cat+"']").attr("selected", true);
}
jQuery(".smd_tags_linkcat").fadeTo("normal", 1);
}
);
}
function smd_autofocus() {
jQuery("input[name='smd_tags_newtitle']").focus().select();
}
// Make sure what is in the boxes is POSTed via the hidden form
function smd_presub() {
jQuery("#smd_tag_postit input[name='step']").val(smd_tags_step);
jQuery("#smd_tag_postit input[name='smd_tag_name']").val(jQuery("input[name='smd_tags_newname']").val());
jQuery("#smd_tag_postit input[name='smd_tag_title']").val(jQuery("input[name='smd_tags_newtitle']").val());
jQuery("#smd_tag_postit input[name='smd_tag_type']").val(jQuery("input[name='smd_tags_type']:checked").val());
jQuery("#smd_tag_postit input[name='smd_tag_parent']").val(jQuery(".smd_tags_showpars option:selected").val());
jQuery("#smd_tag_postit input[name='smd_tag_cat']").val(jQuery(".smd_tags_linkcat option:selected").val());
jQuery("#smd_tag_postit").submit();
}
function smd_multisub() {
theType = jQuery("input[name='smd_tags_type']:checked").val();
jQuery("#smd_tag_multiform input[name='step']").val(smd_tags_step);
jQuery("#smd_tag_multiform input[name='smd_tag_type']").val(theType);
jQuery("#smd_tag_multiform input[name='smd_tag_id']").val(jQuery.map( jQuery("#smd_tags_grp_"+theType+" .visible").find("span[name='smd_tagid']"), function(n,i) {return jQuery(n).text()} ));
jQuery("#smd_tag_multiform input[name='smd_tag_name']").val(jQuery.map( jQuery("#smd_tags_grp_"+theType+" .visible").find("span[name='smd_tagnam']"), function(n,i) {return jQuery(n).text()} ));
jQuery("#smd_tag_multiform input[name='smd_tag_extra']").val(jQuery("#smd_tag_sel_secondary option:selected").val());
}
// Live tag filter
function smd_tag_filter(selector, query, nam) {
var count_lbl = '{$count_lbl}';
var query = jQuery.trim(query);
query = query.replace(/ /gi, '|'); // add OR for regex query
var re = new RegExp(query, "i");
jQuery(selector).each(function() {
sel = (typeof nam=="undefined" || nam=='') ? jQuery(this) : jQuery(this).find("span[name='"+nam+"']");
(sel.text().search(re) < 0) ? jQuery(this).hide().removeClass('visible') : jQuery(this).show().addClass('visible');
});
// Display the matched count
theType = jQuery("input[name='smd_tags_type']:checked").val();
num_matches = count_lbl + jQuery("#smd_tags_grp_"+theType+" .visible").length;
jQuery("#smd_tag_filter_count").text(num_matches);
}
// Handle secondary multi-select options
function smd_tags_seledit(obj) {
smd_tags_step = jQuery(obj).val();
theType = jQuery("input[name='smd_tags_type']:checked").val();
switch(smd_tags_step) {
case "smd_tags_delete":
jQuery("#smd_tag_multi_placeholder").empty();
break;
case "smd_tags_multi_set_parent":
// Grab all elements of the current type that have data in them
listitems = jQuery("#smd_tags_grp_"+theType+" {$sel}").filter(function() {return jQuery(this).find('span').size() > 0}).get();
listitems.sort(function(a, b) {
var compA = jQuery(a).text().toUpperCase();
var compB = jQuery(b).text().toUpperCase();
return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
});
out = '';
jQuery("#smd_tag_multi_placeholder").html(out);
break
case "smd_tags_multi_catlink":
jQuery.post('{$qsVars}', { step: "smd_tag_catlist", type: theType, html_id: 'smd_tag_sel_secondary' },
function(data) {
jQuery("#smd_tag_multi_placeholder").html(data);
}
);
break
}
}
function smd_tags_toggle_report() {
jQuery("#smd_tag_report_pane").toggle('normal');
}
EOJS;
}
// The tag management table
echo startTable('control','','',5);
if (smd_tags_table_exist()) {
// Tables installed
if ($numPrefs == $numReqPrefs) {
// Prefs all installed
echo tr(
fLabelCell(smd_tags_gTxt('control').':')
. tda($btnPrefs, $btnStyle)
. tda($btnSync, $btnStyle)
);
echo endTable();
// Live search / multi-edit
$filtopts = array(
'smd_all' => smd_tags_gTxt('all_lbl'),
'smd_tagttl' => gTxt('title'),
'smd_tagnam' => gTxt('name'),
'smd_tagpar' => smd_tags_gTxt('parent_lbl'),
);
$withselopts = array(
'smd_tags_delete' => gTxt('delete'),
'smd_tags_multi_set_parent' => smd_tags_gTxt('assign_parent_lbl'),
);
// Tack on the category link items if required
if ($clink) {
$filtopts['smd_tagcat'] = smd_tags_gTxt('clink_lbl');
$withselopts['smd_tags_multi_catlink'] = smd_tags_gTxt('link_to_cat_lbl');
}
echo '';
// Main tag list
echo '';
echo '';
echo startTable('list', '', 'smd_tagtable');
echo tr(tda(strong(smd_tags_gTxt('manage_lbl')).' '.$btnHelp, ' colspan="11"'));
echo tr(
td('')
. td(fInput('text', 'smd_tags_newtitle', $smd_tag_title))
. td('')
. td('', '', 'smd_tags_showpars')
. (($clink)
? td(smd_tags_gTxt('clink_lbl').':')
. td('', '', 'smd_tags_linkcat')
: td(sp).td(sp)
)
. td('')
. tda(fInput('text', 'smd_tags_newname', $smd_tag_name))
. tda($btnSave)
. tda($btnCreate)
. tda($btnDelete)
);
echo tr(
td('')
. tdcs($radios, 10)
);
echo tr(
td(sp)
. tdcs($tagout[$smd_tag_type], 10, '', 'smd_tags_showlist')
);
} else if ($numPrefs > 0 && $numPrefs < $numReqPrefs) {
// Prefs possibly corrupt, or plugin updated
echo tr(
tda(strong(smd_tags_gTxt('prefs_some')).br.br
.smd_tags_gTxt('prefs_some_explain').br.br
.smd_tags_gTxt('prefs_some_opts1'), ' colspan="2"')
);
echo tr(
tda($btnRemove,$btnStyle)
. tda($btnInstall, $btnStyle)
);
} else {
// Prefs not installed
echo tr(tda(smd_tags_gTxt('prefs_not_installed'), ' colspan="2"'));
echo tr(tda($btnInstall, $btnStyle));
}
} else {
// Tables not installed
echo tr(tda(strong(smd_tags_gTxt('prefs_some_tbl')).br.br
.smd_tags_gTxt('prefs_some_explain').br.br
.smd_tags_gTxt('prefs_some_opts2'), ' colspan="2"')
);
echo tr(tda($btnInstallTbl, $btnStyle));
}
echo endTable();
}
// ------------------------
// Store the tag that is currently being edited/created
function smd_tag_save() {
extract(doSlash(gpsa(array('smd_tag_oname', 'smd_tag_name', 'smd_tag_title', 'smd_tag_parent', 'smd_tag_cat', 'smd_tag_type', 'smd_tag_id'))));
$ctrls = smd_tags_pref_get(array('smd_tag_t_mdelim'), 1);
$mdelim = $ctrls['smd_tag_t_mdelim']['val'];
$message = $report = '';
$missing = 0;
$ok = $notok = array();
$smd_tag_parent = (!empty($smd_tag_parent)) ? $smd_tag_parent : 'root';
$smd_tag_name = trim($smd_tag_name);
// Can't use safe_upsert() because the WHERE is for ID AND type
if (empty($smd_tag_id)) {
// Create
if ($smd_tag_name=='' && $smd_tag_title=='') {
$message = array(smd_tags_gTxt('no_name', array('{type}' => ucfirst($smd_tag_type))), E_WARNING);
} else {
$alltagnam = do_list($smd_tag_name, $mdelim);
$alltagttl = do_list($smd_tag_title, $mdelim);
$numtags = count($alltagnam);
foreach ($alltagnam as $idx => $theTag) {
if ($theTag == '') {
$missing++;
}
$theTtl = (isset($alltagttl[$idx]) && $alltagttl[$idx] != '') ? $alltagttl[$idx] : $theTag;
$theNam = ($theTag == '') ? sanitizeForUrl($theTtl) : sanitizeForUrl($theTag);
$exists = safe_field('name', SMD_TAG, "name = '$theNam' AND type = '$smd_tag_type'");
$parex = safe_field('name', SMD_TAG, "name = '$smd_tag_parent' AND type = '$smd_tag_type'");
if ($exists) {
if ($numtags > 1) {
$notok[$idx] = 'already_exists';
} else {
$message = array(smd_tags_gTxt('exists', array('{name}' => $theNam, '{type}' => ucfirst($smd_tag_type))), E_WARNING);
}
} else {
$smd_tag_id = safe_insert(
SMD_TAG,
"name='$theNam', title='$theTtl', parent='".(($parex) ? $smd_tag_parent : 'root')."', type='$smd_tag_type'"
);
if ($smd_tag_id > 0) {
if ($smd_tag_cat) {
safe_insert(SMD_TAGC,"tag_id='$smd_tag_id', cat_id=(SELECT id FROM ".PFX."txp_category WHERE name='$smd_tag_cat' AND type='$smd_tag_type')");
}
if ($numtags > 1) {
$ok[] = $theNam;
} else {
$message = smd_tags_gTxt('created', array('{name}' => $theNam, '{type}' => ucfirst($smd_tag_type)));
}
} else {
if ($numtags > 1) {
$notok[$idx] = 'unable_to_create';
} else {
$message = array(smd_tags_gTxt('not_created', array('{name}' => $smd_tag_name, '{type}' => ucfirst($smd_tag_type))), E_WARNING);
}
}
}
}
// Generate the report
if ($ok) {
$report = smd_tags_gTxt('created_rep_lbl').join(', ', $ok).'.';
}
if ($notok) {
$msgs = array();
foreach ($notok as $idx => $reason) {
$msgs[] = $alltagnam[$idx].sp.'('.smd_tags_gTxt($reason).')';
}
$report .= br.br.smd_tags_gTxt('not_created_rep_lbl').join(', ', $msgs);
$report .= ($missing) ? br.br.smd_tags_gTxt('missing_rep_lbl').$missing : '';
}
}
} else {
// Update - no need to maintain referential integrity unlike txp_cats
// since tags are stored against item IDs
$smd_tag_title = (empty($smd_tag_title)) ? $smd_tag_name : $smd_tag_title;
$smd_tag_name = (empty($smd_tag_name)) ? sanitizeForUrl($smd_tag_title) : sanitizeForUrl($smd_tag_name);
$existing_id = safe_field('id', SMD_TAG, "name = '$smd_tag_name' and type = '$smd_tag_type'");
if ($existing_id and $existing_id != $smd_tag_id) {
$message = array(smd_tags_gTxt('exists', array('{name}' => $smd_tag_name, '{type}' => ucfirst($smd_tag_type))), E_WARNING);
} else {
if (safe_update(
SMD_TAG,
"name='$smd_tag_name', title='$smd_tag_title', parent='$smd_tag_parent'",
"type='$smd_tag_type' AND id=$smd_tag_id"
)) {
safe_update(SMD_TAG, "parent='$smd_tag_name'", "parent='$smd_tag_oname' AND type='$smd_tag_type'");
if ($smd_tag_cat) {
safe_upsert(SMD_TAGC, "cat_id=(SELECT id FROM ".PFX."txp_category WHERE name='$smd_tag_cat' AND type='$smd_tag_type')", "tag_id = '$smd_tag_id'");
} else {
safe_delete(SMD_TAGC, "tag_id=$smd_tag_id");
}
$message = smd_tags_gTxt('updated', array('{name}' => $smd_tag_name, '{type}' => ucfirst($smd_tag_type)));
} else {
$message = array(smd_tags_gTxt('not_updated', array('{name}' => $smd_tag_name, '{type}' => ucfirst($smd_tag_type))), E_WARNING);
}
}
}
rebuild_tree_full($smd_tag_type, SMD_TAG);
// Force smd_tag_id to the new ID so it can be immediately edited
$_POST['smd_tag_id'] = $smd_tag_id;
smd_tags_manage($message, $report);
}
// ------------------------
// Store new tag - uses smd_tag_save without the ID/old_name
function smd_tag_create() {
extract(doSlash(gpsa(array('smd_tag_name', 'smd_tag_title', 'smd_tag_parent', 'smd_tag_type'))));
unset($_POST['smd_tag_id']);
smd_tag_save();
}
// ------------------------
// Check if the passed tag exists.
// Return its ID if it does or insert it if it doesn't. Optionally assign it to a parent tag / TXP category
function smd_tag_getsert($tag_name, $tag_type, $tag_title='', $tag_parent='', $tag_cat='', $force_parent=false, $force_cat=false) {
$ctrls = smd_tags_pref_get(array('smd_tag_p_linkcat'), 1);
$clink = $ctrls['smd_tag_p_linkcat']['val'];
$tag_title = ($tag_title == '') ? $tag_name : $tag_title;
$tag_title = doSlash($tag_title);
$tag_name = doSlash(sanitizeForUrl($tag_name));
$tag_type = doSlash($tag_type);
$tag_parent = ($tag_parent) ? doSlash(sanitizeForUrl($tag_parent)) : 'root';
$tag_cat = doSlash($tag_cat);
$ret = safe_field('id', SMD_TAG, "name = '$tag_name' AND type = '$tag_type'");
if (!$ret) {
$ret = safe_insert(SMD_TAG, "name='".$tag_name."', title='$tag_title', parent='$tag_parent', type='$tag_type'");
rebuild_tree_full($tag_type, SMD_TAG);
} elseif($force_parent) {
$upd = safe_update(SMD_TAG, "parent='$tag_parent'", "id='".doSlash($ret)."'");
rebuild_tree_full($tag_type, SMD_TAG);
}
// Assign tag to category if required
$ret = doSlash($ret);
if ($clink && $tag_cat) {
$catid = safe_field('cat_id', SMD_TAGC, "tag_id='$ret'");
if ($catid && $force_cat) {
safe_delete(SMD_TAGC, "cat_id='".$catid."' AND tag_id='".$ret."'");
$catid = false;
}
if(!$catid) {
safe_insert(SMD_TAGC, "tag_id='$ret', cat_id=(SELECT id FROM ".PFX."txp_category WHERE name='$tag_cat' AND type='$tag_type')");
}
}
return $ret;
}
// ------------------------
// Link the passed tags array with the given item ID
function smd_tag_link($itemid, $tagarr, $tag_type) {
assert_int($itemid);
// Remove any existing tags?? Perhaps make this an option?
// safe_delete(SMD_TAGU, 'item_id="'.$itemid.'" AND type="'.doSlash($tag_type).'"');
$tag_type = doSlash($tag_type);
$ctr = $ret = 0;
foreach ($tagarr as $theTag) {
if ($theTag == '') continue;
$theTag = doSlash($theTag); // Probably not necessary
$exists = safe_field('item_id', SMD_TAGU, "tag_id = '$theTag' AND item_id = '$itemid' AND type = '$tag_type'");
if (!$exists) {
$ret = safe_insert(SMD_TAGU, 'tag_id = "'.$theTag.'", item_id="'.$itemid.'", type="'.$tag_type.'"');
if ($ret) {
$ctr++;
}
}
}
return $ctr;
}
// ------------------------
// Delete the given tag(s). Respects noclobber/tree/orphans depending on prefs
function smd_tags_delete() {
global $smd_tag_prefs;
extract(doSlash(gpsa(array('smd_tag_id', 'smd_tag_name', 'smd_tag_type'))));
$message = $msgExtra = $report = '';
$ok = $notok = array();
$pref = smd_tags_pref_get(array('smd_tag_t_delused', 'smd_tag_t_deltree'));
$delu = $pref['smd_tag_t_delused']['val'];
$delt = $pref['smd_tag_t_deltree']['val'];
$smd_tag_id = do_list($smd_tag_id);
$smd_tag_name = do_list($smd_tag_name);
$with_child = safe_column('id', SMD_TAG, "id IN (".join(',', $smd_tag_id).") AND (((rgt-lft-1)/2) > 0) AND type='$smd_tag_type'");
$used_tags = safe_column('tag_id', SMD_TAGU, "tag_id IN (".join(',', $smd_tag_id).") AND type='$smd_tag_type'");
$to_del = $smd_tag_id;
// Protect used and child tags if required
if ($delu == 0) {
$to_del = array_diff($smd_tag_id, $with_child, $used_tags);
foreach (array_merge($with_child, $used_tags) as $delid) {
$notok[] = $smd_tag_name[array_search($delid, $smd_tag_id)];
}
}
$orphans = array();
if ($delt) {
// Add child tags if they are to be deleted
$ids = $to_del;
foreach ($to_del as $delid) {
$row = safe_row('*', SMD_TAG, "id = '$delid' AND type='$smd_tag_type'");
$ids = array_merge($ids, safe_column('id', SMD_TAG, "type = '$smd_tag_type' AND (lft BETWEEN ".$row['lft']." AND ".$row['rgt'].")"));
}
$to_del = array_unique($ids);
$msgExtra = smd_tags_gTxt('children_and');
} else {
// Collect the potential orphans and prepare to promote them
foreach ($to_del as $delid) {
$children = safe_column('id', SMD_TAG, "parent = '".$smd_tag_name[array_search($delid, $smd_tag_id)]."' AND type='$smd_tag_type'");
$children = array_diff($children, $to_del);
if ($children) {
$orphans += $children;
}
}
}
// Everything's prepped: begin the deletion
$numdel = count($to_del);
// Make a note of what we're about to do for the report
$all_dels = safe_rows('id, name', SMD_TAG, "id IN (" . join(',', quote_list($to_del)) . ")");
$all_names = array();
foreach ($all_dels as $aname) {
$all_names[$aname['id']] = $aname['name'];
}
foreach ($to_del as $delid) {
$ok[] = $all_names[$delid];
}
// First get rid of the direct tags
$where = ' IN ('.join(',', $to_del).')';
$ret = safe_delete(SMD_TAG, 'id'.$where." AND type='$smd_tag_type'");
if ($ret) {
if (!$delt) {
// Promote the orphans if required
$orphlist = safe_column('name', SMD_TAG, "type='$smd_tag_type' AND id IN (".join(',', $orphans).")");
safe_update(SMD_TAG, "parent='root'", "type='$smd_tag_type' AND id IN (".join(',', $orphans).")");
}
// Remove references to any deleted tags in articles and linked categories
safe_delete(SMD_TAGU, 'tag_id'.$where." AND type='$smd_tag_type'");
safe_delete(SMD_TAGC, 'tag_id'.$where);
// Force all smd_tag_ boxes clear in the form
$_POST['smd_tag_id'] = $_POST['smd_tag_oname'] = $_POST['smd_tag_name'] = $_POST['smd_tag_title'] = $_POST['smd_tag_parent'] = '';
}
// Build and generate the final report
if ($numdel > 1) {
$message = smd_tags_gTxt('multi_delete', array('{number}' => $numdel));
if ($ok) {
$report = smd_tags_gTxt('deleted_lbl').join(', ', $ok).'.';
}
if ($notok) {
$report .= br.br.smd_tags_gTxt('err_in_use_lbl').join(', ', $notok);
}
if ($orphans) {
$report .= br.br.smd_tags_gTxt('err_orphaned_lbl').join(', ', $orphlist);
}
} else {
if ($ok) {
$message = smd_tags_gTxt('deleted', array('{type}' => ucfirst($smd_tag_type), '{name}' => join(', ', $ok))).$msgExtra;
} else if ($notok) {
$message = smd_tags_gTxt('in_use', array('{type}' => ucfirst($smd_tag_type), '{name}' => join(', ', $notok)));
}
}
rebuild_tree_full($smd_tag_type, SMD_TAG);
smd_tags_manage($message, $report);
}
// ------------------------
// Make the passed tag a child of the given parent (if possible)
function smd_tag_assign_parent($parid, $childid, $tag_type, $childnam, $rebuild = true) {
assert_int($childid);
assert_int($parid);
$status = array();
if ($childid == $parid) {
$status[] = 'err_self_parent';
} else {
$exists = safe_field('name', SMD_TAG, "id = '".doSlash($parid)."' AND type = '".doSlash($tag_type)."'");
$exists = ($exists) ? $exists : 'root';
$ret = safe_update(SMD_TAG, "parent='".doSlash($exists)."'", "id='".$childid."'");
if ($rebuild) {
// Doesn't hurt to rebuild it anyway, even if the assignment fails
rebuild_tree_full($tag_type, SMD_TAG);
}
if ($ret) {
$status[] = 'parent_linked';
} else {
$status[] = 'parent_not_linked';
}
}
return $status;
}
// ------------------------
// Assign a parent to a bunch of tags at once
// Cheating code really; just calls the smd_tag_assign_parent for each tag but delays the
// tree rebuild until the end for speed
function smd_tags_multi_set_parent() {
global $smd_tag_prefs;
extract(doSlash(gpsa(array('smd_tag_type', 'smd_tag_id', 'smd_tag_name', 'smd_tag_extra'))));
$message = $report = '';
$ok = $notok = array();
$theList = array_combine(do_list($smd_tag_id), do_list($smd_tag_name));
foreach ($theList as $idx => $name) {
$status = smd_tag_assign_parent($smd_tag_extra, $idx, $smd_tag_type, $name, false);
if (in_array('parent_linked', $status)) {
$ok[] = $name;
} else {
if (($pos = array_search('parent_not_linked', $status)) !== false) {
unset($status[$pos]);
}
$notok[$idx] = $status;
}
}
if ($ok) {
$parnam = safe_field('name', SMD_TAG, "id='".doSlash($smd_tag_extra)."'");
$parnam = ($parnam) ? $parnam : 'root';
$report = smd_tags_gTxt('parent_linked_lbl', array('{parent}' => $parnam)).join(', ', $ok).'.';
}
if ($notok) {
$msgs = array();
foreach ($notok as $idx => $reasons) {
$rsn = array();
foreach ($reasons as $reason) {
$rsn[] = smd_tags_gTxt($reason);
}
$msgs[] = $theList[$idx].sp.'('.join('/', $rsn).')';
}
$report .= br.br.smd_tags_gTxt('not_parent_linked_lbl').join(', ', $msgs);
}
// Rebuild the tree now all the manipulation has been done
rebuild_tree_full($smd_tag_type, SMD_TAG);
smd_tags_manage($message, $report);
}
// ------------------------
// Links the passed tag with the given category
function smd_tag_assign_category($parnam, $childid, $cat_type, $childnam) {
assert_int($childid);
$status = array();
// Unlink if parent category name is empty
if ($parnam == '') {
safe_delete(SMD_TAGC, "tag_id='".doSlash($childid)."'");
$status[] = 'category_unlinked';
} else {
$exists = safe_field('id', 'txp_category', "name = '".doSlash($parnam)."' AND type = '".doSlash($cat_type)."'");
if ($exists) {
safe_delete(SMD_TAGC, "tag_id='".doSlash($childid)."'");
$ret = safe_insert(SMD_TAGC, "cat_id='".doSlash($exists)."', tag_id = '".doSlash($childid)."'");
if ($ret) {
$status[] = 'category_linked';
} else {
$status[] = 'category_unlinked';
}
}
}
return $status;
}
// ------------------------
// Assign a bunch of tags to a category
// Cheatingly calls smd_tag_assign_category multiple times
function smd_tags_multi_catlink() {
global $smd_tag_prefs;
extract(doSlash(gpsa(array('smd_tag_type', 'smd_tag_id', 'smd_tag_name', 'smd_tag_extra'))));
$message = $report = '';
$ok = $notok = array();
$theList = array_combine(do_list($smd_tag_id), do_list($smd_tag_name));
// Add in any child elements since they all need to take the category too
foreach ($theList as $idx => $name) {
$row = safe_row('*', SMD_TAG, "id='".doSlash($idx)."'");
$rs = safe_rows('id, name', SMD_TAG, "type = '".doSlash($smd_tag_type)."' AND (lft BETWEEN " .$row['lft']. " AND " .$row['rgt']. ")");
if ($rs) {
foreach ($rs as $rec) {
$theList[$rec['id']] = $rec['name'];
}
}
}
foreach ($theList as $idx => $name) {
$status = smd_tag_assign_category($smd_tag_extra, $idx, $smd_tag_type, $name);
if (in_array('category_linked', $status)) {
$ok[] = $name;
} elseif (in_array('category_unlinked', $status)) {
$notok[$idx] = $status;
}
}
if ($ok) {
$report = smd_tags_gTxt('category_linked_lbl', array('{category}' => $smd_tag_extra)).join(', ', $ok).'.';
}
if ($notok) {
$msgs = array();
foreach ($notok as $idx => $reasons) {
$msgs[] = $theList[$idx];
}
$report .= br.br.smd_tags_gTxt('category_unlinked_lbl').join(', ', $msgs);
}
smd_tags_manage($message, $report);
}
// -------------------------------------------------------------
function smd_tags_save_pane_state() {
$panes = array('smd_tag_filters');
$pane = gps('pane');
if (in_array($pane, $panes))
{
set_pref("pane_{$pane}_visible", (gps('visible') == 'true' ? '1' : '0'), 'smd_tags', PREF_HIDDEN, 'yesnoradio', 0, PREF_PRIVATE);
send_xml_response();
} else {
send_xml_response(array('http-status' => '400 Bad Request'));
}
}
// ------------------------
// Allow tags to be imported from tru_tags / rss_uc
function smd_tags_sync($message='', $report='') {
global $smd_tag_prefs, $plugins, $smd_tags_styles;
extract(doSlash(gpsa(array('smd_tags_sync_type', 'smd_tags_sync_section', 'smd_tags_sync_parent', 'smd_tags_delete_orig', 'smd_tags_import_tag_parent', 'smd_tags_import_cat_parent', 'smd_tags_import_force_parent', 'smd_tags_import_force_cat', 'smd_tags_do_import'))));
$ctrls = smd_tags_pref_get(array('smd_tag_p_linkcat', 'smd_tag_t_enrep'), 1);
$clink = $ctrls['smd_tag_p_linkcat']['val'];
$showrep = $report && $ctrls['smd_tag_t_enrep']['val'];
pagetop(smd_tags_gTxt('sync_lbl'),$message);
extract(smd_tags_prefs_buttons());
$plug_tt = (is_array($plugins) && in_array('tru_tags',$plugins));
$plug_uc = (is_array($plugins) && in_array('rss_unlimited_categories',$plugins));
$plugs = $plug_tt || $plug_uc;
// Prefs check
$prefset = smd_tags_pref_get(array_keys($smd_tag_prefs[0]));
$numReqPrefs = count($smd_tag_prefs[0]);
$numPrefs = count($prefset);
// Perform the import via AJAX due to the quantity of tags that may be involved
if ($smd_tags_do_import) {
$impopts = array(
'smd_tags_sync_type' => $smd_tags_sync_type,
'smd_tags_sync_section' => $smd_tags_sync_section,
'smd_tags_sync_parent' => $smd_tags_sync_parent,
'smd_tags_delete_orig' => $smd_tags_delete_orig,
'smd_tags_import_tag_parent' => $smd_tags_import_tag_parent,
'smd_tags_import_cat_parent' => $smd_tags_import_cat_parent,
'smd_tags_import_force_parent' => $smd_tags_import_force_parent,
'smd_tags_import_force_cat' => $smd_tags_import_force_cat,
);
smd_tags_import($impopts);
}
$qs = array(
"event" => "smd_tags",
);
$qsVars = "index.php".join_qs($qs);
// Inject styles
echo '';
echo <<
jQuery(function() {
function smd_tagRestrict(typ) {
grabcat = jQuery("#smd_tags_import_cat_parent option:selected").val();
var smd_clr = (('{$clink}'=='0') ? '2' : ((grabcat=='') ? '2' : ''));
if (grabcat != "undefined" || grabcat != '') {
jQuery.post('{$qsVars}', { step: "smd_tag_parentlist", name: 'smd_tag_parent', type: typ, cat: grabcat, listonly: true, html_id: 'smd_tags_import_tag_parent', clr: smd_clr },
function(data) {
jQuery("#smd_tags_import_tag_parent_holder").html(data);
}
);
}
}
smd_tagRestrict('article');
//TODO: Find out why this doesn't work
jQuery("#smd_tags_import_tag_parent_holder select option[value='{$smd_tags_import_tag_parent}']").attr("selected", true);
if ('{$clink}' == '1') {
jQuery("#smd_tags_import_cat_parent").change(function() {
smd_tagRestrict('article');
});
}
if ('{$showrep}' == '1') {
smd_tags_toggle_report();
}
});
function smd_tags_toggle_report() {
jQuery("#smd_tag_report_pane").toggle('normal');
}
EOS;
// The tag sync preferences
echo startTable('control','','',5);
if (smd_tags_table_exist()) {
// Tables installed
if ($numPrefs == $numReqPrefs) {
// Prefs all installed
echo tr(
fLabelCell(smd_tags_gTxt('control').':')
. tda($btnPrefs, $btnStyle)
. tda($btnManage, $btnStyle)
);
echo endTable();
echo '';
echo '';
} else if ($numPrefs > 0 && $numPrefs < $numReqPrefs) {
// Prefs possibly corrupt, or plugin updated
echo tr(tda(strong(smd_tags_gTxt('prefs_some')).br.br
.smd_tags_gTxt('prefs_some_explain').br.br
.smd_tags_gTxt('prefs_some_opts1'), ' colspan="2"')
);
echo tr(
tda($btnRemove,$btnStyle)
. tda($btnInstall, $btnStyle)
);
} else {
// Prefs not installed
echo tr(tda(smd_tags_gTxt('prefs_not_installed'), ' colspan="2"'));
echo tr(tda($btnInstall, $btnStyle));
}
} else {
// Tables not installed
echo tr(tda(strong(smd_tags_gTxt('prefs_some_tbl')).br.br
.smd_tags_gTxt('prefs_some_explain').br.br
.smd_tags_gTxt('prefs_some_opts2'), ' colspan="2"')
);
echo tr(tda($btnInstallTbl, $btnStyle));
}
echo endTable();
}
// ------------------------
// Import tags from other plugins
function smd_tags_import($smd_tag_options) {
extract($smd_tag_options);
$message = '';
$iparent = ($smd_tags_sync_parent) ? " AND parent = '$smd_tags_sync_parent' " : '';
$rs = safe_rows('*', 'textpattern', (($smd_tags_sync_section) ? "Section='".doSlash($smd_tags_sync_section)."'" : '1=1'));
$row_ctr = $tag_ctr = 0;
$total = count($rs);
$ctrls = smd_tags_pref_get(array('smd_tag_t_enrep'), 1);
$showrep = $ctrls['smd_tag_t_enrep']['val'];
echo '';
}
}
// ------------------------
function smd_tags_import_one() {
$smd_tags_sync_type = gps('smd_tags_sync_type');
$smd_tags_sync_data = gps('smd_tags_sync_data');
$smd_tags_sync_id = gps('smd_tags_sync_id');
$smd_tags_sync_title = gps('smd_tags_sync_title');
$smd_tags_sync_parent = gps('smd_tags_sync_parent');
$smd_tags_import_tag_parent = gps('smd_tags_import_tag_parent');
$smd_tags_import_cat_parent = gps('smd_tags_import_cat_parent');
$smd_tags_import_force_parent = gps('smd_tags_import_force_parent');
$smd_tags_imort_force_cat = gps('smd_tags_import_force_cat');
$smd_tags_delete_orig = gps('smd_tags_delete_orig');
$tag_ids = $tag_names = $keylist = array();
if ($smd_tags_sync_type == '0') {
// tru_tags
$keys = do_list($smd_tags_sync_data);
foreach ($keys as $key) {
if ($key=='') continue;
$keylist[] = array('name' => $key, 'title' => $key);
}
} else if ($smd_tags_sync_type == '1') {
//rss_uc
$clause = 'tc.article_id = '.doSlash($smd_tags_sync_data);
$keylist = getRows("SELECT c.name, c.title FROM ".PFX."textpattern_category as tc LEFT JOIN ".PFX."txp_category as c ON tc.category_id = c.id WHERE ".$clause.$smd_tags_sync_parent);
$keylist = ($keylist) ? $keylist : array();
}
// Insert /update each tag in the given place
foreach ($keylist as $item) {
$itn = trim($item['name']);
$itt = trim($item['title']);
// Skip invalid entries
if ($itn == '' || $itn == NULL || $itt == '' || $itt == NULL) continue;
$id = smd_tag_getsert($itn, 'article', $itt, $smd_tags_import_tag_parent, $smd_tags_import_cat_parent, $smd_tags_import_force_parent, $smd_tags_import_force_cat);
if ($id) {
$tag_ids[] = $id;
$tag_names[] = $itn;
}
}
if ($tag_ids) {
$report = strong($smd_tags_sync_title).': '.join(', ', $tag_names).br;
if ($smd_tags_delete_orig) {
if ($smd_tags_sync_type == '0') {
safe_update('textpattern', "Keywords=''", "id='".doSlash($smd_tags_sync_id)."'");
} else if ($smd_tags_sync_type == '1') {
safe_delete('textpattern_category', "article_id='".doSlash($smd_tags_sync_id)."'");
}
}
}
$tag_ctr = smd_tag_link($smd_tags_sync_id, $tag_ids, 'article');
send_xml_response(array('smd_tags_report' => $report, 'smd_tags_link_ctr' => $tag_ctr));
}
// ------------------------
// MIDDLEWARE - ADMIN/PUBLIC GLUE
// ------------------------
// Grab a valid tag parent list. Cannot be done easily from fixed data: AJAX is the only reliable way :-(
function smd_tag_parentlist() {
$trycat = isset($_POST['cat']);
extract(doSlash(gpsa(array('name', 'type', 'id', 'cat', 'itemid', 'listonly', 'html_id', 'clr'))));
while(@ob_end_clean()); // Get rid of any page so far
$listonly = ($listonly == "" || $listonly == "undefined") ? '' : $listonly;
$html_id = ($html_id == "" || $html_id == "undefined") ? 'smd_tag_parent' : $html_id;
$type = ($type == "" || $type == "undefined") ? 'article' : $type;
$cat = ($cat == "" || $cat == "undefined") ? '' : $cat;
$clr = ($clr == "" || $clr == "undefined") ? '1' : $clr;
$tags = ($itemid == "" || $itemid == "undefined") ? array() : safe_rows("tag_id", SMD_TAGU, "type='$type' AND item_id='$itemid'");
$clrBtn = '['.smd_tags_gTxt('clear').']';
$togBtn = '['.smd_tags_gTxt('toggle').']';
if ($cat) {
$cat = do_list($cat);
$rsid = array();
$rsc = getRows("SELECT txt.id, txt.lft, txt.rgt
FROM ".PFX.SMD_TAG." AS txt, ".PFX."txp_category AS txc, ".PFX.SMD_TAGC." AS txl
WHERE txt.id = txl.tag_id
AND txl.cat_id = txc.id
AND txc.name IN ('" .implode("','", $cat). "')
AND txt.type = '$type'");
if ($rsc) {
foreach ($rsc as $row) {
$ids = safe_column('id', SMD_TAG, "type = '$type' AND (lft BETWEEN " .$row['lft']. " AND " .$row['rgt']. ")");
$rsid = array_merge($rsid, $ids);
}
}
$cat = ($rsc) ? ' AND id IN ('.doQuote(implode("','",$rsid)).')' : '';
}
$clr = (($cat && $clr == '1') || $clr == '2') ? '1=1' : '1=0';
if ($id) {
$id = assert_int($id);
list($lft, $rgt) = array_values(safe_row('lft, rgt', SMD_TAG, 'id = '.$id));
$rs = getTree('root', $type, "lft not between $lft and $rgt".$cat, SMD_TAG);
} else {
$rs = getTree('root', $type, $clr.$cat, SMD_TAG);
}
if ($rs) {
if ($trycat && !$listonly) {
echo smd_multiTreeSelectInput($html_id, $rs, $tags).$clrBtn.$togBtn;
} else {
echo treeSelectInput($html_id, $rs, $name);
}
} else {
echo smd_tags_gTxt('no_parent');
}
exit(); // Don't call page_end()
}
// ------------------------
// Grab a valid category list
function smd_tag_catlist($cat='') {
extract(doSlash(gpsa(array('name','type','html_id'))));
while(@ob_end_clean()); // Get rid of any page so far
$type = ($type == "" || $type == "undefined") ? 'article' : $type;
$html_id = ($html_id == "" || $html_id == "undefined") ? 'smd_catlist' : $html_id;
$rs = getTree('root', $type, "1=1");
if ($rs) {
echo treeSelectInput($html_id, $rs, $name, $html_id);
} else {
echo gTxt('no_categories_available');
}
exit(); // Don't call page_end()
}
// ------------------------
// Handle fake tag section URL
function smd_tags_url_handler($event, $step) {
global $smd_tag_type, $pretext;
if (!smd_tags_table_exist()) {
return;
}
$prefs = smd_tags_pref_get(array('smd_tag_u_sec', 'smd_tag_u_pnam', 'smd_tag_u_ptyp'), 1);
$urlsec = do_list($prefs['smd_tag_u_sec']['val']);
$urlnam = $prefs['smd_tag_u_pnam']['val'];
$urltyp = $prefs['smd_tag_u_ptyp']['val'];
$dflt_url = $urlsec[0];
$subpath = preg_replace("/https?:\/\/.*(\/.*)/Ui","$1",hu);
$regsafesubpath = preg_quote($subpath, '/');
$req = preg_replace("/^$regsafesubpath/i",'/',$_SERVER['REQUEST_URI']);
$qs = strpos($req, '?');
$qatts = ($qs ? a.substr($req, $qs + 1) : '');
if ($qs) $req = substr($req, 0, $qs);
$parts = array_values(array_filter(explode('/', $req)));
$validTypes = array('article','image','file','link');
if ((count($parts) > 1) && (in_array($parts[0], $urlsec))) {
// Clean URL syntax... try to avoid clashes with built-in permlink schemes
if (count($parts) == 4) {
// section/primary_trigger/tag-type/tag
$smd_tag_type = $parts[2];
$smd_tag = $parts[3];
} else if (count($parts) == 3) {
if ($parts[1] == $dflt_url) {
// section/primary_trigger/tag-type
// section/primary_trigger/tag
if (in_array($parts[2], $validTypes)) {
$smd_tag_type = $parts[2];
$smd_tag = '';
} else {
$smd_tag_type = $validTypes[0];
$smd_tag = $parts[2];
}
} else {
// section/id/title
// primary_trigger/tag-type/tag
$smd_tag_type = (is_numeric($parts[1])) ? '' : $parts[1]; // Protect against /section/id/title URL scheme
$smd_tag = $parts[2];
}
} else {
if (in_array($parts[1], $validTypes)) {
// section/tag-type
$smd_tag_type = $parts[1];
$smd_tag = '';
} else {
// section/tag
// May clash with /section/title but there's nothing we can do
$smd_tag_type = $validTypes[0];
$smd_tag = $parts[1];
}
}
smd_tags_set($smd_tag_type, $smd_tag);
$_SERVER['QUERY_STRING'] = $urlnam.'='.$smd_tag .a. $urltyp.'='.$smd_tag_type . $qatts;
$_SERVER['REQUEST_URI'] = $subpath . $parts[0] . '/?' . $_SERVER['QUERY_STRING'];
} else if ((count($parts) == 1) && (in_array($parts[0], $urlsec) || in_array(gps('s'), $urlsec))) {
// Default section + messy tag syntax
$smd_tag_type = (in_array(gps($urltyp), $validTypes)) ? gps($urltyp) : $validTypes[0];
$smd_tag = gps($urlnam);
smd_tags_set($smd_tag_type, $smd_tag);
$_SERVER['QUERY_STRING'] = $urlnam.'='.$smd_tag .a. $urltyp.'='.$smd_tag_type . $qatts;
$_SERVER['REQUEST_URI'] = $subpath . $parts[0] . '/?' . $_SERVER['QUERY_STRING'];
} else if ((count($parts) == 1) && (!in_array($parts[0], $urlsec) && !in_array(gps('s'), $urlsec))) {
// Named section (or /title permlink mode) + messy tag syntax
$theType = gps($urltyp);
$smd_tag = gps($urlnam);
$smd_tag_type = (empty($theType) && empty($smd_tag)) ? '' : ((in_array($theType, $validTypes)) ? $theType : $validTypes[0]);
smd_tags_set($smd_tag_type, $smd_tag);
$_SERVER['QUERY_STRING'] = $urlnam.'='.$smd_tag .a. $urltyp.'='.$smd_tag_type . $qatts;
$_SERVER['REQUEST_URI'] = $subpath . $parts[0] . '/?' . $_SERVER['QUERY_STRING'];
}
}
// ------------------------
function smd_tags_set($typ, $tag='', $item='') {
global $smd_tags;
$smd_tags = array();
// Multi-tag filtering
if ($tag) {
$taglist = explode('|', $tag); //TODO: get delimiter from prefs
$tag = join(',', quote_list($taglist));
}
// MONSTER!
$rs = startRows(
"SELECT id, name, txt.type, parent, title, count(item_id) AS count"
.",(SELECT CAST((rgt - lft - 1) / 2 AS UNSIGNED)) AS children"
.",(SELECT COUNT(*) FROM ".PFX.SMD_TAG." WHERE type='$typ' AND name=name AND lft < txt.lft AND rgt > txt.rgt)-1 AS level"
." FROM ".PFX.SMD_TAG." AS txt, ".PFX.SMD_TAGU." AS txu"
." WHERE txt.type='$typ'"
." AND tag_id = id"
.(($tag) ? " AND name IN ($tag)" : "")
.(($item) ? " AND item_id='$item'" : "")
." GROUP BY id");
while ($row = nextRow($rs)) {
$smd_tags[$typ][$row['id']] = array('tag_name' => $row['name'], 'tag_title' => $row['title'], 'tag_parent' => $row['parent'], 'tag_count' => $row['count'], 'tag_children' => $row['children'], 'tag_level' => $row['level']);
$smd_tags[$typ]['tag_name'][$row['id']] = $row['name'];
$smd_tags[$typ]['tag_title'][$row['id']] = $row['title'];
$smd_tags[$typ]['tag_parent'][$row['id']] = $row['parent'];
$smd_tags[$typ]['tag_count'][$row['id']] = $row['count'];
$smd_tags[$typ]['tag_children'][$row['id']] = $row['children'];
$smd_tags[$typ]['tag_level'][$row['id']] = $row['level'];
}
}
// ------------------------
// Add tags to the global scope, depending on context.
// Returns the current context
function smd_tags_context() {
global $thisarticle, $thisfile, $thislink, $thisimage, $smd_tag_type, $smd_tag_items, $smd_tags, $smd_thistag;
$ctxt = $smd_tag_type;
$ids = array();
$scp = isset($smd_tags[$ctxt]) ? $smd_tags[$ctxt] : array();
// Individual article or list
$id = '';
if (!empty($thisimage)) {
$id = $thisimage['id'];
$ctxt = 'image';
$scp = $thisimage;
} else if(!empty($thisfile)) {
$id = $thisfile['id'];
$ctxt = 'file';
$scp = $thisfile;
} else if(!empty($thislink)) {
$id = $thislink['id'];
$ctxt = 'link';
$scp = $thislink;
} else if(!empty($thisarticle)) {
$id = $thisarticle['thisid'];
$ctxt = 'article';
$scp = $thisarticle;
}
if ($id) {
smd_tags_set($ctxt, '', $id);
}
if(!empty($smd_tags)) {
foreach($smd_tags[$ctxt] as $rid => $row) {
if (is_int($rid)) {
$ids[] = $rid;
}
}
}
// TODO: is this needed to overwrite if tag already set?
if (!empty($smd_thistag)) {
$ids = array($smd_thistag['tag_id']);
}
return array('context' => $ctxt, 'scope' => $scp, 'id' => $ids);
}
// ------------------------
// Load current tag into global scope
function smd_tag_populate($row) {
global $smd_thistag;
$smd_thistag['tag_id'] = $row['id'];
$smd_thistag['tag_name'] = $row['name'];
$smd_thistag['tag_lettername'] = smd_tags_utf8_substr($row['name'], 0, 1);
$smd_thistag['tag_lettertitle'] = smd_tags_utf8_substr($row['title'], 0, 1);
$smd_thistag['tag_type'] = $row['type'];
$smd_thistag['tag_parent'] = $row['parent'];
$smd_thistag['tag_title'] = $row['title'];
$smd_thistag['tag_children'] = $row['children'];
$smd_thistag['tag_level'] = $row['level'];
$smd_thistag['tag_count'] = $row['count'];
$smd_thistag['tag_indent'] = $row['indent'];
$smd_thistag['tag_weight'] = $row['weight'];
}
// Thanks http://us2.php.net/manual/en/function.substr.php
function smd_tags_utf8_substr($str, $start) {
preg_match_all("/./u", $str, $ar);
if(func_num_args() >= 3) {
$end = func_get_arg(2);
return join('',array_slice($ar[0],$start,$end));
} else {
return join('',array_slice($ar[0],$start));
}
}
// ------------------------
// All tags/counts
function smd_tag_list_adm($atts) {
include_once txpath.'/publish/taghandlers.php';
extract(lAtts(array(
'type' => 'article',
'indent' => ' ',
'count' => 1,
'label' => '',
'labeltag' => '',
'wraptag' => '',
'break' => '',
'class' => __FUNCTION__,
'breakclass' => '',
),$atts));
$validTypes = array('list' => 'article', 'image' => 'image', 'file' => 'file', 'link' => 'link');
$rs = getTree('root', $type, '1=1', SMD_TAG);
$rsc = getRows("SELECT txl.tag_id, txc.name
FROM ".PFX.SMD_TAG." AS txt, ".PFX.SMD_TAGC." AS txl, ".PFX."txp_category AS txc
WHERE txt.type = '$type' AND txt.id = txl.tag_id AND txl.cat_id = txc.id");
$rscout = array();
if ($rsc) {
foreach ($rsc as $idx => $row) {
$rscout[$row['tag_id']] = $row['name'];
}
}
if ($rs) {
$out = array();
$totals = array();
// Tally each tag's usage
if ($count) {
$rsu = safe_rows_start('tag_id, count(*) as num', SMD_TAGU, "type='$type' GROUP BY tag_id");
while ($row = nextRow($rsu)) {
$tagid = $row['tag_id'];
$num = $row['num'];
$ids = safe_column('item_id', SMD_TAGU, "type='$type' AND tag_id='$tagid'");
$totals[$tagid] = array($num, join(",",$ids));
}
}
foreach ($rs as $row) {
extract($row);
$parent = (isset($parent)) ? $parent : ''; // Parent might not be defined
$row['type'] = $type;
if (isset($totals[$id])) {
$row['count'] = $totals[$id][0];
$ev = array_keys($validTypes,$type);
$url = 'index.php?event='.$ev[0].a.'search_method=id'.a.'crit='.$totals[$id][1];
$cntop = ' ('. href($totals[$id][0], $url) .')';
} else {
$row['count'] = 0;
$cntop = ' (0)';
}
$rowdata = ''.$name
.''.$id
.''.$title
.''.$parent
.''.((isset($rscout[$id])) ? $rscout[$id] : '')
.'';
$out[] = array(
'data' => $rowdata.str_repeat($indent, $level * 1) . $title . (($count) ? $cntop : '').n,
'level' => $level
);
// $smd_thistag = array();
}
if ($out) {
$wrapit = do_list($wraptag, ":");
if ($wrapit[0] == 'table') {
// Tables are a special case
$totalItems = $rows = count($rs);
$step = 1;
$cols = 1;
$numopts = count($wrapit);
// Each successive level overrides the previous step so by the end of the ifs,
// 3 values are set up: step, rows and cols
if ($numopts > 1) {
$cols = $wrapit[1];
$rows = ceil($totalItems/$cols);
}
if ($numopts > 2) {
if ($wrapit[2] == 'rows') {
$rows = $wrapit[1];
$cols = ceil($totalItems/$rows);
}
}
if ($numopts > 3) {
if ($wrapit[3] == 'bycol') {
$step = ($wrapit[2]=='cols') ? ceil($totalItems/$cols) : $rows;
if ($wrapit[2]=='cols') {
$rows = $step;
} else {
$cols = ceil($totalItems/$rows);
}
}
}
// Generate table based on above rules
$tblout = array();
$cellCtr = 0;
$class = (!empty($class)) ? ' class="'.$class.'"' : '';
$breakclass = (!empty($breakclass)) ? ' class="'.$breakclass.'"' : '';
for ($idx = 0; $idx < $rows; $idx++) {
$bld = array();
for ($jdx = 0; $jdx < $cols; $jdx++) {
$offset = ($wrapit[3]=='bycol') ? ($jdx * $step) + $idx : $cellCtr;
$bld[] = (isset($out[$offset]['data'])) ? td($out[$offset]['data']) : td('');
$cellCtr++;
}
$tblout[] = tr(join('', $bld), $breakclass);
}
return doLabel($label, $labeltag).tag(join(n,$tblout), $wrapit[0], $class);
} else if ($wrapit[0] == 'list') {
// Tags by list, optionally split into new row/col every N items
$totalItems = count($rs);
$lim = 0;
$style = $breakstyle = '';
$numopts = count($wrapit);
if ($numopts > 1) {
$lim = $wrapit[1];
}
if ($numopts > 3) {
if ($wrapit[3] == 'byrow') {
$style = ' style="clear:both;"';
$breakstyle = ' style="float:left;"';
} else {
$style = ' style="float:left;"';
}
}
// Generate the list
$listout = array();
$itemCtr = 0;
$class = (!empty($class)) ? ' class="'.$class.'"' : '';
$breakclass = (!empty($breakclass)) ? ' class="'.$breakclass.'"' : '';
foreach ($out as $item) {
if ($itemCtr == 0) {
$listout[] = "";
}
$listout[] = '- '.$item['data'].'
';
$itemCtr++;
if (($lim > 0 && $itemCtr >= $lim) || $itemCtr >= $totalItems-1) {
$listout[] = '
';
$itemCtr = 0;
}
}
return doLabel($label, $labeltag).tag(join(n,$listout), 'div', $class);
} else if ($wrapit[0] == 'listgrp') {
// Tags by list, split into new row/col every time a new level 'N' is reached
$totalItems = count($rs);
$level = 0;
$style = $breakstyle = '';
$numopts = count($wrapit);
if ($numopts > 1) {
$level = $wrapit[1];
}
if ($numopts > 3) {
if ($wrapit[3] == 'byrow') {
$style = ' style="clear:both;"';
$breakstyle = ' style="float:left;"';
} else {
$style = ' style="float:left;"';
}
}
// Generate the list
$listout = array();
$itemCtr = 0;
$class = (!empty($class)) ? ' class="'.$class.'"' : '';
$breakclass = (!empty($breakclass)) ? ' class="'.$breakclass.'"' : '';
$prevel = 0;
foreach ($out as $item) {
if ($prevel >= $item['level'] && $item['level'] <= $level) {
if ($itemCtr > 0) {
$listout[] = '';
}
$listout[] = "";
}
$listout[] = '- '.$item['data'].'
';
$prevel = $item['level'];
if ($itemCtr >= $totalItems-1) {
$listout[] = '
';
}
$itemCtr++;
}
return doLabel($label, $labeltag).tag(join(n,$listout), 'div', $class);
} else {
//TODO: only use the data column from $out
return doLabel($label, $labeltag).doWrap($out, $wrapit[0], $break, $class, $breakclass);
}
}
return '';
}
}
// ------------------------
// Find some value in a tree and return the matching entries
function smd_tag_tree_search($val, $tree, $limit=0, $field='name') {
$ctr = 0;
$out = array();
foreach ($tree as $idx => $entry) {
if ($entry[$field] == $val) {
$out[] = $tree[$idx];
$ctr++;
}
if ($limit > 0 && $ctr == $limit) {
break;
}
}
return $out;
}
// ------------------------
// PHP4 equivalent of array_combine (from http://www.php.net/manual/en/function.array-combine.php)
if (!function_exists('array_combine')) {
function array_combine($arr1, $arr2) {
$out = array();
$arr1 = array_values($arr1);
$arr2 = array_values($arr2);
foreach($arr1 as $key1 => $value1) {
$out[(string)$value1] = $arr2[$key1];
}
return $out;
}
}
// ------------------------
// 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' => NULL,
'id' => NULL,
'name' => NULL,
'title' => NULL,
'parent' => NULL,
'count' => NULL,
'children' => NULL,
'level' => NULL,
'debug' => '0',
),$atts));
// Validate atts
$validTypes = array('article','image','file','link');
$ctxt = smd_tags_context();
$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- ]+)/';
// TODO: Consolidate this into a loop for each 'id', 'name', type', 'title', ...
$num = preg_match_all($opRE, $id, $parts);
$eqtest['id'] = ($num<=0) ? $id : NULL;
if ($num>0) $nutest['id'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $name, $parts);
$eqtest['name'] = ($num<=0) ? $name : NULL;
if ($num>0) $nutest['name'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $type, $parts);
$eqtest['type'] = ($num<=0) ? $type : NULL;
if ($num>0) $nutest['type'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $title, $parts);
$eqtest['title'] = ($num<=0) ? $title : NULL;
if ($num>0) $nutest['title'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $parent, $parts);
$eqtest['parent'] = ($num<=0) ? $parent : NULL;
if ($num>0) $nutest['parent'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $count, $parts);
$eqtest['count'] = ($num<=0) ? $count : NULL;
if ($num>0) $nutest['count'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $children, $parts);
$eqtest['children'] = ($num<=0) ? $children : NULL;
if ($num>0) $nutest['children'] = array($parts[1][0] => $parts[2][0]);
$num = preg_match_all($opRE, $level, $parts);
$eqtest['level'] = ($num<=0) ? $level : NULL;
if ($num>0) $nutest['level'] = array($parts[1][0] => $parts[2][0]);
if ($debug) {
echo "++ IF_TAG TESTS ++";
dmp($eqtest, $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 (!is_null($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], empty($tval) && $tval !== '0')) {
$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' => 1,
'link' => '',
'section' => '',
'cleanurls' => 1,
'parent' => 0, // not useful to print but good for URLs to nav back up the tree
'parentlabel' => 'Up a level',
'wraptag' => '',
'class' => __FUNCTION__,
'style' => '',
'pad_str' => '',
'pad_pos' => 'left', // left/right/both with optional :in suffix to indicate if it's to go inside the link
),$atts));
$smdpref = smd_tags_pref_get(array('smd_tag_u_sec', 'smd_tag_u_pnam', 'smd_tag_u_ptyp'), 1);
$urlsec = do_list($smdpref['smd_tag_u_sec']['val']);
$section = ($section) ? $section : $urlsec[0];
$in_default = (in_array($section, $urlsec));
$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'];
$level = $smd_thistag['tag_level'];
$style = ($style) ? ' style="'.$style.'"' : '';
$padding = ($pad_str) ? str_repeat($pad_str, $level) : '';
$pad_pos = do_list($pad_pos, ':');
$pad['lin'] = ($padding && (in_array('left', $pad_pos) !== false || in_array('both', $pad_pos) !== false) && in_array('in', $pad_pos) !== false) ? $padding : '';
$pad['rin'] = ($padding && (in_array('right', $pad_pos) !== false || in_array('both', $pad_pos) !== false) && in_array('in', $pad_pos) !== false) ? $padding : '';
$pad['l'] = ($padding && (in_array('left', $pad_pos) !== false || in_array('both', $pad_pos) !== false) && (empty($pad['lin']) || !$link)) ? $padding : '';
$pad['r'] = ($padding && (in_array('right', $pad_pos) !== false || in_array('both', $pad_pos) !== false) && (empty($pad['rin']) || !$link)) ? $padding : '';
$dest = ($permlink_mode == 'messy' || !$cleanurls)
? pagelinkurl(array('s' => $section, $urlnam => $tname, $urltyp => $smd_thistag['tag_type']))
: '/'.$section.'/'.(($in_default) ? '' : $urlsec[0].'/').$smd_thistag['tag_type'].'/'.$tname;
if ($thing) {
$out = ''.parse($thing).'';
} elseif ($link) {
$out = $pad['l'].''.$pad['lin'].$label.$pad['rin'].''.$pad['r'];
} else {
$out = $pad['l'].$label.$pad['r'];
}
return doTag($out, $wraptag, $class, $style);
}
// ------------------------
// 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__,
'style' => '',
'wraptag' => '',
'wrapcount' => ' (:)',
'showempty' => '1',
'paramdelim' => ':',
),$atts));
$wrapcount = explode($paramdelim, $wrapcount); // do_list does a trim: don't want that
$style = ($style) ? ' style="'.$style.'"' : '';
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, $style);
}
// ------------------------
// 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','lettername','lettertitle','type','parent','children','level','count','indent', 'weight');
$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;
}
$matches = array();
foreach ($matchWith as $matchItem) {
if (strpos($matchItem, 'tag_') !== false) {
$lookin = (isset($smd_tags[$matchType])) ? $smd_tags[$matchType] : array();
} 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 ".PFX.SMD_TAGU. " AS tu ON txp.id = tu.item_id
LEFT JOIN ".PFX.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)) {
$safe = ($thisarticle) ? $thisarticle : NULL;
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'];
$thisarticle = $safe;
}
}
}
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 ".PFX.SMD_TAGU. " AS tu ON txp.id = tu.item_id
LEFT JOIN ".PFX.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)) {
$safe = ($thisimage) ? $thisimage : NULL;
$thisimage = image_format_info($row);
$out[] = ($form) ? parse_form($form) : (($thing) ? parse($thing) : image(array('id' => $row['id'])));
$uniqrs[] = $row['id'];
$thisimage = $safe;
}
}
}
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 ".PFX.SMD_TAGU. " AS tu ON txp.id = tu.item_id
LEFT JOIN ".PFX.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)) {
$safe = ($thisfile) ? $thisfile : NULL;
$thisfile = file_download_format_info($row);
$out[] = ($form) ? parse_form($form) : (($thing) ? parse($thing) : file_download_link(array('filename' => $row['filename']), $row['filename']));
$uniqrs[] = $row['id'];
$thisfile = $safe;
}
}
}
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 ".PFX.SMD_TAGU. " AS tu ON txp.id = tu.item_id
LEFT JOIN ".PFX.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)) {
$safe = ($thislink) ? $thislink : NULL;
$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']));
$uniqrs[] = $row['id'];
$thislink = $safe;
}
}
}
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(
'flavour' => 'list', // 1) list: only tags with assignments, 2) cloud (adds weighting to smd_thistag)
'showall' => '0', // 0: just used, 1: include empties
'type' => '',
'id' => '',
'name' => '',
'exclude' => '',
'parent' => '', // Start list from here
'sublevel' => '', // Get tags from this sub-level only (0=top level, 1-level 1, ..., all=everything)
'offset' => 0,
'limit' => 99999,
'form' => '',
'indent' => ' ',
'section_link' => '',
'sort' => '',
'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();
$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 = ($showall) ? getTree((($parent) ? $parent : 'root'), $type, '1=1', SMD_TAG) : $smd_tags[$ctxt];
} else {
$where = ($where) ? $where : '1=1';
if ($where == "1=1" && $attemptMatch) {
$where = 'name = "smd_' .mt_rand(). '"';
}
if ($debug) {
dmp($where);
}
$rs = getTree((($parent) ? $parent : 'root'), $type, (($showall) ? '1=1' : $where), SMD_TAG);
$sublevel = ($parent && $sublevel=='') ? ((smd_tag_tree_search($parent, $rs, 1)) ? '>=1' : '>=0') : $sublevel;
preg_match('/([=<>]+)?([\d]+|all)/', $sublevel, $matches);
if($matches) {
$oper = ($matches[1] && in_list($matches[1], '>, <, >=, <=')) ? $matches[1] : '==';
$sublevel=$matches[2];
} else {
$oper = '==';
}
}
if ($debug) {
echo "++ TREE / LEVEL INFO++";
dmp($rs);
dmp($sublevel, $oper, $matches);
}
if ($rs) {
$out = array();
$totals = array();
$outsub = array();
while (list($key, $row) = each($rs)) {
// Only add the row if one of:
// 1) showall is on
// 2) sublevel = 'all'
// 3) no parent specified
// 4) sublevel is equal/greater/less than the row's level
if ($showall == 1 || strtolower($sublevel) == "all" || !$parent || ($oper=='=='? $row['level'] == $sublevel : (($oper=='>=') ? $row['level'] >= $sublevel : (($oper=='<=') ? $row['level'] <= $sublevel : (($oper=='>') ? $row['level'] > $sublevel : $row['level'] < $sublevel) ) ) ) ) {
$outsub[] = $row;
}
}
$rs = $outsub;
if ($debug) {
echo "++ SUBLEVEL RECORDS ++";
dmp($rs);
}
if ($shuffle) {
shuffle($rs);
} else if ($sort) {
$sortPrefix = "SORT_";
$sortOrder = array();
$sort = do_list($sort);
$nsrt = count($sort);
for ($idx = 0; $idx < $nsrt; $idx++) {
$sort_dir = explode(' ', $sort[$idx]);
if (count($sort_dir) <= 1) {
$sort_dir[1] = "asc";
}
$direction = ($sort_dir[1] == "desc") ? $sortPrefix.'DESC' : $sortPrefix.'ASC';
$sortOrder[] = array("col" => $sort_dir[0], "sort" => $direction);
}
// Translate the rows into columns that can be sorted
foreach($rs as $key => $row) {
foreach ($row as $identifier => $item) {
$varname = "col_".$identifier;
${$varname}[$key] = $item;
}
}
// Make up an array_multisort arg list and execute it.
// The necessary evil() is because we don't know how many cols there will be in the array
for ($idx = 0; $idx < count($sortOrder); $idx++) {
$sortargs[] = '$col_'.$sortOrder[$idx]['col'];
$sortargs[] = $sortOrder[$idx]['sort'];
}
$sortit = 'array_multisort('.implode(", ",$sortargs).', $rs);';
eval($sortit);
}
$rs = array_slice($rs, $offset, $limit);
if ($debug) {
echo "++ POST-FILTER RECORDS ++";
dmp($rs);
}
// Set up the cloud weighting if desired
$flavour = do_list($flavour, ':');
$countStore = array();
if ($flavour[0] == 'cloud') {
$minPercent = (isset($flavour[1]) && !empty($flavour[1])) ? $flavour[1] : 100;
$maxPercent = (isset($flavour[2]) && !empty($flavour[2])) ? $flavour[2] : 200;
$max = 1; $min = 99999; $count = 0;
foreach ($rs as $row) {
$count = safe_count(SMD_TAGU, "type='".doSlash($type)."' AND tag_id='".doSlash($row['id'])."'");
$max = ($count > $max ? $count : $max);
$min = ($min > $count ? $count : $min);
// Cache the results of the usage counts to save us doing them again in the main loop later
$countStore[$row['id']] = $count;
}
$multiplier = ($max > $min) ? ($maxPercent - $minPercent) / ($max - $min) : 1;
}
// Iterate over the tags, populate the local context (global!) variable and parse through the form/container
foreach ($rs as $row) {
extract($row);
$row['type'] = $type;
$row['count'] = (isset($countStore[$id])) ? $countStore[$id] : safe_count(SMD_TAGU, "type='".doSlash($type)."' AND tag_id='".doSlash($id)."'");
$row['indent'] = str_repeat($indent, $level * 1);
$row['weight'] = ($flavour[0] == 'cloud') ? floor($minPercent + (($max - ( $max - ($row['count'] - $min))) * $multiplier)) : 1;
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 '';
}
/*
register_callback('tru_tags_atom_handler', 'atom_entry');
function tru_tags_atom_handler($event, $step) { return tru_tags_feed_handler(true); }
register_callback('tru_tags_rss_handler', 'rss_entry');
function tru_tags_rss_handler($event, $step) { return tru_tags_feed_handler(false); }
function tru_tags_feed_handler($atom) {
global $thisarticle, $tru_tags_prefs;
extract($thisarticle);
$tags = tru_tags_get_tags_for_article($thisid);
if ($tru_tags_prefs[TAGS_IN_FEED_BODY]->value) {
$extrabody = '';
$FORM_NAME = 'tru_tags_feed_tags';
if (fetch('form', 'txp_form', 'name', $FORM_NAME)) {
$form = fetch_form($FORM_NAME);
$extrabody = trim(parse($form));
} else {
$atts = tru_tags_get_standard_cloud_atts(array(), false, true);
$extrabody = 'tags: ' . tru_tags_render_cloud($atts, $tags, $tags) . '
';
}
global $thisarticle;
if (trim($thisarticle['excerpt'])) {
$thisarticle['excerpt'] = trim($thisarticle['excerpt']).n.$extrabody.n;
}
$thisarticle['body'] = trim($thisarticle['body']).n.$extrabody.n;
}
if ($tru_tags_prefs[TAGS_IN_FEED_CATEGORIES]->value) {
$output = array();
foreach ($tags as $tag) {
if ($atom) {
$output[] = '';
} else {
$output[] = '' . htmlspecialchars($tag) . '';
}
}
return n.join(n, $output).n;
}
}
*/
// ------------------------
// Plugin-specific replacement strings - localise as required
function smd_tags_gTxt($what, $atts = array()) {
$lang = array(
'ac_std' => 'Standard',
'ac_str' => 'Strict',
'all_lbl' => 'All',
'already_exists' => 'already exists',
'assign_parent_lbl' => 'Assign parent',
'by_column' => 'Column',
'by_row' => 'Row',
'category_linked_lbl' => 'Linked to category "{category}":',
'category_unlinked_lbl' => 'Unlinked: ',
'clear' => 'clr',
'clink_lbl' => 'Linked cat',
'control' => 'Plugin control',
'count_lbl' => ' Matched tags: ',
'created' => '{type} tag "{name}" created',
'children_and' => ' and its children',
'children_del' => 'Delete children',
'children_pro' => 'Promote children',
'created_rep_lbl' => 'Created tags: ',
'deleted' => '{type} tag "{name}" deleted',
'deleted_lbl' => 'Deleted tags: ',
'err_in_use_lbl' => 'Tags in use: ',
'err_orphaned_lbl' => 'Orphaned tags: ',
'err_self_parent' => 'self parent',
'exists' => '{type} tag "{name}" already exists',
'filter' => 'Filter',
'import_report' => 'Tag import results',
'import_results_pt1' => 'Articles: ',
'import_results_pt2' => 'Tags linked: ',
'in_use' => '{type} tag "{name}" is in use',
'link_to_cat_lbl' => 'Link to category',
'manage_lbl' => 'Manage tags',
'missing_name' => 'Tag name missing',
'missing_rep_lbl' => 'Missing tags: ',
'multi_delete' => 'Tags deleted: {number} (see report for details)',
'no_articles' => 'No articles to import',
'no_name' => '{type} tag NOT created: needs a name',
'no_parent' => 'No parent tags',
'not_available' => 'smd_tags tables are not installed',
'not_created' => '{type} tag "{name}" NOT created',
'not_created_rep_lbl' => 'Tags NOT created: ',
'not_deleted' => '{type} tag "{name}" NOT deleted',
'not_deleted_lbl' => 'Not deleted: ',
'not_parent_linked_lbl' => 'Not linked: ',
'not_updated' => '{type} tag "{name}" NOT updated',
'parent_lbl' => 'Parent',
'parent_linked_lbl' => 'Assigned these tag(s) to "{parent}": ',
'pref_install_lbl' => 'Install prefs',
'pref_remove_lbl' => 'Remove prefs',
'pref_show_lbl' => 'Preferences',
'prefs_installed' => 'Preferences installed',
'prefs_not_installed' => 'Preferences not installed.',
'prefs_pane' => 'Prefs',
'prefs_removed' => 'Preferences removed',
'prefs_some' => 'Not all preferences available.',
'prefs_some_explain' => 'This is either a new installation or a different version'.br.'of the plugin to one you had before.',
'prefs_some_opts1' => 'Choose "Remove prefs" to remove them all or "Install prefs" to add'.br.'any new ones, leaving all existing prefs untouched.',
'prefs_some_opts2' => 'Click "Install tables" to add or update the tables'.br.'leaving all existing data untouched.',
'prefs_some_tbl' => 'Not all table info available.',
'prefs_title' => 'smd Tags Preferences',
'sel_list' => 'Select list',
'prefs_p' => 'Interface settings',
'prefs_t' => 'Tag management',
'prefs_u' => 'URL management',
'recent_report' => 'Display recent report',
'report_lbl' => 'Report: smd_tags',
'rss_uc_only' => ' (rss_uc only)',
'smd_tags' => 'smd Tags',
'smd_tag_p_enable' => 'Enable tags in',
'smd_tag_t_astart' => 'Initial pane',
'smd_tag_p_input' => 'Enter tags using',
'smd_tag_p_linkcat' => 'Link tags to categories',
'smd_tag_p_qtag' => 'Quick tag (requires plugin)',
'smd_tag_p_qtpath' => 'JS plugin dir',
'smd_tag_p_qtstyl' => 'JS style dir',
'smd_tag_p_size' => 'Select/textarea rows',
'smd_tag_t_auto' => 'Auto name',
'smd_tag_t_cols' => 'Tag layout',
'smd_tag_t_colsord' => 'Order tags by',
'smd_tag_t_count' => 'Show tag usage counts',
'smd_tag_t_deltree' => 'When deleting a parent',
'smd_tag_t_delused' => 'Allow deletion of used tags',
'smd_tag_t_enrep' => 'Automatically display reports',
'smd_tag_t_hilite' => 'Clicked RGB colour',
'smd_tag_t_hover' => 'Mouse-over RGB colour',
'smd_tag_t_indent' => 'Sub-tag level indicator',
'smd_tag_t_mdelim' => 'Multi-tag delimiter (1 char)',
'smd_tag_u_pnam' => 'URL name parameter',
'smd_tag_u_ptyp' => 'URL type parameter',
'smd_tag_u_sec' => 'Trigger(s) for tag lists',
'sync_assignicle_lbl' => 'Assign tags to article',
'sync_delete_orig_lbl' => 'Delete original',
'sync_link_cat_lbl' => 'Link tags to category',
'sync_force_cat_lbl' => 'Force category link if tag already exists',
'sync_force_parent_lbl' => 'Force parent if tag already exists',
'sync_lbl' => 'Import tags',
'sync_import_opts' => 'Import options',
'sync_parent_lbl' => 'Start from parent category',
'sync_parent_cat_lbl' => 'Link to category',
'sync_parent_tag_lbl' => 'Assign to parent tag',
'sync_plugin_opts' => 'Plugin options',
'sync_plugs_not_installed' => 'tru_tags or rss_unlimited_categories must be installed and activated to import',
'sync_section_lbl' => 'Articles from section',
'sync_type_lbl' => 'Import from',
'sync_type1' => 'tru_tags',
'sync_type2' => 'rss_unlimited_categories',
'tag_search' => 'Tag search',
'textbox' => 'Text area',
'textboxplus' => 'Text area+',
'textlist' => 'Text list',
'tbl_install_lbl' => 'Install tables',
'tbl_installed' => 'Tag tables installed. ',
'tbl_not_installed' => 'Tag tables NOT installed. ',
'tbl_not_removed' => 'Tag tables NOT removed. ',
'tbl_rebuild_lbl' => 'Rebuild tags',
'tbl_rebuilt' => 'Tag tables rebuilt',
'tbl_remove_lbl' => 'Remove tables',
'tbl_removed' => 'Tag tables removed. ',
'tags_pane' => 'Manage tags',
'title' => 'Tags',
'toggle' => 'tog',
'updated' => '{type} tag "{name}" updated',
'unable_to_create' => 'cannot create',
'with_filtered' => 'With filtered: ',
);
return strtr($lang[$what], $atts);
}