diff options
Diffstat (limited to 'src/SemanticScuttle/Service/Tag2Tag.php')
-rw-r--r-- | src/SemanticScuttle/Service/Tag2Tag.php | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/src/SemanticScuttle/Service/Tag2Tag.php b/src/SemanticScuttle/Service/Tag2Tag.php new file mode 100644 index 0000000..9dddc44 --- /dev/null +++ b/src/SemanticScuttle/Service/Tag2Tag.php @@ -0,0 +1,470 @@ +<?php +/** + * SemanticScuttle - your social bookmark manager. + * + * PHP version 5. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ + +/** + * SemanticScuttle tag-to-tag service which works with tag relations. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Benjamin Huynh-Kim-Bang <mensonge@users.sourceforge.net> + * @author Christian Weiske <cweiske@cweiske.de> + * @author Eric Dane <ericdane@users.sourceforge.net> + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ +class SemanticScuttle_Service_Tag2Tag extends SemanticScuttle_DbService +{ + /** + * Returns the single service instance + * + * @param DB $db Database object + * + * @return SemanticScuttle_Service + */ + public static function getInstance($db) + { + static $instance; + if (!isset($instance)) { + $instance = new self($db); + } + return $instance; + } + + + function __construct($db) + { + $this->db =$db; + $this->tablename = $GLOBALS['tableprefix'] .'tags2tags'; + } + + /** + * Add a tag-to-tag link relation + * + * @param string $tag1 First tag + * @param string $tag2 Second tag + * @param string $relationType Relation: + * "=" (both tags are equal) + * ">" (tag2 is part of tag1) + * @param integer $uId User ID + * + * @return boolean True when it was created, false in case of + * an error or when the link already existed. + */ + public function addLinkedTags($tag1, $tag2, $relationType, $uId) + { + $tagservice = SemanticScuttle_Service_Factory::get('Tag'); + $tag1 = $tagservice->normalize($tag1); + $tag2 = $tagservice->normalize($tag2); + + if ($tag1 == $tag2 || strlen($tag1) == 0 || strlen($tag2) == 0 + || ($relationType != '>' && $relationType != '=') + || !is_numeric($uId) || $uId<=0 + || ($this->existsLinkedTags($tag1, $tag2, $relationType, $uId)) + ) { + return false; + } + + $values = array( + 'tag1' => $tag1, + 'tag2' => $tag2, + 'relationType' => $relationType, + 'uId' => $uId + ); + $query = 'INSERT INTO ' . $this->getTableName() + . ' ' . $this->db->sql_build_array('INSERT', $values); + + //die($query); + if (!($dbresult = $this->db->sql_query($query))) { + $this->db->sql_transaction('rollback'); + message_die( + GENERAL_ERROR, 'Could not attach tag to tag', + '', __LINE__, __FILE__, $query, $this->db + ); + return false; + } + $this->db->sql_transaction('commit'); + + // Update stats and cache + $this->update($tag1, $tag2, $relationType, $uId); + + return true; + } + + + + /** + * Same as getLinkedTags(), but only tags that have been created + * by admin users are returned. + * + * @param string $tag Tag to get related tags for + * @param string $relationType Type of tag-to-tag relation: >, < or = + * @param boolean $inverseRelation Reverse relation (parent -> child) + * @param array $stopList Array of tags that shall not be returned + * + * @return array Array of tag names + * + * @see getLinkedTags() + */ + public function getAdminLinkedTags( + $tag, $relationType, $inverseRelation = false, $stopList = array() + ) { + // look for admin ids + $userservice = SemanticScuttle_Service_Factory :: get('User'); + $adminIds = $userservice->getAdminIds(); + + //ask for their linked tags + return $this->getLinkedTags( + $tag, $relationType, $adminIds, $inverseRelation, $stopList + ); + } + + + + /** + * Returns an array of tags that are in relation to the given $tag. + * + * @param string $tag Tag to get related tags for + * @param string $relationType Type of tag-to-tag relation: >, < or = + * @param boolean $inverseRelation Reverse relation (parent -> child) + * @param array $stopList Array of tags that shall not be returned + * + * @return array Array of tag names + */ + public function getLinkedTags( + $tag, $relationType, $uId = null, $inverseRelation = false, $stopList = array() + ) { + // Set up the SQL query. + if($inverseRelation) { + $queriedTag = "tag1"; + $givenTag = "tag2"; + } else { + $queriedTag = "tag2"; + $givenTag = "tag1"; + } + + $query = "SELECT DISTINCT ". $queriedTag ." as 'tag'"; + $query.= " FROM `". $this->getTableName() ."`"; + $query.= " WHERE 1=1"; + if($tag !=null) { + $query.= " AND ". $givenTag ." = '". $this->db->sql_escape($tag) ."'"; + } + if($relationType) { + $query.= " AND relationType = '". $this->db->sql_escape($relationType) ."'"; + } + if(is_array($uId)) { + $query.= " AND ( 1=0 "; //tricks always false + foreach($uId as $u) { + $query.= " OR uId = '".intval($u)."'"; + } + $query.= " ) "; + } elseif($uId != null) { + $query.= " AND uId = '".intval($uId)."'"; + } + //die($query); + if (! ($dbresult = $this->db->sql_query($query)) ){ + message_die(GENERAL_ERROR, 'Could not get related tags', '', __LINE__, __FILE__, $query, $this->db); + return false; + } + + $rowset = $this->db->sql_fetchrowset($dbresult); + $output = array(); + foreach($rowset as $row) { + if(!in_array($row['tag'], $stopList)) { + $output[] = $row['tag']; + } + } + + //bijective case for '=' + if($relationType == '=' && $inverseRelation == false) { + //$stopList[] = $tag; + $bijectiveOutput = $this->getLinkedTags($tag, $relationType, $uId, true, $stopList); + $output = array_merge($output, $bijectiveOutput); + //$output = array_unique($output); // remove duplication + } + + $this->db->sql_freeresult($dbresult); + return $output; + } + + /* + * Returns all linked tags (all descendants if relation is >, + * all synonyms if relation is = ) + * $stopList allows to avoid cycle (a > b > a) between tags + */ + function getAllLinkedTags($tag1, $relationType, $uId, $stopList=array()) { + if(in_array($tag1, $stopList) || $tag1 == '') { + return array(); + } + + // try to find data in cache + $tcs = SemanticScuttle_Service_Factory::get('TagCache'); + if(count($stopList) == 0) { + $activatedCache = true; + } else { + $activatedCache = false; + } + + // look for existing links + $stopList[] = $tag1; + $linkedTags = $this->getLinkedTags($tag1, $relationType, $uId, false, $stopList); + if($relationType != '=') { + $linkedTags = array_merge($linkedTags, $this->getLinkedTags($tag1, '=', $uId, false, $stopList)); + } + + if(count($linkedTags) == 0) { + return array(); + + } else { + // use cache if possible + if($activatedCache) { + if($relationType == '>') { + $output = $tcs->getChildren($tag1, $uId); + } elseif($relationType == '=') { + $output = $tcs->getSynonyms($tag1, $uId); + } + if(count($output)>0) { + return $output; + } + } + + // else compute the links + $output = array(); + + foreach($linkedTags as $linkedTag) { + $allLinkedTags = $this->getAllLinkedTags($linkedTag, $relationType, $uId, $stopList); + $output[] = $linkedTag; + if(is_array($allLinkedTags)) { + $output = array_merge($output, $allLinkedTags); + } else { + $output[] = $allLinkedTags; + } + } + + // and save in cache + if($activatedCache == true && $uId>0) { + $tcs->updateTag($tag1, $relationType, $output, $uId); + } + + //$output = array_unique($output); // remove duplication + return $output; + + } + } + + function getOrphewTags($relationType, $uId = 0, $limit = null, $orderBy = null) { + $query = "SELECT DISTINCT tts.tag1 as tag"; + $query.= " FROM `". $this->getTableName() ."` tts"; + if($orderBy != null) { + $tsts =SemanticScuttle_Service_Factory::get('TagStat'); + $query.= ", ".$tsts->getTableName() ." tsts"; + } + $query.= " WHERE tts.tag1 <> ALL"; + $query.= " (SELECT DISTINCT tag2 FROM `". $this->getTableName() ."`"; + $query.= " WHERE relationType = '" . $this->db->sql_escape($relationType) . "'"; + if($uId > 0) { + $query.= " AND uId = '".intval($uId)."'"; + } + $query.= ")"; + if($uId > 0) { + $query.= " AND tts.uId = '".intval($uId)."'"; + } + + switch($orderBy) { + case "nb": + $query.= " AND tts.tag1 = tsts.tag1"; + $query.= " AND tsts.relationType = '" . $this->db->sql_escape($relationType) . "'"; + if($uId > 0) { + $query.= " AND tsts.uId = " . intval($uId); + } + $query.= " ORDER BY tsts.nb DESC"; + break; + case "depth": // by nb of descendants + $query.= " AND tts.tag1 = tsts.tag1"; + $query.= " AND tsts.relationType = '" . $this->db->sql_escape($relationType) . "'"; + if($uId > 0) { + $query.= " AND tsts.uId = " . intval($uId); + } + $query.= " ORDER BY tsts.depth DESC"; + break; + case "nbupdate": + $query.= " AND tts.tag1 = tsts.tag1"; + $query.= " AND tsts.relationType = '" . $this->db->sql_escape($relationType) . "'"; + if($uId > 0) { + $query.= " AND tsts.uId = " . intval($uId); + } + $query.= " ORDER BY tsts.nbupdate DESC"; + break; + } + + if($limit != null) { + $query.= " LIMIT 0," . intval($limit); + } + + if (! ($dbresult = $this->db->sql_query($query)) ){ + message_die(GENERAL_ERROR, 'Could not get linked tags', '', __LINE__, __FILE__, $query, $this->db); + return false; + } + $output = $this->db->sql_fetchrowset($dbresult); + $this->db->sql_freeresult($dbresult); + return $output; + } + + function getMenuTags($uId) { + if(strlen($GLOBALS['menuTag']) < 1) { + return array(); + } else { + // we don't use the getAllLinkedTags function in order to improve performance + $query = "SELECT tag2 as 'tag', COUNT(tag2) as 'count'"; + $query.= " FROM `". $this->getTableName() ."`"; + $query.= " WHERE tag1 = '" . $this->db->sql_escape($GLOBALS['menuTag']) . "'"; + $query.= " AND relationType = '>'"; + if($uId > 0) { + $query.= " AND uId = " . intval($uId); + } + $query.= " GROUP BY tag2"; + $query.= " ORDER BY count DESC"; + $query.= " LIMIT 0, " . intval($GLOBALS['maxSizeMenuBlock']); + + if (! ($dbresult = $this->db->sql_query($query)) ){ + message_die(GENERAL_ERROR, 'Could not get linked tags', '', __LINE__, __FILE__, $query, $this->db); + return false; + } + $output = $this->db->sql_fetchrowset($dbresult); + $this->db->sql_freeresult($dbresult); + return $output; + } + } + + + function existsLinkedTags($tag1, $tag2, $relationType, $uId) { + + //$tag1 = mysql_real_escape_string($tag1); + //$tag2 = mysql_real_escape_string($tag2); + + $query = "SELECT tag1, tag2, relationType, uId FROM `". $this->getTableName() ."`"; + $query.= " WHERE tag1 = '" . $this->db->sql_escape($tag1) . "'"; + $query.= " AND tag2 = '" . $this->db->sql_escape($tag2) . "'"; + $query.= " AND relationType = '" . $this->db->sql_escape($relationType) . "'"; + $query.= " AND uId = " . intval($uId); + + //echo($query."<br>\n"); + + $dbres = $this->db->sql_query($query); + $hasTags = $this->db->sql_numrows($dbres) > 0; + $this->db->sql_freeresult($dbres); + return $hasTags; + } + + function getLinks($uId) { + $query = "SELECT tag1, tag2, relationType, uId FROM `". $this->getTableName() ."`"; + $query.= " WHERE 1=1"; + if($uId > 0) { + $query.= " AND uId = " . intval($uId); + } + + $dbres = $this->db->sql_query($query); + $rowset = $this->db->sql_fetchrowset($dbres); + $this->db->sql_freeresult($dbres); + return $rowset; + } + + function removeLinkedTags($tag1, $tag2, $relationType, $uId) { + if(($tag1 != '' && $tag1 == $tag2) || + ($relationType != ">" && $relationType != "=" && $relationType != "") || + ($tag1 == '' && $tag2 == '')) { + return false; + } + $query = 'DELETE FROM '. $this->getTableName(); + $query.= ' WHERE 1=1'; + $query.= strlen($tag1)>0 ? ' AND tag1 = \''. $this->db->sql_escape($tag1) . "'" : ''; + $query.= strlen($tag2)>0 ? ' AND tag2 = \''. $this->db->sql_escape($tag2) . "'" : ''; + $query.= strlen($relationType)>0 ? ' AND relationType = \''. $this->db->sql_escape($relationType) . "'" : ''; + $query.= strlen($uId)>0 ? ' AND uId = '. intval($uId) : ''; + + if (!($dbresult = $this->db->sql_query($query))) { + message_die(GENERAL_ERROR, 'Could not remove tag relation', '', __LINE__, __FILE__, $query, $this->db); + return false; + } + + + // Update stats and cache + $this->update($tag1, $tag2, $relationType, $uId); + + $this->db->sql_freeresult($dbresult); + return true; + } + + function removeLinkedTagsForUser($uId) { + $query = 'DELETE FROM '. $this->getTableName(); + $query.= ' WHERE uId = '. intval($uId); + + if (!($dbresult = $this->db->sql_query($query))) { + message_die(GENERAL_ERROR, 'Could not remove tag relation', '', __LINE__, __FILE__, $query, $this->db); + return false; + } + + + // Update stats and cache + $this->update('', '', '', $uId); + + $this->db->sql_freeresult($dbresult); + return true; + } + + function renameTag($uId, $oldName, $newName) { + $tagservice =SemanticScuttle_Service_Factory::get('Tag'); + $newName = $tagservice->normalize($newName); + + $query = 'UPDATE `'. $this->getTableName() .'`'; + $query.= ' SET tag1=\'' . $this->db->sql_escape($newName) ."'"; + $query.= ' WHERE tag1=\'' . $this->db->sql_escape($oldName) . "'"; + $query.= ' AND uId=' . intval($uId); + $this->db->sql_query($query); + + $query = 'UPDATE `'. $this->getTableName() .'`'; + $query.= ' SET tag2=\'' . $this->db->sql_escape($newName) . "'"; + $query.= ' WHERE tag2=\'' . $this->db->sql_escape($oldName) . "'"; + $query.= ' AND uId=' . intval($uId); + $this->db->sql_query($query); + + + // Update stats and cache + $this->update($oldName, NULL, '=', $uId); + $this->update($oldName, NULL, '>', $uId); + $this->update($newName, NULL, '=', $uId); + $this->update($newName, NULL, '>', $uId); + + return true; + + } + + function update($tag1, $tag2, $relationType, $uId) { + $tsts =SemanticScuttle_Service_Factory::get('TagStat'); + $tsts->updateStat($tag1, $relationType, $uId); + + $tcs = SemanticScuttle_Service_Factory::get('TagCache'); + $tcs->deleteByUser($uId); + } + + function deleteAll() { + $query = 'TRUNCATE TABLE `'. $this->getTableName() .'`'; + $this->db->sql_query($query); + + $tsts =SemanticScuttle_Service_Factory::get('TagStat'); + $tsts->deleteAll(); + } + +} +?> |