aboutsummaryrefslogtreecommitdiff
path: root/src/SemanticScuttle/Service/Tag2Tag.php
diff options
context:
space:
mode:
Diffstat (limited to 'src/SemanticScuttle/Service/Tag2Tag.php')
-rw-r--r--src/SemanticScuttle/Service/Tag2Tag.php470
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();
+ }
+
+}
+?>