// 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' => '
'.fInput('submit', 'submit', smd_tags_gTxt('tbl_install_lbl'), 'smallerbox').'
', 'btnRemoveTbl' => '
'.fInput('submit', 'submit', smd_tags_gTxt('tbl_remove_lbl'), 'smallerbox').'
', 'btnRebuildTbl' => '
'.fInput('submit', 'submit', smd_tags_gTxt('tbl_rebuild_lbl'), 'smallerbox').'
', 'btnInstall' => '
'.fInput('submit', 'submit', smd_tags_gTxt('pref_install_lbl'), 'smallerbox').'
', 'btnRemove' => '
'.fInput('submit', 'submit', smd_tags_gTxt('pref_remove_lbl'), 'smallerbox').'
', 'btnPrefs' => '
'.fInput('submit', 'submit', smd_tags_gTxt('pref_show_lbl'), 'smallerbox').'
', 'btnManage' => '
'.fInput('submit', 'submit', smd_tags_gTxt('manage_lbl'), 'smallerbox').'
', 'btnSync' => '
'.fInput('submit', 'submit', smd_tags_gTxt('sync_lbl'), 'smallerbox').'
', '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 '
'; $ctr = 1; foreach ($prefset as $pnam => $parr) { $pval = $parr['val']; $out = (isset($smd_tag_prefs[2][$ctr])) ? tr(tdcs(hed($smd_tag_prefs[2][$ctr], 2, ' class="pref-heading"'), 4)) : ''; $label = ''; $out .= tda($label, ' style="text-align: right; vertical-align: middle;"'); switch($pnam) { case "smd_tag_p_enable": $options = smd_tags_pref_explode($pval); $out .= ''; foreach ($options as $opt => $onoff) { $out .= '' . checkbox("enable".$opt, 1, $onoff) . '  '; } $out .= ''; $out .= fInput('hidden', 'smd_tag_p_enable', implode($options),'','','','','','smd_tag_p_enable'); break; case "smd_tag_p_size": case "smd_tag_t_cols": case "smd_tag_t_hilite": case "smd_tag_t_hover": case "smd_tag_t_indent": case "smd_tag_t_mdelim": case "smd_tag_u_pnam": case "smd_tag_u_ptyp": case "smd_tag_u_sec": $out .= tag(fInput('text', $pnam, $pval, 'edit', '', '', 20, '', $pnam), 'td', ' colspan="3"'); break; case "smd_tag_p_qtpath": case "smd_tag_p_qtstyl": $out .= tag(fInput('text', $pnam, $pval, 'edit', '', '', 30, '', $pnam), 'td', ' colspan="3"'); break; case "smd_tag_t_auto": $out .= tdcs(onoffRadio($pnam, $pval),3); break; case "smd_tag_t_colsord": $out .= tdcs(radio($pnam,0,(($pval==0)?1:0)).smd_tags_gTxt('by_column')." " . radio($pnam,1,(($pval==1)?1:0)).smd_tags_gTxt('by_row')." ",3); break; case "smd_tag_t_deltree": $out .= tdcs(radio($pnam,0,(($pval==0)?1:0)).smd_tags_gTxt('children_pro')." " . radio($pnam,1,(($pval==1)?1:0)).smd_tags_gTxt('children_del')." ",3); break; case "smd_tag_t_count": case "smd_tag_t_delused": case "smd_tag_t_enrep": case "smd_tag_p_linkcat": $out .= tdcs(yesnoRadio($pnam, $pval),3); break; case "smd_tag_t_astart": $out .= tdcs(radio($pnam,0,(($pval==0)?1:0)).smd_tags_gTxt('prefs_pane')." " . radio($pnam,1,(($pval==1)?1:0)).smd_tags_gTxt('tags_pane')." ",3); break; case "smd_tag_p_qtag": $out .= tdcs(radio($pnam,0,(($pval==0)?1:0)).gTxt('off')." " . radio($pnam,1,(($pval==1)?1:0)).smd_tags_gTxt('ac_std')." "." " . radio($pnam,2,(($pval==2)?1:0)).smd_tags_gTxt('ac_str')." ",3); break; case "smd_tag_p_input": $out .= tdcs(radio($pnam,0,(($pval==0)?1:0)).smd_tags_gTxt('sel_list')." ". radio($pnam,1,(($pval==1)?1:0)).smd_tags_gTxt('textlist')." ". radio($pnam,2,(($pval==2)?1:0)).smd_tags_gTxt('textbox')." ". radio($pnam,3,(($pval==3)?1:0)).smd_tags_gTxt('textboxplus')." ",3); break; } echo tr($out); $ctr++; } echo tr(tda($btnPrefsSave, $btnStyle)); 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 '
'; echo '
'.smd_tags_gTxt('tag_search').'
'; echo hInput('step', ''); echo hInput('smd_tag_type', ''); echo hInput('smd_tag_id', ''); echo hInput('smd_tag_name', ''); echo hInput('smd_tag_extra', ''); echo ''; echo ''; echo '
'.radioSet($filtopts, 'smd_tag_filtopt', 'smd_all', '', 'smd_tag_filtopt').'
'; echo '
'.smd_tags_gTxt('with_filtered').selectInput('smd_tag_multisel',$withselopts, '', true, ' onchange="smd_tags_seledit(this); return false;"').n.fInput('submit', '', gTxt('go'), 'smallerbox').'
'; echo '
'.smd_tags_gTxt('recent_report').''; echo '
'; // Main tag list echo '

