';
}
return $return;
}
/* This method can be called statically.
Tags should be an array with the key being the tag name, and val being
the number of occurances. */
function displayTags($tags, $xml, $nl, $scaling, $maxSize = 200, $minSize = 100, $useFlash = false, $flashbgtrans = true, $flashtagcolor = 'ff6600', $flashbgcolor = 'ffffff', $flashwidth = 190, $flashspeed = 100)
{
global $serendipity;
if (!is_array($tags)) {
return false;
}
static $taglink = null;
if ($taglink == null) {
$taglink = $this->get_config('taglink');
}
$template = $this->get_config('template');
if (!$template) {
serendipity_event_freetag::renderTags($tags, $xml, $nl, $scaling, $maxSize, $minSize, $useFlash, $flashbgtrans, $flashtagcolor, $flashbgcolor, $flashwidth, $flashspeed, $taglink);
} else {
arsort($tags);
$tagsWithLinks = array();
foreach ($tags as $tag => $count) {
$tagsWithLinks[$tag] = array(
'count' => $count,
'href' => $taglink . serendipity_event_freetag::makeUrlTag($tag),
);
}
$serendipity['smarty']->assign('tags', $tagsWithLinks);
$template = serendipity_getTemplateFile($template, 'serendipityPath');
$serendipity['smarty']->display($template);
}
return true;
}
function renderTags($tags, $xml, $nl, $scaling, $maxSize, $minSize, $useFlash, $flashbgtrans, $flashtagcolor, $flashbgcolor, $flashwidth, $flashspeed, $taglink)
{
global $serendipity;
$rsslink = $serendipity['serendipityHTTPPath'] . 'rss.php?serendipity%5Btag%5D=';
$xmlImg = serendipity_getTemplateFile($this->get_config('xml_image','img/xml.gif'));
$first = true;
$biggest = max($tags);
$smallest= min($tags);
$scale = $biggest - $smallest;
if ($scale < 0) {
$scale = 1;
}
$key = uniqid(rand());
if ($useFlash) {
echo '
'. "\n";
echo "\n". ''. "\n";
echo '
'. "\n";
}
}
function event_hook($event, &$bag, &$eventData, $addData = null) {
global $serendipity;
$hooks = &$bag->get('event_hooks');
if (isset($hooks[$event])) {
switch($event) {
case 'backend_delete_entry':
$this->deleteTagsForEntry((int)$eventData);
return true;
case 'frontend_header':
if (serendipity_db_bool($this->get_config('use_flash'))) {
echo ''. "\n";
echo ''. "\n";
}
$this->displayMetaKeywords($serendipity['GET']['id'], $this->displayTag );
return true;
case 'frontend_display:rss-2.0:per_entry':
case 'frontend_display:rss-0.91:per_entry':
$eventData['display_dat'] .= $this->getFeedXmlForTags('category', $eventData['properties']['freetag_tags']);
return true;
case 'frontend_display:rss-1.0:per_entry':
case 'frontend_display:rss-0.91:per_entry':
case 'frontend_display:atom-0.3:per_entry':
case 'frontend_display:atom-1.0:per_entry':
$eventData['display_dat'] .= $this->getFeedXmlForTags('dc:subject', $eventData['properties']['freetag_tags']);
return true;
case 'external_plugin':
$uri_parts = explode('?', str_replace(array('&', '%FF'), array('&', '.'), $eventData));
$param = explode('/', $uri_parts[0]);
$plugincode = array_shift($param);
if (($plugincode == "tag") || ($plugincode == "tags") || ($plugincode == "freetag")) {
/* Attempt to locate hidden variables within the URI */
foreach ($serendipity['uriArguments'] as $k => $v) {
if ($v[0] == 'P') { /* Page */
$page = substr($v, 1);
if (is_numeric($page)) {
$serendipity['GET']['page'] = $page;
unset($serendipity['uriArguments'][$k]);
if ($param[count($param)-1] == "P{$page}.html") {
array_pop($param); // knock it off of the param array as well
}
}
}
}
if (count($param) == 0 || empty($param[0])) {
$serendipity['head_subtitle'] = PLUGIN_EVENT_FREETAG_ALLTAGS;
$this->displayTag = true;
$param = null;
} else if (count($param) == 1) {
$param = urldecode($param[0]);
$serendipity['head_subtitle'] = sprintf(PLUGIN_EVENT_FREETAG_USING, htmlspecialchars($param));
$emit_404 = true;
} else {
$serendipity['head_subtitle'] = sprintf(PLUGIN_EVENT_FREETAG_USING, implode(' + ', array_map('htmlspecialchars', $param)));
$param = array_map('urldecode', $param);
$emit_404 = true;
}
$this->tags['show'] = $param;
$serendipity['plugin_vars']['tag'] = $param;
if (is_array($param)) {
@define('PLUGIN_VARS_TAG', implode(',', $param));
} else {
@define('PLUGIN_VARS_TAG', $param);
}
$serendipity['GET']['subpage'] = $eventData;
unset($serendipity['GET']['category']); // No restriction should be enforced here.
include_once(S9Y_INCLUDE_PATH . 'include/genpage.inc.php');
if ($emit_404 && $this->TaggedEntries !== null && $this->TaggedEntries < 1) {
@header('HTTP/1.0 404 Not found');
@header('Status: 404 Not found');
if (serendipity_db_bool($this->get_config('send_http_header', true))) {
@header('X-FreeTag: not found');
}
} else {
if (serendipity_db_bool($this->get_config('send_http_header', true))) {
@header('X-FreeTag: ' . $this->TaggedEntries);
}
}
$raw_data = ob_get_contents();
ob_end_clean();
$serendipity['smarty']->assign('raw_data', $raw_data);
serendipity_gzCompression();
$serendipity['smarty']->display(serendipity_getTemplateFile($serendipity['smarty_file'], 'serendipityPath'));
@define('NO_EXIT', true);
}
break;
case 'backend_sidebar_entries':
?>
eventData = $eventData;
$this->displayManageTags($event, $bag, $eventData, $addData);
return true;
break;
case 'backend_publish':
case 'backend_save':
if (function_exists('mb_internal_encoding')) {
mb_internal_encoding(LANG_CHARSET);
}
if (!isset($serendipity['POST']['properties']) || !is_array($serendipity['POST']['properties']) || !isset($eventData['id'])) {
return true;
}
$to_lower = serendipity_db_bool($this->get_config('lowercase_tags'));
$keylist = serendipity_db_query("SELECT tag, keywords FROM {$serendipity['dbPrefix']}tagkeywords", false, 'assoc');
$automatted = array(array());
if (is_array($keylist)) {
foreach($keylist AS $key) {
$keywords = explode(',', $key['keywords']);
foreach($keywords AS $keyword) {
$automatted[trim($keyword)][$key['tag']] = true;
}
}
}
$tags = $this->makeTagsFromTagList($serendipity['POST']['properties']['freetag_tagList']);
if (serendipity_db_bool($this->get_config('keyword2tag'))) {
$searchtext = strip_tags($eventData['body'] . $eventData['extended']);
foreach($automatted AS $keyword => $ktags) {
$keyword = trim($keyword);
if (empty($keyword)) continue;
if (!is_array($ktags) || count($ktags) < 1) continue;
$regex = sprintf("/((\s+|[\(\[-]+)%s([-\/,\.\?!'\";\)\]]*+|[\/-]+))/i", $keyword);
if (preg_match($regex, $searchtext) > 0) {
foreach($ktags AS $tag => $is_assigned) {
if (!is_array($tags) || (!in_array(strtolower($tag), $tags) && !in_array($tag, $tags))) {
if ($to_lower) {
if (function_exists("mb_strtolower")) {
$tag = mb_strtolower($tag);
} else {
$tag = strtolower($tag);
}
}
$tags[] = $tag;
printf(PLUGIN_EVENT_FREETAG_KEYWORDS_ADD, htmlspecialchars($keyword), htmlspecialchars($tag));
}
}
}
}
}
if (!empty($tags)) {
if (serendipity_db_bool($this->get_config('cat2tag'))) {
if (is_array($cats = serendipity_fetchCategories())) {
$cats = serendipity_walkRecursive($cats, 'categoryid', 'parentid', VIEWMODE_THREADED);
foreach ($cats as $cat) {
if ($to_lower) {
if (function_exists("mb_strtolower")) {
$cat['category_name'] = mb_strtolower($cat['category_name']);
} else {
$cat['category_name'] = strtolower($cat['category_name']);
}
}
$names = explode(',', $cat['category_name']);
foreach($names AS $name) {
$name = trim($name);
if (in_array($cat['categoryid'], $eventData['categories']) && !in_array($name, $tags)) {
$tags[] = $name;
}
}
}
}
}
$serendipity['POST']['properties']['freetag_tagList'] = implode(',', $tags);
$this->deleteTagsForEntry($eventData['id']);
$this->addTagsToEntry($eventData['id'], $tags);
} else {
$this->deleteTagsForEntry($eventData['id']);
}
return true;
break;
case 'backend_display':
if (function_exists('mb_internal_encoding')) {
mb_internal_encoding(LANG_CHARSET);
}
if (isset($serendipity['POST']['properties']['freetag_tagList'])) {
$tagList = $serendipity['POST']['properties']['freetag_tagList'];
} else if (isset($eventData['id'])) {
$tagList = implode(',', $this->getTagsForEntry($eventData['id']));
} else {
$tagList = '';
}
if (serendipity_db_bool($this->get_config('lowercase_tags', true))) {
if (function_exists("mb_strtolower")) {
$tagList = mb_strtolower($tagList);
} else {
$tagList = strtolower($tagList);
}
}
$freetags = $this->makeTagsFromTagList($tagList);
if (!empty($freetags)) {
$tagList = implode(',', $freetags);
}
$taglist = (array)$this->getAllTags();
if ($this->get_config('admin_ftayt')) {
foreach ($taglist as $k => $v) {
$wicktags[] = '\'' . addslashes($k) . '\'';
}
echo '
';
}
if ($this->get_config('admin_show_taglist')) {
?>
importEntryTagsIntoProperties($eventData, $addData);
return true;
break;
case 'frontend_fetchentries':
case 'frontend_fetchentry':
if (!empty($this->tags['show'])) {
if (is_array($this->tags['show'])) {
$showtag = array_map('serendipity_db_escape_string', $this->tags['show']);
} else {
$showtag = serendipity_db_escape_string($this->tags['show']);
}
} else if (!empty($serendipity['GET']['tag'])) {
$showtag = serendipity_db_escape_string(urldecode($serendipity['GET']['tag']));
}
$arr_showtag = explode(';', $showtag);
$multimode = 'and';
if (count($arr_showtag) > 1) {
$showtag = $arr_showtag;
$multimode = 'or';
}
if (!empty($show_tag) && is_string($show_tag) && serendipity_db_bool($this->get_config('lowercase_tags', true))) {
if (function_exists("mb_strtolower")) {
if (function_exists('mb_internal_encoding')) {
mb_internal_encoding(LANG_CHARSET);
}
$showtag = mb_strtolower($showtag);
} else {
$showtag = strtolower($showtag);
}
}
$coll_target = $this->get_config('collation', '');
if (empty($coll_target) && stristr($serendipity['dbType'], 'mysql') ) {
$cd = serendipity_db_query("SHOW FULL COLUMNS FROM {$serendipity['dbPrefix']}entrytags LIKE 'tag'");
if (!empty($cd[0]['Collation'])) {
$coll_target = $cd[0]['Collation'];
$this->set_config('collation', $coll_target);
}
}
if (!empty($showtag)) {
if (LANG_CHARSET == 'UTF-8' && stristr($serendipity['dbType'], 'mysql') && !stristr($coll_target, 'utf8')) {
$collate = "COLLATE utf8_general_ci";
$collateP = '_utf8 ';
} else {
$collate = $collateP = "";
}
if (is_string($showtag)) {
$join = "INNER JOIN {$serendipity['dbPrefix']}entrytags AS entrytags ON (e.id = entrytags.entryid) ";
$cond = "entrytags.tag = $collateP '$showtag' $collate";
} else if (is_array($showtag)) {
$_taglist = array();
$cond = '(1=2 ';
foreach($showtag AS $_showtag) {
$_taglist[] = serendipity_db_escape_string($_showtag);
$cond .= " OR entrytags.tag = $collateP '" . serendipity_db_escape_string($_showtag) . "' $collate ";
}
$cond .= ' ) ';
$total = count($showtag);
$join = "INNER JOIN {$serendipity['dbPrefix']}entrytags AS entrytags ".
"ON e.id = entrytags.entryid ";
if ($multimode == 'and') {
$eventData['having'] = " HAVING count(entrytags.tag) = $total";
}
}
if (empty($eventData['and'])) {
$eventData['and'] = " WHERE $cond ";
} else {
$eventData['and'] .= " AND $cond ";
}
if (empty($eventData['joins'])) {
$eventData['joins'] = $join;
} else {
$eventData['joins'] .= $join;
}
$this->displayTag = $showtag;
$serendipity['plugin_vars']['displayTag'] = $showtag;
@define('PLUGIN_VARS_DISPLAYTAG', $showtag);
}
return true;
break;
case 'frontend_rss':
if (!empty($this->displayTag)) {
$eventData['title'] .= serendipity_utf8_encode(htmlspecialchars(' (' . sprintf(PLUGIN_EVENT_FREETAG_USING, $this->displayTag) . ')'));
}
return true;
break;
case 'entries_header':
if (isset($eventData['plugin_vars']['tag']) && serendipity_db_bool($this->get_config('show_tagcloud'))) {
$this->displayTagCloud($eventData['plugin_vars']['tag']);
}
return true;
break;
case 'css':
if (strpos($eventData, '.serendipity_freeTag')) {
// class exists in CSS, so a user has customized it and we don't need default
return true;
}
$this->addToCSS($eventData);
return true;
break;
case 'entry_display':
// Don't display entries if we are getting a full tag list
if (is_array($eventData)) {
$this->TaggedEntries = count($eventData);
if (serendipity_db_bool($this->get_config('send_http_header', true))) {
@header('X-FreeTag-Count: Array');
}
} else {
if (serendipity_db_bool($this->get_config('send_http_header', true))) {
@header('X-FreeTag-Count: Empty');
}
$this->TaggedEntries = 0;
}
if ($this->displayTag === true) {
$eventData['clean_page'] = true;
return true;
}
// This falls into the default case, which returns false... Is this supposed to happen?
// Is it a bug?
// Is it a feature?
$this->displayEntry($eventData, $addData);
return true;
case 'xmlrpc_updertEntry':
if (isset($eventData['id']) && isset($eventData['mt_keywords'])) {
//XMLRPC call
$tags = $this->makeTagsFromTagList($eventData['mt_keywords']);
if (!empty($tags)) {
$this->deleteTagsForEntry($eventData['id']);
$this->addTagsToEntry($eventData['id'], $tags);
}
}
return true;
break;
case 'xmlrpc_fetchEntry':
$eventData['mt_keywords']=implode(',', $this->getTagsForEntry($eventData['id']));
return true;
break;
case 'xmlrpc_deleteEntry':
if (isset($eventData["id"])) {
$this->deleteTagsForEntry($eventData["id"]);
}
return true;
break;
default:
return false;
break;
}
} else {
return false;
}
}
function displayEntry(&$eventData, $addData)
{
global $serendipity;
//echo real smarty-elements:
$show_related = serendipity_db_bool($this->get_config('show_related', true));
$elements = count($eventData);
//If not using extended-smarty, we want related entries only when
//showing only one entry
if ($elements > 1) {
$manyEntries = true;
} else {
$manyEntries = false;
}
for ($entry = 0; $entry < $elements; $entry++) {
$tags = $this->getTagsForEntry($eventData[$entry]['id']);
//when in preview, maybe there are no tags stored yet
if ($addData['preview'] && empty($tags)) {
$tags = explode(',', $serendipity['POST']['properties']['freetag_tagList']);
}
$eventData = $this->addTags($entry, $tags, $eventData);
if ($show_related) {
$relatedEntries =& $this->getRelatedEntries($tags, $eventData[$entry]['id']);
$eventData = $this->addRelatedEntries($entry, $manyEntries, $relatedEntries, $eventData);
}
}
}
#
# Add related entries to eventData[$entry]
#
# $entry: number of entry in $eventData
# for use in displayEntry
function addRelatedEntries($entry, $manyEntries, $relatedEntries, $eventData) {
if (is_array($relatedEntries)) {
if ($this->get_config('extended_smarty', false)) {
$eventData[$entry]['freetag']['extended'] = true;
$eventData[$entry]['freetag']['related'] = $this->getRelatedEntriesHtml($relatedEntries, true);
} else if (! $manyEntries){
$field = $this->getField($eventData, $entry);
//work with getFieldReference to prevent caching-issues
$entryText =& $this->getFieldReference($field, $eventData[$entry]);
$entryText .= $this->getRelatedEntriesHtml($relatedEntries);
}
}
return $eventData;
}
#
# $entry: number of entry in $eventData
# for use in displayEntry
function addTags($entry, $tags, $eventData) {
if ($this->get_config('extended_smarty', false)) {
$eventData[$entry]['freetag']['extended'] = true;
$eventData[$entry]['freetag']['tags']['description'] = str_replace('%s', '', PLUGIN_EVENT_FREETAG_LIST);
$eventData[$entry]['freetag']['tags']['tags'] = $this->getTagHtml($tags, true);
} else {
if (!empty($tags)) {
$field = $this->getField($eventData, $entry);
$msg = '
' . PLUGIN_EVENT_FREETAG_LIST . '
';
//in preview, $eventData maybe don't contain the field - till now
if (!isset($eventData[$entry][$field])) {
$eventData[$entry][$field] = "";
}
$entryText =& $this->getFieldReference($field, $eventData[$entry]);
$entryText .= sprintf($msg, $this->getTagHtml($tags));
}
}
return $eventData;
}
function getField($eventData, $entry) {
$embed_footer = $this->get_config('embed_footer');
if ($embed_footer === 'yes' || ($embed_footer !== 'no' && serendipity_db_bool($embed_footer))) {
$field = 'add_footer';
} else if ($embed_footer === 'smarty') {
$field = 'freetag';
} else {
//The option is set to add to the entry, but where?
if (strlen($eventData[$entry]['extended']) > 0) {
$field = 'extended';
} else {
$field = 'body';
}
}
return $field;
}
/**
* Returns a list of all tags
* This performs a memoization operation, so that if we happen to be
* getting all tags more then one time per request, we only perform
* the SQL query once
*/
// static
function makeTagsFromTaglist($tagList)
{
$freetags = explode(',', $tagList);
foreach($freetags AS $idx => $tag) {
$tag = trim($tag);
if (!empty($tag)) {
$tags[] = $tag;
}
}
return $tags;
}
function getAllTags()
{
global $serendipity;
static $memo = false;
if (is_array($memo)) {
return $memo;
}
$q = "SELECT tag, count(tag) as total
FROM {$serendipity['dbPrefix']}entrytags
GROUP BY tag
ORDER BY tag";
$rows = serendipity_db_query($q);
if (!is_array($rows)) {
echo $rows;
return false;
}
$memo = array();
foreach((array)$rows as $r) {
$memo[$r['tag']] = $r['total'];
}
serendipity_plugin_api::hook_event('sort', $memo);
return $memo;
}
function displayTagCloud($tag)
{
global $serendipity;
$tags = $this->getTagCloudTags($tag);
$serendipity['smarty']->assign('freetag_tagTitle', htmlspecialchars(is_array($this->displayTag) ? implode(' + ',$this->displayTag) : $this->displayTag));
if (!empty($tags)) {
$serendipity['smarty']->assign('freetag_hasTags', true);
$min = $this->get_config('min_percent', 100);
$max = $this->get_config('max_percent', 300);
ob_start();
serendipity_event_freetag::displayTags($tags, false, false, true, $max, $min,
serendipity_db_bool($this->get_config('use_flash')),
serendipity_db_bool($this->get_config('flash_bg_trans', true)),
$this->get_config('flash_tag_color', 'ff6600'), $this->get_config('flash_bg_color', 'ffffff'),
$this->get_config('flash_width', 600), $this->get_config('flash_speed', 100));
$tagout = ob_get_contents();
ob_end_clean();
$serendipity['smarty']->assign('freetag_displayTags', $tagout);
} else {
$serendipity['smarty']->assign('freetag_hasTags', false);
}
$filename = 'plugin_freetag.tpl';
$tfile = serendipity_getTemplateFile($filename, 'serendipityPath');
if (!$tfile || $tfile == $filename) {
$tfile = dirname(__FILE__) . '/' . $filename;
}
$inclusion = $serendipity['smarty']->security_settings[INCLUDE_ANY];
$serendipity['smarty']->security_settings[INCLUDE_ANY] = true;
$content = $serendipity['smarty']->fetch('file:'. $tfile);
$serendipity['smarty']->security_settings[INCLUDE_ANY] = $inclusion;
echo $content;
}
#
# descend: if true, get the related tags of the related tags of given tag
#
function getTagCloudTags($tag, $descend = true) {
$rows = serendipity_db_query($this->getTagCloudQuery('', $tag));
if (is_array($rows)) {
foreach((array)$rows as $r) {
$tags[$r['tag']] = $r['total'];
#get also tags which are related only by other tags
if($descend) {
$descended_tags = $this->getTagCloudTags($r['tag'], false);
foreach($descended_tags AS $dtag => $value) {
$descended_tags[$dtag] = $value / 2;
}
#$tags = array_merge($tags, $descended_tags);
$tags = $tags + $descended_tags;
}
}
}
unset($tags["$tag"]);
return $tags;
}
function getTagCloudQuery($sort = '', $tag)
{
global $serendipity;
if ($tag === true) {
$title = PLUGIN_EVENT_FREETAG_ALLTAGS;
$q = "SELECT tag, count(tag) as total FROM {$serendipity['dbPrefix']}entrytags GROUP BY tag ORDER BY tag";
} else {
$title = PLUGIN_EVENT_FREETAG_SUBTAG;
if (is_string($tag)) {
$cond = "main.tag = '$tag'";
$ncond = "neg.tag != '$tag'";
$join = "LEFT JOIN {$serendipity['dbPrefix']}entrytags AS neg ".
"ON main.entryid = neg.entryid ";
$totalModifier = '';
} else if (is_array($tag)) {
$join = "LEFT JOIN {$serendipity['dbPrefix']}entrytags AS neg ".
"ON main.entryid = neg.entryid ";
$ccond = '';
$ncond = '';
$first = true;
$total = count($tag);
$totalModifier = " - $total";
for ($i = 0; $i < $total; $i++) {
if (!$first) {
$ncond .= " AND ";
$cond .= " AND ";
} else {
$first = false;
}
$join .= "LEFT JOIN {$serendipity['dbPrefix']}entrytags AS sub{$i} ".
"ON main.entryid = sub{$i}.entryid ";
$cond .= "sub{$i}.tag = '{$tag[$i]}' ";
$ncond .= "neg.tag != '{$tag[$i]}' ";
}
} else {
return;
}
$q = "SELECT neg.tag AS tag, count(neg.tag) {$totalModifier} AS total
FROM {$serendipity['dbPrefix']}entrytags AS main
{$join}
WHERE ($cond)
AND ($ncond)
GROUP BY neg.tag";
}
if (serendipity_db_bool($this->get_config('use_flash'))) {
$mt = $this->get_config('max_tags', 45);
} else {
$mt = $this->get_config('max_tags', 0);
}
if ($mt > 0 && $sort == '') {
$q = $q . " LIMIT " . $mt;
}
return $q;
}
function displayMetaKeywords($id = null, $tag) {
global $serendipity;
$id = (int)$id;
$max_keywords = (int)$this->get_config('meta_keywords', 0);
if ($max_keywords < 1) {
return;
}
if ($tag !== false && $tag !== true) { //show related tags
$query = $this->getTagCloudQuery(' ORDER BY total DESC LIMIT ' . $max_keywords, $tag);
} else if ($id == null) { // show all tags
// select most used tags in descending order
$query = "SELECT tag,
count(tag) AS total
FROM {$serendipity['dbPrefix']}entrytags
GROUP BY tag
ORDER BY total DESC
LIMIT " . $max_keywords;
} else { // show tags for entry
// select tags from entry $id ordered by most usage descending
$query = "SELECT one.tag,
two.entryid,
count(two.tag) AS total
FROM {$serendipity['dbPrefix']}entrytags
AS one
JOIN {$serendipity['dbPrefix']}entrytags AS two
ON two.entryid = " . $id . "
AND one.tag = two.tag
GROUP BY one.tag, two.entryid
ORDER BY total DESC
LIMIT " . $max_keywords;
}
$rows = serendipity_db_query($query);
if (!is_array($rows)) {
return;
}
echo "tags['show'])) {
if (is_array($this->tags['show'])) {
$not_first = false;
foreach($this->tags['show'] AS $r) {
$not_first ? print(', ') : $not_first = true;
echo $r;
}
} else {
echo $this->tags['show'];
$not_first = true;
}
}
foreach($rows AS $r) {
if (empty($r['tag'])) {
continue;
}
$not_first ? print(', ') : $not_first = true;
echo htmlspecialchars($r['tag']);
}
echo "\" />";
}
function getRelatedTags($tag) {
global $serendipity;
$q = "SELECT sub.tag AS tag, count(sub.tag) AS total
FROM {$serendipity['dbPrefix']}entrytags AS main
LEFT JOIN {$serendipity['dbPrefix']}entrytags AS sub
ON main.entryid = sub.entryid
WHERE main.tag = '$tag'
AND sub.tag != '$tag'
GROUP BY sub.tag
ORDER BY sub.tag";
$rows = serendipity_db_query($q);
if (!is_array($rows)) {
if ($rows !== true && $rows !== 1 && $rows !== 'true') {
echo $rows;
}
return array();
}
foreach($rows as $r) {
$tags[$r['tag']] = $r['total'];
}
return $tags;
}
function getLeafTags($leafWeight=1) {
global $serendipity;
$q = "SELECT tag, count(tag) as total
FROM {$serendipity['dbPrefix']}entrytags
GROUP BY tag
HAVING count(tag) <= $leafWeight
ORDER BY tag";
$rows = serendipity_db_query($q);
if (!is_array($rows)) {
echo $rows;
}
$tags = array();
foreach((array)$rows as $r) {
$tags[$r['tag']] = $r['total'];
}
return $tags;
}
function getTagsForEntries($entries) {
global $serendipity;
if (!is_array($entries) || count($entries) < 1 || implode(',', $entries) == '') {
return false;
}
$q = "SELECT entryid, tag from {$serendipity['dbPrefix']}entrytags WHERE entryid IN (".implode(',', $entries).") order by entryid, tag";
$result = serendipity_db_query($q);
if (!is_array($result)) {
return false;
}
$return = array();
foreach($result as $idx => $row) {
if (isset($row['entryid'])) {
$return[$row['entryid']][] = $row['tag'];
}
}
return $return;
}
function getTagsForEntry($entryId) {
$array = $this->getTagsForEntries(array($entryId));
return (is_array($array) ? array_pop($array) : array());
}
function deleteTagsForEntry($entryId) {
global $serendipity;
$q = "DELETE FROM {$serendipity['dbPrefix']}entrytags WHERE entryid = ".(int)$entryId;
serendipity_db_query($q);
}
// Static
function addTagsToEntry($entryId, $tags) {
global $serendipity;
if (!is_array($tags)) {
return false;
}
foreach($tags as $tag) {
$q = "INSERT INTO {$serendipity['dbPrefix']}entrytags (entryid, tag) VALUES (".(int)$entryId.", '".serendipity_db_escape_string($tag)."')";
serendipity_db_query($q);
}
}
// This may not be the right way to do this...
function importEntryTagsIntoProperties(&$eventData, $addData) {
// we do a dual loop here, which is probably the worst thing to do.
// A better thing might be some kind of array merge action, but I am not
// entirely sure how do do that with the arrays we are given.
//
// RefactorMe Later.
// Loop one in getTagsForEntries
$tagGroups = $this->getTagsForEntries(array_keys($addData));
// Loop 2
if (is_array($tagGroups)) {
foreach($tagGroups as $entryId => $tagList) {
$eventData[$addData[$entryId]]['properties']['freetag_tags'] = $tagList;
$eventData[$addData[$entryId]]['properties']['freetag_tagList'] = implode(",", $tagList);
}
}
}
function getFeedXmlForTags($element, $tagList) {
$out = '';
if (!is_array($tagList)) {
return $out;
}
foreach($tagList as $tag) {
$out .= serendipity_utf8_encode("<$element>".htmlspecialchars($tag)."$element>\n");
}
return $out;
}
function displayManageTags($event, &$bag, &$eventData, $addData) {
global $serendipity;
if ($this->get_config('dbversion', 1) != 2) {
$this->install();
$this->set_config('dbversion', 2);
}
?>
\n";
}
echo DONE . " \n";
}
}
echo ' ';
if ($to < $total) {
?>
' . DONE . '';
}
break;
case 'cat2tag':
$e = serendipity_db_query("SELECT e.id, e.title, c.category_name, et.tag
FROM {$serendipity['dbPrefix']}entries AS e
LEFT OUTER JOIN {$serendipity['dbPrefix']}entrycat AS ec
ON e.id = ec.entryid
LEFT OUTER JOIN {$serendipity['dbPrefix']}category AS c
ON ec.categoryid = c.categoryid
LEFT OUTER JOIN {$serendipity['dbPrefix']}entrytags AS et
ON e.id = et.entryid", false, 'assoc');
// Get all categories and tags of all entries
$entries = array();
foreach($e AS $row) {
$entries[$row['id']]['title'] = $row['title'];
$entries[$row['id']]['categories'][$row['category_name']] = $row['category_name'];
$entries[$row['id']]['tags'][$row['tag']] = $row['tag'];
}
// Cycle all entries
foreach($entries AS $id => $props) {
$newtags = array();
// Fetch all tags that should be added
foreach($props['categories'] AS $tag) {
if (empty($tag)) continue;
$newtags[$tag] = $tag;
}
// Subtract all tags that already exist
foreach($props['tags'] AS $tag) {
unset($newtags[$tag]);
}
if (count($newtags) < 1) {
continue;
}
$this->addTagsToEntry($id, $newtags);
printf(PLUGIN_EVENT_FREETAG_GLOBALCAT2TAG_ENTRY . ' ',
$id,
htmlspecialchars($props['title']),
htmlspecialchars(implode(', ', $newtags))
);
}
echo PLUGIN_EVENT_FREETAG_GLOBALCAT2TAG . ' ';
break;
case 'cleanupmappings':
$this->cleanupTagAssignments();
break;
default:
if (!empty($this->eventData['GET']['tagview'])) {
echo "Can't execute tagview";
}
break;
}
return true;
}
function displayUntaggedEntries() {
global $serendipity;
$q = "SELECT e.id as id, e.title as title
FROM ${serendipity['dbPrefix']}entries AS e
LEFT OUTER JOIN ${serendipity['dbPrefix']}entrytags AS t
ON e.id = t.entryid
WHERE entryid IS NULL
GROUP BY e.id, e.title";
$this->displayEditEntries($q);
}
function displayLeafTaggedEntries() {
global $serendipity;
$q = "SELECT e.id as id, e.title as title, count(t.tag) as total
FROM ${serendipity['dbPrefix']}entries AS e
LEFT JOIN ${serendipity['dbPrefix']}entrytags AS t
ON e.id = t.entryid
GROUP BY e.id, e.title
HAVING total = 1";
$this->displayEditEntries($q);
}
function displayEditEntries($q) {
global $serendipity;
$r = serendipity_db_query($q);
if ($r === true) {
echo PLUGIN_EVENT_FREETAG_MANAGE_UNTAGGED_NONE;
} else if (!is_array($r)) {
echo $r;
} else {
foreach ($r as $row) {
echo '
' . $row['title'] . '
';
}
}
}
function displayKeywordAssignment($taglist) {
global $serendipity;
if ($serendipity['POST']['keywordsubmit']) {
serendipity_db_query("DELETE FROM {$serendipity['dbPrefix']}tagkeywords WHERE tag = '" . serendipity_db_escape_string(urldecode($serendipity['POST']['tag'])) . "'");
serendipity_db_query("INSERT INTO {$serendipity['dbPrefix']}tagkeywords (tag, keywords) VALUES ('" . serendipity_db_escape_string(urldecode($serendipity['POST']['tag'])) . "', '" . serendipity_db_escape_string($serendipity['POST']['keywords']) . "')");
}
$keys = array();
$keylist = serendipity_db_query("SELECT tag, keywords FROM {$serendipity['dbPrefix']}tagkeywords ORDER BY tag");
if (is_array($keylist)) {
foreach($keylist AS $key) {
$keys[$key['tag']] = $key['keywords'];
}
}
$url = FREETAG_MANAGE_URL . "&serendipity[tagview]=".htmlspecialchars($this->eventData['GET']['tagview']);
echo ' ' . PLUGIN_EVENT_FREETAG_KEYWORDS_DESC . '
makeTagsFromTaglist(urldecode($this->eventData['GET']['newtags']));
$tag = serendipity_db_escape_string($tag);
$q = "SELECT entryid from ${serendipity['dbPrefix']}entrytags where tag = '$tag'";
$entries = serendipity_db_query($q);
if (!is_array($entries)) {
echo $entries;
return false;
}
$q = "DELETE FROM ${serendipity['dbPrefix']}entrytags where tag = '$tag'";
$r = serendipity_db_query($q);
if ($r !== true) {
echo $r;
return false;
}
foreach ($entries as $entryid) {
foreach ($newtags as $tag) {
$q = "INSERT INTO ${serendipity['dbPrefix']}entrytags (entryid, tag)
VALUES ('${entryid['entryid']}', '$tag')";
$r = serendipity_db_query($q);
}
}
}
function cleanupTagAssignments() {
global $serendipity;
echo " ";
// Search for inconsistencies
$q_search = "SELECT et.tag AS tag, et.entryid AS entryid, e.id ".
"FROM {$serendipity['dbPrefix']}entrytags AS et LEFT JOIN {$serendipity['dbPrefix']}entries AS e ".
"ON et.entryid = e.id ".
"WHERE e.id IS NULL ".
"ORDER BY et.tag ASC";
$mappings = serendipity_db_query($q_search, FALSE, 'assoc', TRUE);
if (is_array($mappings) && count($mappings) > 0) {
// Inconsistencies found
if ($this->eventData['GET']['perform'] == 'true') {
// Perform cleanup
$entryIDs = array();
foreach ($mappings as $mapping) {
if (!in_array($mapping['entryid'], array_values($entryIDs))) {
$entryIDs[] = $mapping['entryid'];
}
}
$q_cleanup = "DELETE FROM {$serendipity['dbPrefix']}entrytags " .
"WHERE entryid IN (".implode(", ", $entryIDs).")";
$cleanup = serendipity_db_query($q_cleanup);
if ($cleanup === TRUE) {
echo "".PLUGIN_EVENT_FREETAG_MANAGE_CLEANUP_SUCCESSFUL."";
}
else {
echo "".PLUGIN_EVENT_FREETAG_MANAGE_CLEANUP_FAILED."
DB-Error:".$cleanup;
}
}
else {
// Show inconsistencies
foreach ($mappings as $mapping) {
$cleanup_tags[$mapping['tag']][] = $mapping['entryid'];
}
echo PLUGIN_EVENT_FREETAG_MANAGE_CLEANUP_INFO."
";
// Display list of found inconsistencies
echo "
";
echo "
".PLUGIN_EVENT_FREETAG_MANAGE_LIST_TAG."
".PLUGIN_EVENT_FREETAG_MANAGE_CLEANUP_ENTRIES."
";
foreach ($cleanup_tags as $tag => $entries) {
echo "
$tag
".implode(', ', $entries)."
";
}
echo "
";
// Display submit form to start cleanup process
echo '';
}
}
elseif ($mappings === TRUE) {
// No inconsistencies found
echo "".PLUGIN_EVENT_FREETAG_MANAGE_CLEANUP_NOTHING."";
}
else {
// An error occures while searching for inconsistencies
echo "".PLUGIN_EVENT_FREETAG_MANAGE_CLEANUP_LOOKUP_ERROR."