[ Index ] |
PHP Cross Reference of vtigercrm-6.1.0 |
[Summary view] [Print] [Text view]
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
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 20:08:37 2014 | Cross-referenced by PHPXref 0.7.1 |