'.smd_tags_gTxt('report_lbl').'

'.$report.'
'; echo '
'; echo hInput('step', ''); echo hInput('smd_tag_id', $smd_tag_id); echo hInput('smd_tag_name', ''); echo hInput('smd_tag_oname', $smd_tag_name); echo hInput('smd_tag_parent', ''); echo hInput('smd_tag_cat', ''); echo hInput('smd_tag_title', ''); echo hInput('smd_tag_type', ''); 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 '

'.smd_tags_gTxt('report_lbl').'

'.$report.'
'; echo '
'; echo startTable('list'); if ($plugs) { echo tr(tda(strong(smd_tags_gTxt('sync_lbl')).' '.$btnHelp, ' colspan="5"')); $lblStyle = ' style="text-align: right; vertical-align: middle;"'; echo tr(tdcs(hed(smd_tags_gTxt('sync_plugin_opts'), ' class="pref-heading"'), 4)); echo tr( tda('', $lblStyle) .tdcs((($plug_tt) ? radio('smd_tags_sync_type',0,((empty($smd_tags_sync_type)) ? 1 : ($plug_tt?1:0) )).smd_tags_gTxt('sync_type1').' ' : '') . (($plug_uc) ? radio('smd_tags_sync_type',1,(($smd_tags_sync_type=='1') ? 1 : (($plug_uc && !$plug_tt)?1:0) )).smd_tags_gTxt('sync_type2').' ' : '') ,3) ); // Section import $rs = safe_column('name', 'txp_section', "name != 'default'"); if ($rs) { $out = tag(selectInput('smd_tags_sync_section', $rs, $smd_tags_sync_section, true), 'td', ' colspan="3"'); } else { $out = tag(smd_tags_gTxt('no_sections'), 'td', ' colspan="3"'); } echo tr(tda('', $lblStyle) . $out); // Start at parent category (rss_uc only) // The list is populated anyway even if the plugin isn't active because $rsCats is used later if ($clink) { $rsCats = getTree('root', 'article'); if ($rsCats) { $catlist = tag(treeSelectInput('smd_tags_sync_parent', $rsCats, $smd_tags_sync_parent, 'smd_tags_sync_parent', 35).(($plug_tt) ? smd_tags_gTxt('rss_uc_only') : ''), 'td', ' colspan="3"'); } else { $catlist = tag(smd_tags_gTxt('no_parent'), 'td', ' colspan="3"'); } if ($plug_uc) { echo tr(tda('', $lblStyle) . $catlist); } } // Delete original echo tr( tda('', $lblStyle) .tdcs(yesnoRadio('smd_tags_delete_orig', $smd_tags_delete_orig),3) ); echo tr(tdcs(hed(smd_tags_gTxt('sync_import_opts'), ' class="pref-heading"'), 4)); // Assign to category if ($clink) { if ($rsCats) { $catlist = tag(treeSelectInput('smd_tags_import_cat_parent', $rsCats, $smd_tags_import_cat_parent, 'smd_tags_import_cat_parent', 35), 'td', ' colspan="3"'); } else { $catlist = tag(smd_tags_gTxt('no_parent'), 'td', ' colspan="3"'); } echo tr(tda('', $lblStyle) . $catlist); echo tr(tda('', $lblStyle) . td(checkbox('smd_tags_import_force_cat', 1, $smd_tags_import_force_cat))); } // Assign to parent tag echo tr(tda('', $lblStyle). td('
')); echo tr(tda('', $lblStyle) . td(checkbox('smd_tags_import_force_parent', 1, $smd_tags_import_force_parent))); // Report viewer echo tr(tda(''.smd_tags_gTxt('recent_report').'', ' class="noline"').tda(smd_tags_gTxt('import_results_pt1') . ' ' . smd_tags_gTxt('import_results_pt2') . '', ' class="noline"')); } else { echo tr(td(smd_tags_gTxt('sync_plugs_not_installed'))); } echo tr(tda($btnSyncGo, $btnStyle)); 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 = '