[ Index ]

PHP Cross Reference of vtigercrm-6.1.0

title

Body

[close]

/libraries/freetag/ -> freetag.class.php (source)

   1  <?php
   2  /**
   3   *  Gordon Luk's Freetag - Generalized Open Source Tagging and Folksonomy.
   4   *  Copyright (C) 2004-2005 Gordon D. Luk <gluk AT getluky DOT net>
   5   *
   6   *  Released under both BSD license and Lesser GPL library license.  Whenever
   7   *  there is any discrepancy between the two licenses, the BSD license will
   8   *  take precedence. See License.txt.  
   9   *
  10   */
  11  /**
  12   *  Freetag API Implementation
  13   *
  14   *  Freetag is a generic PHP class that can hook-in to existing database
  15   *  schemas and allows tagging of content within a social website. It's fun,
  16   *  fast, and easy!  Try it today and see what all the folksonomy fuss is
  17   *  about.
  18   * 
  19   *  Contributions and/or donations are welcome.
  20   *
  21   *  Author: Gordon Luk
  22   *  http://www.getluky.net
  23   *  
  24   *  Version: 0.240
  25   *  Last Updated: 12/26/2005 
  26   * 
  27   */ 
  28  
  29  class freetag {
  30  
  31      /**#@+
  32       *  @access private
  33       *  @param string
  34       */ 
  35      /**#@-*/
  36  
  37      /**
  38       * @access private
  39       * @param ADOConnection The ADODB Database connection instance.
  40       */
  41      //var $_db;
  42      /**
  43       * @access private
  44       * @param bool Prints out limited debugging information if true, not fully implemented yet.
  45       */
  46      var $_debug = FALSE;
  47      /**
  48       * @access private
  49       * @param string The prefix of freetag database vtiger_tables.
  50       */
  51      var $_table_prefix = 'vtiger_';
  52      /**
  53       * @access private
  54       * @param string The regex-style set of characters that are valid for normalized tags.
  55       */
  56      var $_normalized_valid_chars = 'a-zA-Z0-9';
  57      /**
  58       * @access private
  59       * @param string Whether to normalize tags at all.
  60       * value 0 saves the tag in case insensitive mode
  61       * value 1 save the tag in lower case
  62       */
  63      var $_normalize_tags = 0;
  64      /**
  65       * @access private
  66       * @param string Whether to prevent multiple vtiger_users from tagging the same object. By default, set to block (ala Upcoming.org)
  67       */
  68      var $_block_multiuser_tag_on_object =0;
  69      /**
  70       * @access private
  71       * @param bool Whether to use persistent ADODB connections. False by default.
  72       */
  73      //var $_PCONNECT = FALSE;
  74      /**
  75       * @access private
  76       * @param int The maximum length of a tag.
  77       */ 
  78      var $_MAX_TAG_LENGTH = 30;
  79      /**
  80       * @access private
  81       * @param string The file path to the installation of ADOdb used.
  82       */ 
  83      //var $_ADODB_DIR = 'adodb/';
  84  
  85      /**
  86       * freetag
  87       *
  88       * Constructor for the freetag class. 
  89       *
  90       * @param array An associative array of options to pass to the instance of Freetag.
  91       * The following options are valid:
  92       * - debug: Set to TRUE for debugging information. [default:FALSE]
  93       * - db: If you've already got an ADODB ADOConnection, you can pass it directly and Freetag will use that. [default:NULL]
  94       * - db_user: Database username
  95       * - db_pass: Database password
  96       * - db_host: Database hostname [default: localhost]
  97       * - db_name: Database name
  98       * - vtiger_table_prefix: If you wish to create multiple Freetag databases on the same database, you can put a prefix in front of the vtiger_table names and pass separate prefixes to the constructor. [default: '']
  99       * - normalize_tags: Whether to normalize (lowercase and filter for valid characters) on tags at all. [default: 1]
 100       * - normalized_valid_chars: Pass a regex-style set of valid characters that you want your tags normalized against. [default: 'a-zA-Z0-9' for alphanumeric]
 101       * - block_multiuser_tag_on_object: Set to 0 in order to allow individual vtiger_users to all tag the same object with the same tag. Default is 1 to only allow one occurence of a tag per object. [default: 1]
 102       * - MAX_TAG_LENGTH: maximum length of normalized tags in chars. [default: 30]
 103       * - ADODB_DIR: directory in which adodb is installed. Change if you don't want to use the bundled version. [default: adodb/]
 104       * - PCONNECT: Whether to use ADODB persistent connections. [default: FALSE]
 105       * 
 106       */ 
 107  	function freetag($options = NULL) {
 108  /*
 109          $available_options = array('debug', 'db', 'db_user', 'db_pass', 'db_host', 'db_name', 'table_prefix', 'normalize_tags', 'normalized_valid_chars', 'block_multiuser_tag_on_object', 'MAX_TAG_LENGTH', 'ADODB_DIR', 'PCONNECT');
 110          if (is_array($options)) {
 111              foreach ($options as $key => $value) {
 112                  $this->debug_text("Option: $key");
 113  
 114                  if (in_array($key, $available_options) ) {
 115                      $this->debug_text("Valid Config options: $key");
 116                      $property = '_'.$key;
 117                      $this->$property = $value;
 118                      $this->debug_text("Setting $property to $value");
 119                  } else {
 120                      $this->debug_text("ERROR: Config option: $key is not a valid option");
 121                  }
 122              }
 123          }*/
 124  /*
 125          require_once($this->_ADODB_DIR . "/adodb.inc.php");
 126          if (is_object($this->_db)) {
 127              $this->db = &$this->_db;
 128              $this->debug_text("DB Instance already exists, using this one.");
 129          } else {
 130              $this->db = ADONewConnection("mysql");
 131              $this->debug_text("Connecting to db with:" . $this->_db_host . " " . $this->_db_user . " " . $this->_db_pass . " " . $this->_db_name);
 132              if ($this->_PCONNECT) {
 133                  $this->db->PConnect($this->_db_host, $this->_db_user, $this->_db_pass, $this->_db_name);
 134              } else {
 135                  $this->db->Connect($this->_db_host, $this->_db_user, $this->_db_pass, $this->_db_name);
 136              }
 137          }
 138          $this->db->debug = $this->_debug;
 139          // Freetag uses ASSOC for ease of maintenance and compatibility with people who choose to modify the schema.
 140          // Feel free to convert to NUM if performance is the highest concern.
 141          $this->db->SetFetchMode(ADODB_FETCH_ASSOC);*/
 142      }
 143  
 144      /**
 145       * get_objects_with_tag
 146       *
 147       * Use this function to build a page of results that have been tagged with the same tag.
 148       * Pass along a tagger_id to collect only a certain user's tagged objects, and pass along
 149       * none in order to get back all user-tagged objects. Most of the get_*_tag* functions
 150       * operate on the normalized form of tags, because most interfaces for navigating tags
 151       * should use normal form.
 152       *
 153       * @param string - Pass the normalized tag form along to the function.
 154       * @param int (Optional) - The numerical offset to begin display at. Defaults to 0.
 155       * @param int (Optional) - The number of results per page to show. Defaults to 100.
 156       * @param int (Optional) - The unique ID of the 'user' who tagged the object.
 157       *
 158       * @return An array of Object ID numbers that reference your original objects.
 159       */ 
 160  	function get_objects_with_tag($tag, $offset = 0, $limit = 100, $tagger_id = NULL) {
 161          if(!isset($tag)) {
 162              return false;
 163          }        
 164          global $adb;
 165          
 166          $where = "tag = ? ";
 167          $params = array($tag);
 168  
 169          if(isset($tagger_id) && ($tagger_id > 0)) {
 170              $where .= "AND tagger_id = ? ";
 171              array_push($params, $tagger_id);
 172          } 
 173          
 174          $prefix = $this->_table_prefix;
 175  
 176          $sql = "SELECT DISTINCT object_id
 177              FROM $prefix}freetagged_objects INNER JOIN $prefix}freetags ON (tag_id = id)
 178              WHERE $where
 179              ORDER BY object_id ASC
 180              LIMIT $offset, $limit";
 181          echo $sql;
 182          $rs = $adb->pquery($sql, $params) or die("Error: $sql");
 183          $retarr = array();
 184          while(!$rs->EOF) {
 185              $retarr[] = $rs->fields['object_id'];
 186              $rs->MoveNext();
 187          }
 188          return $retarr;
 189      }
 190  
 191      /**
 192       * get_objects_with_tag_all
 193       *
 194       * Use this function to build a page of results that have been tagged with the same tag.
 195       * This function acts the same as get_objects_with_tag, except that it returns an unlimited
 196       * number of results. Therefore, it's more useful for internal displays, not for API's.
 197       * Pass along a tagger_id to collect only a certain user's tagged objects, and pass along
 198       * none in order to get back all user-tagged objects. Most of the get_*_tag* functions
 199       * operate on the normalized form of tags, because most interfaces for navigating tags
 200       * should use normal form.
 201       *
 202       * @param string - Pass the normalized tag form along to the function.
 203       * @param int (Optional) - The unique ID of the 'user' who tagged the object.
 204       *
 205       * @return An array of Object ID numbers that reference your original objects.
 206       */ 
 207  	function get_objects_with_tag_all($tag, $tagger_id = NULL) {
 208          if(!isset($tag)) {
 209              return false;
 210          }        
 211          global $adb;
 212          
 213          $where = "tag = ? ";
 214          $params = array($tag);
 215  
 216          if(isset($tagger_id) && ($tagger_id > 0)) {
 217              $where .= "AND tagger_id = ? ";
 218              array_push($params, $tagger_id);
 219          } 
 220          $prefix = $this->_table_prefix;
 221  
 222          $sql = "SELECT DISTINCT object_id
 223              FROM $prefix}freetagged_objects INNER JOIN $prefix}freetags ON (tag_id = id)
 224              WHERE $where
 225              ORDER BY object_id ASC
 226              ";
 227              //echo $sql;
 228          $rs = $adb->pquery($sql, $params) or die("Error: $sql");
 229          $retarr = array();
 230          while(!$rs->EOF) {
 231              $retarr[] = $rs->fields['object_id'];
 232              $rs->MoveNext();
 233          }
 234          return $retarr;
 235      }
 236  
 237      /**
 238       * get_objects_with_tag_combo
 239       *
 240       * Returns an array of object ID's that have all the tags passed in the
 241       * tagArray parameter. Use this to provide tag combo services to your vtiger_users.
 242       *
 243       * @param array - Pass an array of normalized form tags along to the function.
 244       * @param int (Optional) - The numerical offset to begin display at. Defaults to 0.
 245       * @param int (Optional) - The number of results per page to show. Defaults to 100.
 246       * @param int (Optional) - Restrict the result to objects tagged by a particular user.
 247       *
 248       * @return An array of Object ID numbers that reference your original objects.
 249       */
 250  	 function get_objects_with_tag_combo($tagArray, $offset = 0, $limit = 100, $tagger_id = NULL) {
 251          if (!isset($tagArray) || !is_array($tagArray)) {
 252              return false;
 253          }
 254          global $adb;
 255          //$db = &$this->db;
 256          $retarr = array();
 257          if (count($tagArray) == 0) {
 258              return $retarr;
 259          }
 260          $params = array($tagArray);
 261          if(isset($tagger_id) && ($tagger_id > 0)) {
 262              $tagger_sql = "AND tagger_id = ?";
 263              array_push($params, $tagger_id);
 264          } else {
 265              $tagger_sql = "";
 266          }
 267  
 268          foreach ($tagArray as $key => $value) {
 269              $tagArray[$key] = $adb->qstr($value, get_magic_quotes_gpc());
 270          }
 271  
 272          $tagArray = array_unique($tagArray);
 273          $numTags = count($tagArray);
 274          $prefix = $this->_table_prefix;
 275  
 276          // We must adjust for duplicate normalized tags appearing multiple times in the join by 
 277          // counting only the distinct tags. It should also work for an individual user.
 278  
 279          $sql = "SELECT $prefix}freetagged_objects.object_id, tag, COUNT(DISTINCT tag) AS uniques
 280              FROM $prefix}freetagged_objects 
 281              INNER JOIN $prefix}freetags ON ($prefix}freetagged_objects.tag_id = $prefix}freetags.id)
 282              WHERE $prefix}freetags.tag IN (". generateQuestionMarks($tagArray) .")
 283              $tagger_sql
 284              GROUP BY $prefix}freetagged_objects.object_id
 285              HAVING uniques = $numTags
 286              LIMIT $offset, $limit";
 287          $this->debug_text("Tag combo: " . join("+", $tagArray) . " SQL: $sql");
 288          $rs = $adb->pquery($sql, $params) or die("Error: $sql");
 289          while(!$rs->EOF) {
 290              $retarr[] = $rs->fields['object_id'];
 291              $rs->MoveNext();
 292          }
 293          return $retarr;
 294      }
 295  
 296      /**
 297       * get_objects_with_tag_id
 298       *
 299       * Use this function to build a page of results that have been tagged with the same tag.
 300       * This function acts the same as get_objects_with_tag, except that it accepts a numerical
 301       * tag_id instead of a text tag.
 302       * Pass along a tagger_id to collect only a certain user's tagged objects, and pass along
 303       * none in order to get back all user-tagged objects.
 304       *
 305       * @param int - Pass the ID number of the tag.
 306       * @param int (Optional) - The numerical offset to begin display at. Defaults to 0.
 307       * @param int (Optional) - The number of results per page to show. Defaults to 100.
 308       * @param int (Optional) - The unique ID of the 'user' who tagged the object.
 309       *
 310       * @return An array of Object ID numbers that reference your original objects.
 311       */ 
 312  	function get_objects_with_tag_id($tag_id, $offset = 0, $limit = 100, $tagger_id = NULL) {
 313          if(!isset($tag_id)) {
 314              return false;
 315          }        
 316          global $adb;
 317  
 318          $where = "id = ? ";
 319          $params = array($tag_id);
 320          
 321          if(isset($tagger_id) && ($tagger_id > 0)) {
 322              $where .= "AND tagger_id = ?";
 323              array_push($params, $tagger_id);
 324          } 
 325      
 326          $prefix = $this->_table_prefix;
 327  
 328          $sql = "SELECT DISTINCT object_id
 329              FROM $prefix}freetagged_objects INNER JOIN $prefix}freetags ON (tag_id = id)
 330              WHERE $where
 331              ORDER BY object_id ASC
 332              LIMIT $offset, $limit ";
 333          $rs = $adb->pquery($sql, $params) or die("Error: $sql");
 334          $retarr = array();
 335          while(!$rs->EOF) {
 336              $retarr[] = $rs->fields['object_id'];
 337              $rs->MoveNext();
 338          }
 339          return $retarr;
 340      }
 341  
 342  
 343      /**
 344       * get_tags_on_object
 345       *
 346       * You can use this function to show the tags on an object. Since it supports both user-specific
 347       * and general modes with the $tagger_id parameter, you can use it twice on a page to make it work
 348       * similar to upcoming.org and flickr, where the page displays your own tags differently than
 349       * other vtiger_users' tags.
 350       *
 351       * @param int The unique ID of the object in question.
 352       * @param int The offset of tags to return.
 353       * @param int The size of the tagset to return. Use a zero size to get all tags.
 354       * @param int The unique ID of the person who tagged the object, if user-level tags only are preferred.
 355       *
 356       * @return array Returns a PHP array with object elements ordered by object ID. Each element is an associative
 357       * array with the following elements:
 358       *   - 'tag' => Normalized-form tag
 359       *     - 'raw_tag' => The raw-form tag
 360       *     - 'tagger_id' => The unique ID of the person who tagged the object with this tag.
 361       */ 
 362  	function get_tags_on_object($object_id, $offset = 0, $limit = 10, $tagger_id = NULL) {
 363          if(!isset($object_id)) {
 364              return false;
 365          }    
 366          
 367          $where = "object_id = ? ";
 368          $params = array($object_id);
 369              
 370          if(isset($tagger_id) && ($tagger_id > 0)) {
 371              $where .= "AND tagger_id = ? ";
 372              array_push($params, $tagger_id);
 373          } 
 374  
 375          if($limit <= 0) {
 376              $limit_sql = "";
 377          } else {
 378              $limit_sql = "LIMIT $offset, $limit";
 379          }
 380          $prefix = $this->_table_prefix;
 381  
 382          global $adb;
 383  
 384          $sql = "SELECT DISTINCT tag, raw_tag, tagger_id, id
 385              FROM $prefix}freetagged_objects INNER JOIN $prefix}freetags ON (tag_id = id)
 386              WHERE $where
 387              ORDER BY id ASC
 388              $limit_sql
 389              ";
 390              //echo ' <br><br>get_tags_on_object sql is ' .$sql;
 391          $rs = $adb->pquery($sql, $params) or die("Error: $sql");
 392          $retarr = array();
 393          while(!$rs->EOF) {
 394              $retarr[] = array(
 395                      'tag' => $rs->fields['tag'],
 396                      'raw_tag' => $rs->fields['raw_tag'],
 397                      'tagger_id' => $rs->fields['tagger_id']
 398                      );
 399              $rs->MoveNext();
 400          }
 401          return $retarr;
 402      }
 403  
 404      /**
 405       * safe_tag
 406       *
 407       * Pass individual tag phrases along with object and person ID's in order to 
 408       * set a tag on an object. If the tag in its raw form does not yet exist,
 409       * this function will create it.
 410       * Fails transparently on duplicates, and checks for dupes based on the 
 411       * block_multiuser_tag_on_object constructor param.
 412       *
 413       * @param int The unique ID of the person who tagged the object with this tag.
 414       * @param int The unique ID of the object in question.
 415       * @param string A raw string from a web form containing tags.
 416       *
 417       * @return boolean Returns true if successful, false otherwise. Does not operate as a transaction.
 418       */ 
 419  
 420  	function safe_tag($tagger_id, $object_id, $tag, $module) {
 421          if(!isset($tagger_id)||!isset($object_id)||!isset($tag)) {
 422              die("safe_tag argument missing");
 423              return false;
 424          }
 425          global $adb;
 426  
 427          $normalized_tag = $this->normalize_tag($tag);
 428          $prefix = $this->_table_prefix;
 429          $params = array();
 430          // First, check for duplicate of the normalized form of the tag on this object.
 431          // Dynamically switch between allowing duplication between vtiger_users on the constructor param 'block_multiuser_tag_on_object'.
 432          // If it's set not to block multiuser tags, then modify the existence
 433          // check to look for a tag by this particular user. Otherwise, the following
 434          // query will reveal whether that tag exists on that object for ANY user.
 435          if ($this->_block_multiuser_tag_on_object == 0) {
 436              $tagger_sql = " AND tagger_id = ? ";
 437              array_push($params, $tagger_id);
 438          } else $tagger_sql = "";
 439          $sql = "SELECT COUNT(*) as count 
 440              FROM $prefix}freetagged_objects INNER JOIN $prefix}freetags ON (tag_id = id)
 441              WHERE 1=1 
 442              $tagger_sql
 443              AND object_id = ?
 444              AND tag = ? ";
 445              
 446          array_push($params, $object_id, $normalized_tag);
 447          $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql");
 448          if($rs->fields['count'] > 0) {
 449              return true;
 450          }
 451          // Then see if a raw tag in this form exists.
 452          $sql = "SELECT id 
 453              FROM $prefix}freetags 
 454              WHERE raw_tag = ? ";
 455          $rs = $adb->pquery($sql, array($tag)) or die("Syntax Error: $sql");
 456          if(!$rs->EOF) {
 457              $tag_id = $rs->fields['id'];
 458          } else {
 459              // Add new tag! 
 460              $tag_id = $adb->getUniqueId('vtiger_freetags');
 461              $sql = "INSERT INTO $prefix}freetags (id, tag, raw_tag) VALUES (?,?,?)";
 462              $params = array($tag_id, $normalized_tag, $tag);
 463              $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql");
 464              
 465          }
 466          if(!($tag_id > 0)) {
 467              return false;
 468          }
 469          $sql = "INSERT INTO $prefix}freetagged_objects
 470              (tag_id, tagger_id, object_id, tagged_on, module) VALUES (?,?,?, NOW(),?)";
 471          $params = array($tag_id, $tagger_id, $object_id, $module);
 472          $rs = $adb->pquery($sql, $params) or die("Syntax error: $sql");
 473  
 474          return true;
 475      }
 476  
 477      /**
 478       * normalize_tag
 479       *
 480       * This is a utility function used to take a raw tag and convert it to normalized form.
 481       * Normalized form is essentially lowercased alphanumeric characters only, 
 482       * with no spaces or special characters.
 483       *
 484       * Customize the normalized valid chars with your own set of special characters
 485       * in regex format within the option 'normalized_valid_chars'. It acts as a filter
 486       * to let a customized set of characters through.
 487       * 
 488       * After the filter is applied, the function also lowercases the characters using strtolower 
 489       * in the current locale.
 490       *
 491       * The default for normalized_valid_chars is a-zA-Z0-9, or english alphanumeric.
 492       *
 493       * @param string An individual tag in raw form that should be normalized.
 494       *
 495       * @return string Returns the tag in normalized form.
 496       */ 
 497  	function normalize_tag($tag) {
 498          if ($this->_normalize_tags) {
 499              $normalized_valid_chars = $this->_normalized_valid_chars;
 500              $normalized_tag = preg_replace("/[^$normalized_valid_chars]/", "", $tag);
 501              return strtolower($normalized_tag);
 502          } else {
 503              return $tag;
 504          }
 505  
 506      }
 507  
 508      /**
 509       * delete_object_tag
 510       *
 511       * Removes a tag from an object. This does not delete the tag itself from
 512       * the database. Since most applications will only allow a user to delete
 513       * their own tags, it supports raw-form tags as its tag parameter, because
 514       * that's what is usually shown to a user for their own tags.
 515       *
 516       * @param int The unique ID of the person who tagged the object with this tag.
 517       * @param int The ID of the object in question.
 518       * @param string The raw string form of the tag to delete. See above for vtiger_notes.
 519       *
 520       * @return string Returns the tag in normalized form.
 521       */ 
 522  	function delete_object_tag($tagger_id, $object_id, $tag) {
 523          if(!isset($tagger_id)||!isset($object_id)||!isset($tag)) {
 524              die("delete_object_tag argument missing");
 525              return false;
 526          }
 527          global $adb;
 528          $tag_id = $this->get_raw_tag_id($tag);
 529          $prefix = $this->_table_prefix;
 530          if($tag_id > 0) {
 531  
 532              $sql = "DELETE FROM $prefix}freetagged_objects
 533                  WHERE tagger_id = ? AND object_id = ? AND tag_id = ? LIMIT 1";
 534              $params = array($tagger_id, $object_id, $tag_id);
 535              $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql");    
 536              return true;
 537          } else {
 538              return false;    
 539          }
 540      }
 541  
 542      /**
 543       * delete_all_object_tags
 544       *
 545       * Removes all tag from an object. This does not
 546       * delete the tag itself from the database. This is most useful for
 547       * cleanup, where an item is deleted and all its tags should be wiped out
 548       * as well.
 549       *
 550       * @param int The ID of the object in question.
 551       *
 552       * @return boolean Returns true if successful, false otherwise. It will return true if the tagged object does not exist.
 553       */ 
 554  	function delete_all_object_tags($object_id) {
 555          global $adb;
 556          $prefix = $this->_table_prefix;
 557          if($object_id > 0) {
 558              $sql = "DELETE FROM $prefix}freetagged_objects
 559                  WHERE object_id = ? ";    
 560                  $rs = $adb->pquery($sql, array($object_id)) or die("Syntax Error: $sql");    
 561              return true;
 562          } else {
 563              return false;    
 564          }
 565      }
 566  
 567  
 568      /**
 569       * delete_all_object_tags_for_user
 570       *
 571       * Removes all tag from an object for a particular user. This does not
 572       * delete the tag itself from the database. This is most useful for
 573       * implementations similar to del.icio.us, where a user is allowed to retag
 574       * an object from a text box. That way, it becomes a two step operation of
 575       * deleting all the tags, then retagging with whatever's left in the input.
 576       *
 577       * @param int The unique ID of the person who tagged the object with this tag.
 578       * @param int The ID of the object in question.
 579       *
 580       * @return boolean Returns true if successful, false otherwise. It will return true if the tagged object does not exist.
 581       */ 
 582  
 583  	function delete_all_object_tags_for_user($tagger_id, $object_id) {
 584          if(!isset($tagger_id)||!isset($object_id)) {
 585              die("delete_all_object_tags_for_user argument missing");
 586              return false;
 587          }
 588          global $adb;
 589          $prefix = $this->_table_prefix;
 590          if($object_id > 0) {
 591  
 592              $sql = "DELETE FROM $prefix}freetagged_objects
 593                  WHERE tagger_id = ? AND object_id = ?";    
 594              $rs = $adb->pquery($sql, array($tagger_id, $object_id)) or die("Syntax Error: $sql");    
 595              return true;
 596          } else {
 597              return false;    
 598          }
 599      }
 600  
 601      /**
 602       * get_tag_id
 603       *
 604       * Retrieves the unique ID number of a tag based upon its normal form. Actually,
 605       * using this function is dangerous, because multiple tags can exist with the same
 606       * normal form, so be careful, because this will only return one, assuming that
 607       * if you're going by normal form, then the individual tags are interchangeable.
 608       *
 609       * @param string The normal form of the tag to fetch.
 610       *
 611       * @return string Returns the tag in normalized form.
 612       */ 
 613  	function get_tag_id($tag) {
 614          if(!isset($tag)) {
 615              die("get_tag_id argument missing");
 616              return false;
 617          }
 618          global $adb;
 619          
 620          $prefix = $this->_table_prefix;
 621  
 622          $sql = "SELECT id FROM $prefix}freetags
 623              WHERE tag = ? LIMIT 1 ";    
 624              $rs = $adb->pquery($sql, array($tag)) or die("Syntax Error: $sql");    
 625          return $rs->fields['id'];
 626  
 627      }
 628  
 629      /**
 630       * get_raw_tag_id
 631       *
 632       * Retrieves the unique ID number of a tag based upon its raw form. If a single
 633       * unique record is needed, then use this function instead of get_tag_id, 
 634       * because raw_tags are unique.
 635       *
 636       * @param string The raw string form of the tag to fetch.
 637       *
 638       * @return string Returns the tag in normalized form.
 639       */ 
 640  
 641  	function get_raw_tag_id($tag) {
 642          if(!isset($tag)) {
 643              die("get_tag_id argument missing");
 644              return false;
 645          }
 646          global $adb;
 647          $prefix = $this->_table_prefix;
 648  
 649          $sql = "SELECT id FROM $prefix}freetags
 650              WHERE raw_tag = ? LIMIT 1 ";    
 651              $rs = $adb->pquery($sql, array($tag)) or die("Syntax Error: $sql");    
 652          return $rs->fields['id'];
 653  
 654      }
 655  
 656      /**
 657       * tag_object
 658       *
 659       * This function allows you to pass in a string directly from a form, which is then
 660       * parsed for quoted phrases and special characters, normalized and converted into tags.
 661       * The tag phrases are then individually sent through the safe_tag() method for processing
 662       * and the object referenced is set with that tag. 
 663       *
 664       * This method has been refactored to automatically look for existing tags and run
 665       * adds/updates/deletes as appropriate.
 666       *
 667       * @param int The unique ID of the person who tagged the object with this tag.
 668       * @param int The ID of the object in question.
 669       * @param string The raw string form of the tag to delete. See above for vtiger_notes.
 670       * @param int Whether to skip the update portion for objects that haven't been tagged. (Default: 1)
 671       *
 672       * @return string Returns the tag in normalized form.
 673       */
 674  	function tag_object($tagger_id, $object_id, $tag_string, $module, $skip_updates = 1) {
 675          if($tag_string == '') {
 676              // If an empty string was passed, just return true, don't die.
 677              // die("Empty tag string passed");
 678              return true;
 679          }
 680          $tagArray = $this->_parse_tags($tag_string);
 681  
 682          $oldTags = $this->get_tags_on_object($object_id, 0, 0, $tagger_id);
 683  
 684          $preserveTags = array();
 685  
 686          if (($skip_updates == 0) && (count($oldTags) > 0)) {
 687              foreach ($oldTags as $tagItem) {
 688                  if (!in_array($tagItem['raw_tag'], $tagArray)) {
 689                      // We need to delete old tags that don't appear in the new parsed string.
 690                      $this->delete_object_tag($tagger_id, $object_id, $tagItem['raw_tag']);
 691                  } else {
 692                      // We need to preserve old tags that appear (to save timestamps)
 693                      $preserveTags[] = $tagItem['raw_tag'];
 694                  }
 695              }
 696          }
 697          $newTags = array_diff($tagArray, $preserveTags);
 698  
 699          $this->_tag_object_array($tagger_id, $object_id, $newTags, $module);
 700  
 701          return true;
 702      }
 703  
 704      /**
 705       * _tag_object_array
 706       *
 707       * Private method to add tags to an object from an array.
 708       *
 709       * @param int Unique ID of tagger
 710       * @param int Unique ID of object
 711       * @param array Array of tags to add.
 712       *
 713       * @return boolean True if successful, false otherwise.
 714       */
 715  	function _tag_object_array($tagger_id, $object_id, $tagArray, $module) {
 716          foreach($tagArray as $tag) {
 717              $tag = trim($tag);
 718              if(($tag != '') && (strlen($tag) <= $this->_MAX_TAG_LENGTH)) {
 719                  if(get_magic_quotes_gpc()) {
 720                      $tag = addslashes($tag);
 721                  }
 722                  $this->safe_tag($tagger_id, $object_id, $tag, $module);
 723              }
 724          }
 725          return true;
 726      }
 727  
 728      /**
 729       * _parse_tags
 730       *
 731       * Private method to parse tags out of a string and into an array.
 732       *
 733       * @param string String to parse.
 734       *
 735       * @return array Returns an array of the raw "tags" parsed according to the freetag settings.
 736       */
 737  
 738  	function _parse_tags($tag_string) {
 739          $newwords = array();
 740          if ($tag_string == '') {
 741              // If the tag string is empty, return the empty set.
 742              return $newwords;
 743          }
 744          # Perform tag parsing
 745          if(get_magic_quotes_gpc()) {
 746              $query = stripslashes(trim($tag_string));
 747          } else {
 748              $query = trim($tag_string);
 749          }
 750          $words = preg_split('/(")/', $query,-1,PREG_SPLIT_NO_EMPTY|PREG_SPLIT_DELIM_CAPTURE);
 751          $delim = 0;
 752          foreach ($words as $key => $word)
 753          {
 754              if ($word == '"') {
 755                  $delim++;
 756                  continue;
 757              }
 758              if (($delim % 2 == 1) && $words[$key - 1] == '"') {
 759                  $newwords[] = $word;
 760              } else {
 761                  $newwords = array_merge($newwords, preg_split('/\s+/', $word, -1, PREG_SPLIT_NO_EMPTY));
 762              }
 763          }
 764          return $newwords;
 765      }
 766  
 767      /**
 768       * update_tags
 769       *
 770       * This method supports a user updating their set of all tags on an object
 771       * in a streamlined manner. Very useful for interfaces where all tags on an
 772       * object from a user may be edited through a single text box.
 773       */
 774  
 775      /**
 776       * get_most_popular_tags
 777       *
 778       * This function returns the most popular tags in the freetag system, with
 779       * offset and limit support for pagination. It also supports restricting to 
 780       * an individual user. Call it with no parameters for a list of 25 most popular
 781       * tags.
 782       * 
 783       * @param int The unique ID of the person to restrict results to.
 784       * @param int The offset of the tag to start at.
 785       * @param int The number of tags to return in the result set.
 786       *
 787       * @return array Returns a PHP array with tags ordered by popularity descending. 
 788       * Each element is an associative array with the following elements:
 789       *   - 'tag' => Normalized-form tag
 790       *     - 'count' => The number of objects tagged with this tag.
 791       */
 792  
 793  	function get_most_popular_tags($tagger_id = NULL, $offset = 0, $limit = 25) {
 794          global $adb;
 795          $params = array();
 796          if(isset($tagger_id) && ($tagger_id > 0)) {
 797              $tagger_sql = "AND tagger_id = ?";
 798              array_push($params, $tagger_id);
 799          } else {
 800              $tagger_sql = "";
 801          }
 802          $prefix = $this->_table_prefix;
 803  
 804          $sql = "SELECT tag, COUNT(*) as count
 805              FROM $prefix}freetags INNER JOIN $prefix}freetagged_objects ON (id = tag_id)
 806              WHERE 1
 807              $tagger_sql
 808              GROUP BY tag
 809              ORDER BY count DESC, tag ASC
 810              LIMIT $offset, $limit";
 811  
 812          $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql");
 813          $retarr = array();
 814          while(!$rs->EOF) {
 815              $retarr[] = array(
 816                      'tag' => $rs->fields['tag'],
 817                      'count' => $rs->fields['count']
 818                      );
 819              $rs->MoveNext();
 820          }
 821  
 822          return $retarr;
 823  
 824      }
 825  
 826      /**
 827       * count_tags
 828       *
 829       * Returns the total number of tag->object links in the system.
 830       * It might be useful for pagination at times, but i'm not sure if I actually use
 831       * this anywhere. Restrict to a person's tagging by using the $tagger_id parameter.
 832       *
 833       * @param int The unique ID of the person to restrict results to.
 834       *
 835       * @return int Returns the count 
 836       */
 837  	function count_tags($tagger_id = NULL) {
 838          global $adb;
 839          $params = array();
 840          if(isset($tagger_id) && ($tagger_id > 0)) {
 841              $tagger_sql = "AND tagger_id = ?";
 842              array_push($params, $tagger_id);
 843          } else {
 844              $tagger_sql = "";
 845          }
 846          $prefix = $this->_table_prefix;
 847  
 848          $sql = "SELECT COUNT(*) as count
 849              FROM $prefix}freetags INNER JOIN $prefix}freetagged_objects ON (id = tag_id)
 850              WHERE 1
 851              $tagger_sql
 852              ";
 853  
 854          $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql");
 855          if(!$rs->EOF) {
 856              return $rs->fields['count'];
 857          }
 858          return false;
 859  
 860      }
 861  
 862      /**
 863       * get_tag_cloud_html
 864       *
 865       * This is a pretty straightforward, flexible method that automatically
 866       * generates some html that can be dropped in as a tag cloud.
 867       * It uses explicit font sizes inside of the style attribute of SPAN 
 868       * elements to accomplish the differently sized objects.
 869       *
 870       * It will also link every tag to $tag_page_url, appended with the 
 871       * normalized form of the tag. You should adapt this value to your own
 872       * tag detail page's URL.
 873       *
 874       * @param int The maximum number of tags to return. (default: 100)
 875       * @param int The minimum font size in the cloud. (default: 10)
 876       * @param int The maximum number of tags to return. (default: 20)
 877       * @param string The "units" for the font size (i.e. 'px', 'pt', 'em') (default: px)
 878       * @param string The class to use for all spans in the cloud. (default: cloud_tag)
 879       * @param string The tag page URL (default: /tag/)
 880       *
 881       * @return string Returns an HTML snippet that can be used directly as a tag cloud.
 882       */
 883  
 884  	function get_tag_cloud_html($module="",$tagger_id = NULL,$obj_id= NULL,$num_tags = 100, $min_font_size = 10, $max_font_size = 20, $font_units = 'px', $span_class = '', $tag_page_url = '/tag/') {
 885          global $theme;
 886          $theme_path="themes/".$theme."/";
 887          $image_path=$theme_path."images/";    
 888          $tag_list = $this->get_tag_cloud_tags($num_tags, $tagger_id,$module,$obj_id);
 889          if (count($tag_list[0])) {
 890              // Get the maximum qty of tagged objects in the set
 891              $max_qty = max(array_values($tag_list[0]));
 892              // Get the min qty of tagged objects in the set
 893              $min_qty = min(array_values($tag_list[0]));
 894          } else {
 895              return '';
 896          }
 897  
 898          // For ever additional tagged object from min to max, we add
 899          // $step to the font size.
 900          $spread = $max_qty - $min_qty;
 901          if (0 == $spread) { // Divide by zero
 902              $spread = 1;
 903          }
 904          $step = ($max_font_size - $min_font_size)/($spread);
 905  
 906          // Since the original tag_list is alphabetically ordered,
 907          // we can now create the tag cloud by just putting a span
 908          // on each element, multiplying the diff between min and qty
 909          // by $step.
 910          $cloud_html = '';
 911          $cloud_spans = array();
 912          if($module =='')
 913              $module = 'All';
 914          if($module != 'All') {
 915              foreach ($tag_list[0] as $tag => $qty) {
 916                  $size = $min_font_size + ($qty - $min_qty) * $step;
 917                  $cloud_span[] = '<span id="tag_'.$tag_list[1][$tag].'" class="' . $span_class . '" onMouseOver=$("tagspan_'.$tag_list[1][$tag].'").style.display="inline"; onMouseOut=$("tagspan_'.$tag_list[1][$tag].'").style.display="none";><a class="tagit" href="index.php?module=Home&action=UnifiedSearch&search_module='.$module.'&search_tag=tag_search&query_string='. urlencode($tag) . '" style="font-size: '. $size . $font_units . '">' . htmlspecialchars(stripslashes($tag)) . '</a><span class="'. $span_class .'" id="tagspan_'.$tag_list[1][$tag].'" style="display:none;cursor:pointer;" onClick="DeleteTag('.$tag_list[1][$tag].','.$obj_id.');"><img src="' . vtiger_imageurl('del_tag.gif', $theme) . '"></span></span>';
 918  
 919              }
 920          } else {
 921              foreach($tag_list[0] as $tag => $qty) {
 922                  $size = $min_font_size + ($qty - $min_qty) * $step;
 923                  $cloud_span[] = '<span class="' . $span_class . '"><a class="tagit" href="index.php?module=Home&action=UnifiedSearch&search_module='.$module.'&search_tag=tag_search&query_string='. urlencode($tag) . '" style="font-size: '. $size . $font_units . '">' . htmlspecialchars(stripslashes($tag)) . '</a></span>';
 924              }
 925          }
 926          $cloud_html = join("\n ", $cloud_span);
 927          return $cloud_html;
 928  
 929      }
 930  
 931      /*
 932       * get_tag_cloud_tags
 933       *
 934       * This is a function built explicitly to set up a page with most popular tags
 935       * that contains an alphabetically sorted list of tags, which can then be sized
 936       * or colored by popularity.
 937       *
 938       * Also known more popularly as Tag Clouds!
 939       *
 940       * Here's the example case: http://upcoming.org/tag/
 941       *
 942       * @param int The maximum number of tags to return.
 943       *
 944       * @return array Returns an array where the keys are normalized tags, and the
 945       * values are numeric quantity of objects tagged with that tag.
 946       */
 947  
 948  	function get_tag_cloud_tags($max = 100, $tagger_id = NULL,$module = "",$obj_id = NULL) {
 949          global $adb;
 950          $params = array();
 951          if(isset($tagger_id) && ($tagger_id > 0)) {
 952              $tagger_sql = " AND tagger_id = ?";
 953              array_push($params, $tagger_id);
 954          } else {
 955              $tagger_sql = "";
 956          }
 957  
 958          if($module != "") {
 959              $tagger_sql .= " AND module = ?";
 960              array_push($params, $module);
 961          } else {
 962              $tagger_sql .= "";
 963          }
 964  
 965          if(isset($obj_id) && $obj_id > 0) {
 966                $tagger_sql .= " AND object_id = ?";
 967              array_push($params, $obj_id);
 968          } else {
 969              $tagger_sql .= "";
 970          }
 971  
 972          $prefix = $this->_table_prefix;
 973          $sql = "SELECT tag,tag_id,COUNT(object_id) AS quantity
 974              FROM $prefix}freetags INNER JOIN $prefix}freetagged_objects
 975              ON ($prefix}freetags.id = tag_id)
 976              WHERE 1=1
 977              $tagger_sql
 978              GROUP BY tag
 979              ORDER BY quantity DESC LIMIT 0, $max";
 980          //echo $sql;
 981          $rs = $adb->pquery($sql, $params) or die("Syntax Error: $sql");
 982          $retarr = array();
 983          while(!$rs->EOF) {
 984              $retarr[$rs->fields['tag']] = $rs->fields['quantity'];
 985              $retarr1[$rs->fields['tag']] = $rs->fields['tag_id'];
 986              $rs->MoveNext();
 987          }
 988          if($retarr) ksort($retarr);
 989          if($retarr1) ksort($retarr1);
 990          $return_value[]=$retarr;
 991          $return_value[]=$retarr1;
 992          return $return_value;
 993  
 994      }
 995  
 996      /**
 997       * similar_tags
 998       *
 999       * Finds tags that are "similar" or related to the given tag.
1000       * It does this by looking at the other tags on objects tagged with the tag specified.
1001       * Confusing? Think of it like e-commerce's "Other vtiger_users who bought this also bought," 
1002       * as that's exactly how this works.
1003       *
1004       * Returns an empty array if no tag is passed, or if no related tags are found.
1005       * Hint: You can detect related tags returned with count($retarr > 0)
1006       *
1007       * It's important to note that the quantity passed back along with each tag
1008       * is a measure of the *strength of the relation* between the original tag
1009       * and the related tag. It measures the number of objects tagged with both
1010       * the original tag and its related tag.
1011       *
1012       * Thanks to Myles Grant for contributing this function!
1013       *
1014       * @param string The raw normalized form of the tag to fetch.
1015       * @param int The maximum number of tags to return.
1016       *
1017       * @return array Returns an array where the keys are normalized tags, and the
1018       * values are numeric quantity of objects tagged with BOTH tags, sorted by
1019       * number of occurences of that tag (high to low).
1020       */ 
1021  
1022  	function similar_tags($tag, $max = 100) {
1023          $retarr = array();
1024          if(!isset($tag)) {
1025              return $retarr;
1026          }
1027          global $adb;
1028  
1029          // This query was written using a double join for PHP. If you're trying to eke
1030          // additional performance and are running MySQL 4.X, you might want to try a subselect
1031          // and compare perf numbers.
1032          $prefix = $this->_table_prefix;
1033  
1034          $sql = "SELECT t1.tag, COUNT( o1.object_id ) AS quantity
1035              FROM $prefix}freetagged_objects o1
1036              INNER JOIN $prefix}freetags t1 ON ( t1.id = o1.tag_id )
1037              INNER JOIN $prefix}freetagged_objects o2 ON ( o1.object_id = o2.object_id )
1038              INNER JOIN $prefix}freetags t2 ON ( t2.id = o2.tag_id )
1039              WHERE t2.tag = ? AND t1.tag != ?
1040              GROUP BY o1.tag_id
1041              ORDER BY quantity DESC
1042              LIMIT 0, ?";
1043  
1044          $rs = $adb->pquery($sql, array($tag, $tag, $max)) or die("Syntax Error: $sql");
1045          while(!$rs->EOF) {
1046              $retarr[$rs->fields['tag']] = $rs->fields['quantity'];
1047              $rs->MoveNext();
1048          }
1049  
1050          return $retarr;
1051      }
1052  
1053      /**
1054       * similar_objects
1055       *
1056       * This method implements a simple ability to find some objects in the database
1057       * that might be similar to an existing object. It determines this by trying
1058       * to match other objects that share the same tags.
1059       *
1060       * The user of the method has to use a threshold (by default, 1) which specifies
1061       * how many tags other objects must have in common to match. If the original object 
1062       * has no tags, then it won't match anything. Matched objects are returned in order
1063       * of most similar to least similar.
1064       *
1065       * The more tags set on a database, the better this method works. Since this
1066       * is such an expensive operation, it requires a limit to be set via max_objects.
1067       *
1068       * @param int The unique ID of the object to find similar objects for.
1069       * @param int The Threshold of tags that must be found in common (default: 1)
1070       * @param int The maximum number of similar objects to return (default: 5).
1071       * @param int Optionally pass a tagger id to restrict similarity to a tagger's view.
1072       * 
1073       * @return array Returns a PHP array with matched objects ordered by strength of match descending. 
1074       * Each element is an associative array with the following elements:
1075       * - 'strength' => A floating-point strength of match from 0-1.0
1076       * - 'object_id' => Unique ID of the matched object
1077       *
1078       */
1079  	function similar_objects($object_id, $threshold = 1, $max_objects = 5, $tagger_id = NULL) {
1080          global $adb;    
1081          $retarr = array();
1082  
1083          $object_id = intval($object_id);
1084          $threshold = intval($threshold);
1085          $max_objects = intval($max_objects);
1086          if (!isset($object_id) || !($object_id > 0)) {
1087              return $retarr;
1088          }
1089          if ($threshold <= 0) {
1090              return $retarr;
1091          }
1092          if ($max_objects <= 0) {
1093              return $retarr;
1094          }
1095  
1096          // Pass in a zero-limit to get all tags.
1097          $tagItems = $this->get_tags_on_object($object_id, 0, 0);
1098  
1099          $tagArray = array();
1100          foreach ($tagItems as $tagItem) {
1101              $tagArray[] = $tagItem['tag'];
1102          }
1103          $tagArray = array_unique($tagArray);
1104  
1105          $numTags = count($tagArray);
1106          if ($numTags == 0) {
1107              return $retarr; // Return empty set of matches
1108          }
1109  
1110          $prefix = $this->_table_prefix;
1111  
1112          $sql = "SELECT matches.object_id, COUNT( matches.object_id ) AS num_common_tags
1113              FROM $prefix}freetagged_objects as matches
1114              INNER JOIN $prefix}freetags as tags ON ( tags.id = matches.tag_id )
1115              WHERE tags.tag IN (". generateQuestionMarks($tagArray) .")
1116              GROUP BY matches.object_id
1117              HAVING num_common_tags >= ?
1118              ORDER BY num_common_tags DESC
1119              LIMIT 0, ? ";
1120  
1121          $rs = $adb->pquery($sql, array($tagArray, $threshold, $max_objects)) or die("Syntax Error: $sql, Error: " . $adb->ErrorMsg());
1122          while(!$rs->EOF) {
1123              $retarr[] = array (
1124                  'object_id' => $rs->fields['object_id'],
1125                  'strength' => ($rs->fields['num_common_tags'] / $numTags)
1126                  );
1127              $rs->MoveNext();
1128          }
1129  
1130          return $retarr;
1131      }
1132  
1133  
1134      /*
1135       * Prints debug text if debug is enabled.
1136       *
1137       * @param string The text to output
1138       * @return boolean Always returns true
1139       */
1140  	function debug_text($text) {
1141          if ($this->_debug) {
1142              echo "$text<br>\n";
1143          }
1144          return true;
1145      }
1146  
1147  }
1148  


Generated: Fri Nov 28 20:08:37 2014 Cross-referenced by PHPXref 0.7.1