From: Penny Leach Date: Fri, 4 Aug 2006 21:22:58 +0000 (+1200) Subject: added freetag plugin X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=3aa507ae41728a9f958cb710e26898eb65c57553;p=s9y.git added freetag plugin --- diff --git a/plugins/serendipity_event_freetag/lang_en.inc.php b/plugins/serendipity_event_freetag/lang_en.inc.php new file mode 100644 index 0000000..3b1ac2e --- /dev/null +++ b/plugins/serendipity_event_freetag/lang_en.inc.php @@ -0,0 +1,59 @@ + + * EN-Revision: Revision of lang_en.inc.php + */ + +// +// serendipity_event_freetag.php +// +@define('PLUGIN_EVENT_FREETAG_TITLE', 'Tagging of entries'); +@define('PLUGIN_EVENT_FREETAG_DESC', 'Allows freestyle tagging of entries'); +@define('PLUGIN_EVENT_FREETAG_ENTERDESC', 'Enter any tags that apply. Seperate multiple tags with a comma (,)'); +@define('PLUGIN_EVENT_FREETAG_LIST', 'Defined tags for this entry: %s'); +@define('PLUGIN_EVENT_FREETAG_USING', 'Entries tagged as %s'); +@define('PLUGIN_EVENT_FREETAG_SUBTAG', 'Tags related to tag %s'); +@define('PLUGIN_EVENT_FREETAG_NO_RELATED','No related tags.'); +@define('PLUGIN_EVENT_FREETAG_ALLTAGS', 'All defined Tags'); +@define('PLUGIN_EVENT_FREETAG_MANAGETAGS', 'Manage Tags'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_ALL', 'Manage All Tags'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_LEAF', 'Manage \'Leaf\' Tags'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_UNTAGGED', 'List Untagged Entries'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_LEAFTAGGED', 'List \'Leaf\' Tagged Entries'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_UNTAGGED_NONE', 'No Untagged entries!'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_LIST_TAG', 'Tag'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_LIST_WEIGHT', 'Weight'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_LIST_ACTIONS', 'Action'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_ACTION_RENAME', 'Rename'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_ACTION_SPLIT', 'Split'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_ACTION_DELETE', 'Delete'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_CONFIRM_DELETE', 'Do you really want to delete the %s tag?'); +@define('PLUGIN_EVENT_FREETAG_MANAGE_INFO_SPLIT', 'use a comma to seperate tags:'); +@define('PLUGIN_EVENT_FREETAG_SHOW_TAGCLOUD', 'Show tag cloud to related tags?'); + +// +// serendipity_plugin_freetag.php +// +@define('PLUGIN_FREETAG_NAME', 'Show tagged entries'); +@define('PLUGIN_FREETAG_BLAHBLAH', 'Shows a list of existing tags for entries'); +@define('PLUGIN_FREETAG_NEWLINE', 'Linefeed after each Tag?'); +@define('PLUGIN_FREETAG_XML', 'Show XML-icons?'); +@define('PLUGIN_FREETAG_SCALE','Scale tag font size depending on popularity (like Technorati, flickr)?'); +@define('PLUGIN_FREETAG_UPGRADE1_2','Upgrading %d tags for entry number: %d'); +@define('PLUGIN_FREETAG_MAX_TAGS', 'How many tags should be shown?'); +@define('PLUGIN_FREETAG_TRESHOLD_TAG_COUNT', 'How many occurences must a tag have in order to be shown?'); + +@define('PLUGIN_EVENT_FREETAG_TAGCLOUD_MIN', 'Minimum font size % of tag in tag cloud'); +@define('PLUGIN_EVENT_FREETAG_TAGCLOUD_MAX', 'Maximum font size % of tag in tag cloud'); + +@define('PLUGIN_FREETAG_META_KEYWORDS', 'Number of meta keywords to embed in HTML source (0: disabled)'); + +@define('PLUGIN_EVENT_FREETAG_RELATED_ENTRIES', 'Related entries by tags:'); +@define('PLUGIN_EVENT_FREETAG_SHOW_RELATED','Display related entries by tags?'); +@define('PLUGIN_EVENT_FREETAG_SHOW_RELATED_COUNT','How many related entries should be dislayed?'); +@define('PLUGIN_EVENT_FREETAG_EMBED_FOOTER', 'Show tags in footer?'); +@define('PLUGIN_EVENT_FREETAG_EMBED_FOOTER_DESC', 'If enabled, the tags will be shown in the footer of an entry. If disabled, the tags will be put inside the body/extended part of your entries.'); +@define('PLUGIN_EVENT_FREETAG_LOWERCASE_TAGS', 'Lowercase tags'); + diff --git a/plugins/serendipity_event_freetag/serendipity_event_freetag.php b/plugins/serendipity_event_freetag/serendipity_event_freetag.php new file mode 100644 index 0000000..7fe088e --- /dev/null +++ b/plugins/serendipity_event_freetag/serendipity_event_freetag.php @@ -0,0 +1,1422 @@ +add('name', PLUGIN_EVENT_FREETAG_TITLE); + $propbag->add('description', PLUGIN_EVENT_FREETAG_DESC); + $propbag->add('stackable', false); + $propbag->add('author', 'Garvin Hicking, Jonathan Arkell'); + $propbag->add('requirements', array( + 'serendipity' => '0.8', + 'smarty' => '2.6.7', + 'php' => '4.1.0' + )); + $propbag->add('version', '2.48'); + $propbag->add('event_hooks', array( + 'frontend_fetchentries' => true, + 'frontend_fetchentry' => true, + 'frontend_display:rss-2.0:per_entry' => true, + 'frontend_header' => true, +// 'frontend_display:rss-0.92:per_entry' => true, + 'frontend_display:rss-1.0:per_entry' => true, +// 'frontend_display:rss-0.91:per_entry' => true, + 'frontend_display:atom-0.3:per_entry' => true, + 'frontend_display:atom-1.0:per_entry' => true, + 'frontend_entryproperties' => true, + 'frontend_rss' => true, + 'entry_display' => true, + 'entries_header' => true, + 'backend_publish' => true, + 'backend_save' => true, + 'backend_display' => true, + 'backend_sidebar_entries' => true, + 'backend_sidebar_entries_event_display_managetags' => true, + 'backend_delete_entry' => true, + 'external_plugin' => true, + 'xmlrpc_updertEntry' => true, + 'xmlrpc_fetchEntry' => true, + 'xmlrpc_deleteEntry' => true, + 'css' => true + )); + $propbag->add('groups', array('BACKEND_EDITOR')); + $this->supported_properties = array('freetag_name', 'freetag_tagList'); + $this->dependencies = array('serendipity_plugin_freetag' => 'keep'); + $propbag->add('configuration', array('embed_footer', 'show_tagcloud', 'min_percent', 'max_percent', 'meta_keywords', 'show_related', 'show_related_count', 'lowercase_tags')); + } + + function introspect_config_item($name, &$propbag) + { + switch($name) { + case 'show_tagcloud': + $propbag->add('type', 'boolean'); + $propbag->add('name', PLUGIN_EVENT_FREETAG_SHOW_TAGCLOUD); + $propbag->add('description', ''); + $propbag->add('default', true); + break; + + case 'embed_footer': + $propbag->add('type', 'boolean'); + $propbag->add('name', PLUGIN_EVENT_FREETAG_EMBED_FOOTER); + $propbag->add('description', PLUGIN_EVENT_FREETAG_EMBED_FOOTER_DESC); + $propbag->add('default', true); + break; + + case 'min_percent': + $propbag->add('type', 'string'); + $propbag->add('name', PLUGIN_EVENT_FREETAG_TAGCLOUD_MIN); + $propbag->add('description', ''); + $propbag->add('default', '100'); + break; + + case 'max_percent': + $propbag->add('type', 'string'); + $propbag->add('name', PLUGIN_EVENT_FREETAG_TAGCLOUD_MAX); + $propbag->add('description', ''); + $propbag->add('default', '300'); + break; + + case 'meta_keywords': + $propbag->add('type', 'string'); + $propbag->add('name', PLUGIN_FREETAG_META_KEYWORDS); + $propbag->add('description', ''); + $propbag->add('default', '0'); + break; + + case 'show_related': + $propbag->add('type', 'boolean'); + $propbag->add('name', PLUGIN_EVENT_FREETAG_SHOW_RELATED); + $propbag->add('description', ''); + $propbag->add('default', true); + break; + + case 'show_related_count': + $propbag->add('type', 'string'); + $propbag->add('name', PLUGIN_EVENT_FREETAG_SHOW_RELATED_COUNT); + $propbag->add('description', ''); + $propbag->add('default', '5'); + break; + + case 'lowercase_tags': + $propbag->add('type', 'boolean'); + $propbag->add('name', PLUGIN_EVENT_FREETAG_LOWERCASE_TAGS); + $propbag->add('description', ''); + $propbag->add('default', true); + } + return true; + } + + function generate_content(&$title) { + $title = $this->title; + } + + function tableCreated() { + global $serendipity; + + $q = "select count(tag) from {$serendipity['dbPrefix']}entrytags"; + $row = serendipity_db_query($q, true, 'num'); + + if (!is_numeric($row[0])) { // if the response we got back was an SQL error.. :P + return false; + } else { + return true; + } + } + + function upgradeFromVersion1() { + global $serendipity; + + $q = "select count(*) from {$serendipity['dbPrefix']}entryproperties where property = 'ep_freetag_name'"; + $result = serendipity_db_query($q); + + if ((int)$result[0] > 0) { + return true; + } else { + return false; + } + } + + function convertEntryPropertiesTags() { + global $serendipity; + + $q = "select entryid, value from {$serendipity['dbPrefix']}entryproperties where property = 'ep_freetag_name'"; + $result = serendipity_db_query($q); + + if (!is_array($result)) { + return false; + } + + foreach($result as $entry) { + $tags = serendipity_event_freetag::makeTagsFromTaglist($entry['value']); + serendipity_event_freetag::addTagsToEntry($entry['entryid'], $tags); + + printf(PLUGIN_FREETAG_UPGRADE1_2, count($tags), $entry['entryid']); + echo '
'; + } + + $q = "delete from {$serendipity['dbPrefix']}entryproperties where property = 'ep_freetag_name'"; + $result = serendipity_db_query($q); + } + + function cleanup() { + global $serendipity; + + serendipity_event_freetag::install(); + } + + function install() { + global $serendipity; + + if (!serendipity_event_freetag::tableCreated()) { + $q = "create table {$serendipity['dbPrefix']}entrytags (" . + "entryid int(10) not null, " . + "tag varchar(50) not null, " . + "primary key (entryid, tag)" . + ")"; + + $result = serendipity_db_schema_import($q); + + if ($result !== true) { + return; + } + + serendipity_db_schema_import("CREATE INDEX tagsentryindex ON {$serendipity['dbPrefix']}entrytags (entryid)"); + serendipity_db_schema_import("CREATE INDEX tagsTagIndex ON {$serendipity['dbPrefix']}entrytags (tag)"); + } + + if (serendipity_event_freetag::upgradeFromVersion1()) { + serendipity_event_freetag::convertEntryPropertiesTags(); + } else { + echo "NOT UPGRADING!"; + } + } + + function getTagHtmlFromCSV($tagString) { + global $serendipity; + + $links = array(); + if (empty($tagString)) { + return array(); + } + $tags = explode(',', $tagString); + $taglink = $serendipity['baseURL'] . ($serendipity['rewrite'] == 'none' ? $serendipity['indexFile'] . '?/' : '') . 'plugin/tag/'; + foreach($tags as $tag) { + $tag = trim($tag); + if (empty($tag)) { + continue; + } + $links[] = ''; + } + + return implode(', ', $links); + } + + function getTagHtml($tags) { + global $serendipity; + + $links = array(); + $taglink = $serendipity['baseURL'] . ($serendipity['rewrite'] == 'none' ? $serendipity['indexFile'] . '?/' : '') . 'plugin/tag/'; + if (!is_array($tags)) { + return ''; + } + + foreach($tags as $tag) { + $tag = trim($tag); + if (empty($tag)) { + continue; + } + $links[] = ''; + } + + return implode(', ', $links); + } + + function getRelatedEntries($tags, $postID) { + global $serendipity; + + if (!is_array($tags)) { + return false; + } + + $q = "SELECT DISTINCT e1.entryid, + e2.title + FROM {$serendipity['dbPrefix']}entrytags AS e1 + LEFT JOIN {$serendipity['dbPrefix']}entries AS e2 + ON e1.entryid = e2.id + WHERE e1.tag IN ('" . implode("', '", $tags) . "') + AND e1.entryid != " . (int)$postID . " + AND e2.isdraft = 'false' + " . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e2.timestamp <= " . time() : '') . " + LIMIT " . $this->get_config('show_related_count', 10); + + $result = serendipity_db_query($q, false, 'assoc', false, 'entryid', 'title'); + + if (!is_array($result)) { + return false; + } + + return $result; + } + + function getRelatedEntriesHtml(&$entries) { + global $serendipity; + + if (!is_array($entries)) { + return false; + } + + $entrylink = $serendipity['baseURL'] . ($serendipity['rewrite'] == 'none' ? $serendipity['indexFile'] . '?/' : '/'); + + $return = ''; + 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) + { + global $serendipity; + + if (!is_array($tags)) { + return false; + } + + $taglink = $serendipity['baseURL'] . ($serendipity['rewrite'] == 'none' ? $serendipity['indexFile'] . '?/' : '') . 'plugin/tag/'; + $rsslink = $serendipity['serendipityHTTPPath'] . 'rss.php?serendipity[tag]='; + $xmlImg = serendipity_getTemplateFile('img/xml.gif'); + + $first = true; + $biggest = max($tags); + $smallest= min($tags) - 1; + $scale = $biggest - $smallest; + + if ($scale < 0) { + $scale = 1; + } + + foreach($tags AS $name => $quantity) { + if (empty($name)) { + continue; + } + + if (!$first && !$nl && !$scaling) { + echo ', '; + } + + if ($scaling) { + $fontSize = round((($maxSize - $minSize) * $quantity) / $scale) + $minSize; + echo ''; + } + + echo ''; + + if ($scaling) { + echo ''; + } + + if ($xml) { + echo ''; + } + + if ($nl) { + echo '
' . "\n"; + } else { + echo ' '; + } + + $first = false; + } + } + + 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': + $this->displayMetaKeywords($serendipity['GET']['id'] ); + 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('&', '&', $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; + + 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('X-FreeTag: not found'); + } else { + @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 (!isset($serendipity['POST']['properties']) || !is_array($serendipity['POST']['properties']) || !isset($eventData['id'])) { + return true; + } + + $tags = $this->makeTagsFromTagList($serendipity['POST']['properties']['freetag_tagList']); + if (!empty($tags)) { + $serendipity['POST']['properties']['freetag_tagList'] = implode(',', $tags); + + $this->deleteTagsForEntry($eventData['id']); + $this->addTagsToEntry($eventData['id'], $tags); + } + + return true; + break; + + case 'backend_display': + 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(); +?> + +
    + +
    + $count) { + if (substr($tag, 0, 1) != $lastletter) + echo " |".strtoupper(substr($tag, 0, 1)).': '; + echo "$tag, "; + $lastletter = substr($tag, 0, 1); + } +?> +
    + +
    + +
    +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'])); + } + + if (is_string($show_tag) && serendipity_db_bool($this->get_config('lowercase_tags', true))) { + if (function_exists("mb_strtolower")) { + $showtag = mb_strtolower($showtag); + } else { + $showtag = strtolower($showtag); + } + } + + if (!empty($showtag)) { + if (is_string($showtag)) { + $join = "INNER JOIN {$serendipity['dbPrefix']}entrytags AS entrytags ON (e.id = entrytags.entryid) "; + $cond = "entrytags.tag = '$showtag' "; + } else if (is_array($showtag)) { + $taglist = implode('\',\'', $showtag); // outputs tag','tag2','tag3 + $total = count($showtag); + $join = "INNER JOIN {$serendipity['dbPrefix']}entrytags AS entrytags ". + "ON e.id = entrytags.entryid "; + $cond = "(entrytags.tag IN ('$taglist'))"; + $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; + } + + 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 ($this->displayTag !== false && serendipity_db_bool($this->get_config('show_tagcloud'))) { + $this->displayTagCloud(); + } + + 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); + @header('X-FreeTag-Count: Array'); + } else { + @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; + $msg = '
    ' . PLUGIN_EVENT_FREETAG_LIST . '
    '; + + if (serendipity_db_bool($this->get_config('embed_footer'))) { + $field = 'add_footer'; + } else { + if (strlen($eventData[0]['extended']) > 0) { + $field = 'extended'; + } else { + $field = 'body'; + } + + } + + if ($addData['extended']) { + $key =& $this->getFieldReference($field, $eventData[0]); + + $tags = $this->getTagsForEntries(array($eventData[0]['id'])); + $key .= sprintf($msg, $this->getTagHtml($tags[$eventData[0]['id']])); + if (serendipity_db_bool($this->get_config('show_related', true))) { + $eventData[0]['add_footer'] .= $this->getRelatedEntriesHtml($this->getRelatedEntries($tags[$eventData[0]['id']], $eventData[0]['id'])); + } + } else if ($addData['preview']) { + $eventData[0]['exflag'] = 1; + $eventData[0][$field] .= sprintf($msg, $this->getTagHtmlFromCSV($serendipity['POST']['properties']['freetag_tagList'])); + if (serendipity_db_bool($this->get_config('show_related', true))) { + $ctags = explode(',', $serendipity['POST']['properties']['freetag_tagList']); + $eventData[0]['add_footer'] .= $this->getRelatedEntriesHtml($this->getRelatedEntries($ctags, $eventData[0]['id'])); + } + } else { + $elements = count($eventData); + $skeys = array(); + for ($i = 0; $i < $elements; $i++) { + if (empty($eventData[$i]['properties']['freetag_tags'])) { + continue; + } + + if (!isset($eventData[$i]['add_footer'])) { + $eventData[$i]['add_footer'] = ''; + } + + $cont = sprintf($msg, $this->getTagHtml($eventData[$i]['properties']['freetag_tags'])); + if ($field == 'add_footer') { + $skeys[$i] =& $eventData[$i]['add_footer']; + } elseif (strlen($eventData[$i]['extended']) > 0) { + $skeys[$i] =& $this->getFieldReference('extended', $eventData[$i]); + } else { + $skeys[$i] =& $this->getFieldReference('body', $eventData[$i]); + } + $skeys[$i] .= $cont; + } + } + } + + /** + * 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']; + } + + return $memo; + } + + function displayTagCloud() + { + $tags = $this->getTagCloudTags(); + + echo '
    '; + + echo '

    '; + $tagTitle = is_array($this->displayTag) ? implode(' + ',$this->displayTag) : $this->displayTag; + printf ($title, $tagTitle); + echo '

    '; + echo '
    '; + if (!empty($tags)) { + $min = $this->get_config('min_percent', 100); + $max = $this->get_config('max_percent', 300); + serendipity_event_freetag::displayTags($tags, false, false, true, $max, $min); + } else { + echo PLUGIN_EVENT_FREETAG_NO_RELATED; + } + echo '
    '; + echo '
    '; + } + + function getTagCloudTags() { + $rows = serendipity_db_query($this->getTagCloudQuery()); + + foreach((array)$rows as $r) { + $tags[$r['tag']] = $r['total']; + } + return $tags; + } + + /* Todo: + - turn use of instance var to parameter */ + function getTagCloudQuery() + { + global $serendipity; + if ($this->displayTag === 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($this->displayTag)) { + $cond = "main.tag = '$this->displayTag'"; + $ncond = "neg.tag != '$this->displayTag'"; + $join = "LEFT JOIN {$serendipity['dbPrefix']}entrytags AS neg ". + "ON main.entryid = neg.entryid "; + $totalModifier = ''; + } else if (is_array($this->displayTag)) { + $join = "LEFT JOIN {$serendipity['dbPrefix']}entrytags AS neg ". + "ON main.entryid = neg.entryid "; + $ccond = ''; + $ncond = ''; + + $first = true; + $total = count($this->displayTag); + + $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 = '{$this->displayTag[$i]}' "; + $ncond .= "neg.tag != '{$this->displayTag[$i]}' "; + } + } else { + echo "FATAL ERROR! Unrecognized type for serendipity_event_freetag::$displayTag !"; + } + $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"; + } + return $q; + } + + /* Todo: + - turn use of instance var to parameter */ + function displayMetaKeywords($id = null) + { + global $serendipity; + $max_keywords = (int)$this->get_config('meta_keywords', 0); + if ($max_keywords < 1) { + return; + } + if ($this->displayTag !== false && $this->displayTag !== true) { //show related tags + $query = $this->getTagCloudQuery(); + $query = $query . " ORDER BY total DESC LIMIT " . $max_keywords; + } 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 + 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'])) { + 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 total <= $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; + + $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) { + $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)."\n"); + } + return $out; + } + + function displayManageTags($event, &$bag, &$eventData, $addData) { + ?> +
    + +
    + eventData['GET']['tagaction'])) { + $this->displayTagAction($this->eventData); + } + + switch(@$this->eventData['GET']['tagview']) { + case "entryuntagged": + $this->displayUntaggedEntries(); + break; + + case "entryleaf": + $this->displayLeafTaggedEntries(); + break; + + case "all": + $tags = (array)$this->getAllTags(); + $this->displayEditTags($this->eventData, $tags); + break; + + case "leaf": + $tags = $this->getLeafTags(); + $this->displayEditTags($this->eventData, $tags); + break; + + default: + echo "Tag Administration"; + if (!empty($this->eventData['GET']['tagview'])) { + echo "Can't execute {$this->eventData['GET']['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 isnull(entryid) + GROUP BY e.id"; + + $this->displayEditEntries($q); + } + + function displayLeafTaggedEntries() { + global $serendipity; + + $q = "SELECT e.id as id, e.title as title, count(*) as total + FROM ${serendipity['dbPrefix']}entries AS e + LEFT JOIN ${serendipity['dbPrefix']}entrytags AS t + ON e.id = t.entryid + GROUP BY e.id + 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 displayEditTags(&$eventData, $taglist) { + $url = FREETAG_MANAGE_URL . "&serendipity[tagview]=".$this->eventData['GET']['tagview']; +?> + + + + + + + $weight) { +?> + + + + + + +
    + + + +
    +eventData['GET']['tag']); + $action = urldecode($this->eventData['GET']['tagaction']); + if (!in_array($this->eventData['GET']['tagaction'], $validActions)) + exit ("DON'T HACK!"); + + if ($this->eventData['GET']['commit'] == 'true') { + $method = 'get'.$this->eventData['GET']['tagaction'].'TagQuery'; + $q = $this->$method($tag, $this->eventData); + echo $this->eventData['GET']['tagaction'] . " Completed"; + } else { + $method = 'display'.$this->eventData['GET']['tagaction']."Tag"; + $this->$method($tag, $this->eventData); + } + } + + function getManageUrlAsHidden(&$eventData) { + return ' + '; + } + + function displayRenameTag($tag, &$eventData) { +?> +
    + getManageUrlAsHidden($this->eventData) ?> + + + + + => +
    +eventData['GET']['tagview']; + $yes = FREETAG_MANAGE_URL . "&serendipity[tagview]=".$this->eventData['GET']['tagview']. + "&serendipity[tagaction]=delete". + "&serendipity[tag]=".urlencode($tag)."&serendipity[commit]=true"; +?> +

    +

       

    + +
    + getManageUrlAsHidden($this->eventData) ?> + + + + +


    + foobarbaz => foo,bar,baz

    + => + +
    +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 addToCSS(&$eventData) { + $eventData .= ' +.serendipity_freeTag +{ + margin-left: auto; + margin-right: 0px; + text-align: right; + font-size: 7pt; + display: block; + margin-top: 5px; + margin-bottom: 0px; +} + +.serendipity_freeTag_related +{ + margin-left: 50px; + margin-right: 0px; + text-align: left; + font-size: small; + display: block; + margin-top: 20px; + margin-bottom: 0px; +} + +.serendipity_freetag_taglist +{ + margin: 10px; + border: 1px solid #6265F0; + padding: 5px; + background-color: #B5B8FF; + text-align: justify; +} + +.serendipity_freeTag a +{ + font-size: 7pt; + text-decoration: none; +} + +.serendipity_freeTag a:hover +{ + color: green; + text-decoration: underline; +} +img.serendipity_freeTag_xmlButton +{ + vertical-align: bottom; + display: inline; + border: 0px +} +'; + } +} + +/* vim: set sts=4 ts=4 expandtab : */ diff --git a/plugins/serendipity_event_freetag/serendipity_plugin_freetag.php b/plugins/serendipity_event_freetag/serendipity_plugin_freetag.php new file mode 100644 index 0000000..c554189 --- /dev/null +++ b/plugins/serendipity_event_freetag/serendipity_plugin_freetag.php @@ -0,0 +1,134 @@ +title = $this->get_config('title', $this->title); + + $propbag->add('name', PLUGIN_FREETAG_NAME); + $propbag->add('description', PLUGIN_FREETAG_BLAHBLAH); + $propbag->add('stackable', false); + $propbag->add('author', 'Garvin Hicking, Jonathan Arkell'); + $propbag->add('requirements', array( + 'serendipity' => '0.8', + 'smarty' => '2.6.7', + 'php' => '4.1.0' + )); + $propbag->add('version', '2.37'); + $propbag->add('groups', array('FRONTEND_ENTRY_RELATED')); + $propbag->add('configuration', array('title', 'show_xml', 'show_newline', 'scale_tag', 'max_tags', 'treshold_tag_count')); + $this->dependencies = array('serendipity_event_freetag' => 'keep'); + } + + + function introspect_config_item($name, &$propbag) + { + switch($name) { + case 'title': + $propbag->add('type', 'string'); + $propbag->add('name', TITLE); + $propbag->add('description', TITLE_FOR_NUGGET); + $propbag->add('default', PLUGIN_FREETAG_NAME); + break; + + case 'scale_tag': + $propbag->add('type', 'boolean'); + $propbag->add('name', PLUGIN_FREETAG_SCALE); + $propbag->add('description', ''); + $propbag->add('default', false); + break; + + case 'show_xml': + $propbag->add('type', 'boolean'); + $propbag->add('name', PLUGIN_FREETAG_XML); + $propbag->add('description', ''); + $propbag->add('default', true); + break; + + case 'show_newline': + $propbag->add('type', 'boolean'); + $propbag->add('name', PLUGIN_FREETAG_NEWLINE); + $propbag->add('description', ''); + $propbag->add('default', true); + break; + + case 'max_tags': + $propbag->add('type', 'string'); + $propbag->add('name', PLUGIN_FREETAG_MAX_TAGS); + $propbag->add('description', ''); + $propbag->add('default', '100'); + break; + + case 'treshold_tag_count': + $propbag->add('type', 'string'); + $propbag->add('name', PLUGIN_FREETAG_TRESHOLD_TAG_COUNT); + $propbag->add('description', ''); + $propbag->add('default', '2'); + break; + } + + return true; + } + + function generate_content(&$title) + { + global $serendipity; + + $title = $this->get_config('title', $this->title); + + if ($this->get_config('max_tags', 0) != 0) { + $limit = "LIMIT " . $this->get_config('max_tags', 0); + } else { + $limit = ''; + } + + $query = "SELECT et.tag, count(et.tag) AS total + FROM {$serendipity['dbPrefix']}entrytags AS et + LEFT OUTER JOIN {$serendipity['dbPrefix']}entries AS e + ON et.entryid = e.id + WHERE e.isdraft = 'false' " + . (!serendipity_db_bool($serendipity['showFutureEntries']) ? " AND e.timestamp <= " . time() : '') . " + GROUP BY et.tag + HAVING count(et.tag) >= " . $this->get_config('treshold_tag_count') . " + ORDER BY et.tag " . $limit; + + $rows = serendipity_db_query($query); + + if (!is_array($rows)) { + return; + } + + // not sure if we can optimize this loop... :/ + // Probably through some SQL magick. + foreach($rows as $r) { + $tags[$r['tag']] = $r['total']; + } + + $xml = serendipity_db_bool($this->get_config('show_xml')); + $nl = serendipity_db_bool($this->get_config('show_newline')); + $scaling = serendipity_db_bool($this->get_config('scale_tag')); + + serendipity_event_freetag::displayTags($tags, $xml, $nl, $scaling); + } + + function cleanup() { + global $serendipity; + + serendipity_event_freetag::install(); + } +} + +/* vim: set sts=4 ts=4 expandtab : */