[ Index ]

PHP Cross Reference of MantisBT

title

Body

[close]

/core/ -> tag_api.php (source)

   1  <?php
   2  # MantisBT - A PHP based bugtracking system
   3  
   4  # MantisBT is free software: you can redistribute it and/or modify
   5  # it under the terms of the GNU General Public License as published by
   6  # the Free Software Foundation, either version 2 of the License, or
   7  # (at your option) any later version.
   8  #
   9  # MantisBT is distributed in the hope that it will be useful,
  10  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  # GNU General Public License for more details.
  13  #
  14  # You should have received a copy of the GNU General Public License
  15  # along with MantisBT.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Tag API
  19   *
  20   * @package CoreAPI
  21   * @subpackage TagAPI
  22   * @author John Reese
  23   * @copyright Copyright (C) 2000 - 2002  Kenzaburo Ito - kenito@300baud.org
  24   * @copyright Copyright (C) 2002 - 2011  MantisBT Team - mantisbt-dev@lists.sourceforge.net
  25   * @link http://www.mantisbt.org
  26   *
  27   * @uses access_api.php
  28   * @uses authentication_api.php
  29   * @uses bug_api.php
  30   * @uses config_api.php
  31   * @uses constant_inc.php
  32   * @uses database_api.php
  33   * @uses error_api.php
  34   * @uses form_api.php
  35   * @uses history_api.php
  36   * @uses lang_api.php
  37   * @uses string_api.php
  38   * @uses user_api.php
  39   * @uses utility_api.php
  40   */
  41  
  42  require_api( 'access_api.php' );
  43  require_api( 'authentication_api.php' );
  44  require_api( 'bug_api.php' );
  45  require_api( 'config_api.php' );
  46  require_api( 'constant_inc.php' );
  47  require_api( 'database_api.php' );
  48  require_api( 'error_api.php' );
  49  require_api( 'form_api.php' );
  50  require_api( 'history_api.php' );
  51  require_api( 'lang_api.php' );
  52  require_api( 'string_api.php' );
  53  require_api( 'user_api.php' );
  54  require_api( 'utility_api.php' );
  55  
  56  /**
  57   * Determine if a tag exists with the given ID.
  58   * @param integer Tag ID
  59   * @return boolean True if tag exists
  60   */
  61  function tag_exists( $p_tag_id ) {
  62      $c_tag_id = db_prepare_int( $p_tag_id );
  63      $t_tag_table = db_get_table( 'tag' );
  64  
  65      $query = "SELECT * FROM $t_tag_table WHERE id=" . db_param();
  66      $result = db_query_bound( $query, Array( $c_tag_id ) );
  67  
  68      return db_num_rows( $result ) > 0;
  69  }
  70  
  71  /**
  72   * Ensure a tag exists with the given ID.
  73   * @param integer Tag ID
  74   */
  75  function tag_ensure_exists( $p_tag_id ) {
  76      if( !tag_exists( $p_tag_id ) ) {
  77          error_parameters( $p_tag_id );
  78          trigger_error( ERROR_TAG_NOT_FOUND, ERROR );
  79      }
  80  }
  81  
  82  /**
  83   * Determine if a given name is unique (not already used).
  84   * Uses a case-insensitive search of the database for existing tags with the same name.
  85   * @param string Tag name
  86   * @return boolean True if name is unique
  87   */
  88  function tag_is_unique( $p_name ) {
  89      $c_name = trim( $p_name );
  90      $t_tag_table = db_get_table( 'tag' );
  91  
  92      $query = 'SELECT id FROM ' . $t_tag_table . ' WHERE ' . db_helper_like( 'name' );
  93      $result = db_query_bound( $query, Array( $c_name ) );
  94  
  95      return db_num_rows( $result ) == 0;
  96  }
  97  
  98  /**
  99   * Ensure that a name is unique.
 100   * @param string Tag name
 101   */
 102  function tag_ensure_unique( $p_name ) {
 103      if( !tag_is_unique( $p_name ) ) {
 104          trigger_error( ERROR_TAG_DUPLICATE, ERROR );
 105      }
 106  }
 107  
 108  /**
 109   * Determine if a given name is valid.
 110   *
 111   * Name must not begin with '+' and '-' characters (they are used for
 112   * filters) and must not contain the configured tag separator.
 113   * The matches parameter allows to also receive an array of regex matches,
 114   * which by default only includes the valid tag name itself.
 115   * The prefix parameter is optional, but allows you to prefix the regex
 116   * check, which is useful for filters, etc.
 117   * @param string Tag name
 118   * @param array Array reference for regex matches
 119   * @param string Prefix regex pattern
 120   * @return boolean True if the name is valid
 121   */
 122  function tag_name_is_valid( $p_name, &$p_matches, $p_prefix = '' ) {
 123      $t_separator = config_get( 'tag_separator' );
 124      $t_pattern = "/^$p_prefix([^\+\-{$t_separator}][^{$t_separator}]*)$/";
 125      return preg_match( $t_pattern, $p_name, $p_matches );
 126  }
 127  
 128  /**
 129   * Ensure a tag name is valid.
 130   * @param string Tag name
 131   */
 132  function tag_ensure_name_is_valid( $p_name ) {
 133      $t_matches = array();
 134      if( !tag_name_is_valid( $p_name, $t_matches ) ) {
 135          trigger_error( ERROR_TAG_NAME_INVALID, ERROR );
 136      }
 137  }
 138  
 139  /**
 140   * Compare two tag rows based on tag name.
 141   * @param array Tag row 1
 142   * @param array Tag row 2
 143   * @return integer -1 when Tag 1 < Tag 2, 1 when Tag 1 > Tag 2, 0 otherwise
 144   */
 145  function tag_cmp_name( $p_tag1, $p_tag2 ) {
 146      return strcasecmp( $p_tag1['name'], $p_tag2['name'] );
 147  }
 148  
 149  /**
 150   * Parse a form input string to extract existing and new tags.
 151   * When given a string, parses for tag names separated by configured separator,
 152   * then returns an array of tag rows for each tag.  Existing tags get the full
 153   * row of information returned.  If the tag does not exist, a row is returned with
 154   * id = -1 and the tag name, and if the name is invalid, a row is returned with
 155   * id = -2 and the tag name.  The resulting array is then sorted by tag name.
 156   * @param string Input string to parse
 157   * @return array Rows of tags parsed from input string
 158   */
 159  function tag_parse_string( $p_string ) {
 160      $t_tags = array();
 161  
 162      $t_strings = explode( config_get( 'tag_separator' ), $p_string );
 163      foreach( $t_strings as $t_name ) {
 164          $t_name = trim( $t_name );
 165          if( is_blank( $t_name ) ) {
 166              continue;
 167          }
 168  
 169          $t_matches = array();
 170          $t_tag_row = tag_get_by_name( $t_name );
 171          if( $t_tag_row !== false ) {
 172              $t_tags[] = $t_tag_row;
 173          } else {
 174              if( tag_name_is_valid( $t_name, $t_matches ) ) {
 175                  $t_id = -1;
 176              } else {
 177                  $t_id = -2;
 178              }
 179              $t_tags[] = array(
 180                  'id' => $t_id,
 181                  'name' => $t_name,
 182              );
 183          }
 184      }
 185      usort( $t_tags, 'tag_cmp_name' );
 186      return $t_tags;
 187  }
 188  
 189  /**
 190   * Parse a filter string to extract existing and new tags.
 191   * When given a string, parses for tag names separated by configured separator,
 192   * then returns an array of tag rows for each tag.  Existing tags get the full
 193   * row of information returned.  If the tag does not exist, a row is returned with
 194   * id = -1 and the tag name, and if the name is invalid, a row is returned with
 195   * id = -2 and the tag name.  The resulting array is then sorted by tag name.
 196   * @param string Filter string to parse
 197   * @return array Rows of tags parsed from filter string
 198   */
 199  function tag_parse_filters( $p_string ) {
 200      $t_tags = array();
 201      $t_prefix = '[+-]{0,1}';
 202  
 203      $t_strings = explode( config_get( 'tag_separator' ), $p_string );
 204      foreach( $t_strings as $t_name ) {
 205          $t_name = trim( $t_name );
 206          $t_matches = array();
 207  
 208          if( !is_blank( $t_name ) && tag_name_is_valid( $t_name, $t_matches, $t_prefix ) ) {
 209              $t_tag_row = tag_get_by_name( $t_matches[1] );
 210              if( $t_tag_row !== false ) {
 211                  $t_filter = utf8_substr( $t_name, 0, 1 );
 212  
 213                  if( '+' == $t_filter ) {
 214                      $t_tag_row['filter'] = 1;
 215                  } else if( '-' == $t_filter ) {
 216                      $t_tag_row['filter'] = -1;
 217                  } else {
 218                      $t_tag_row['filter'] = 0;
 219                  }
 220  
 221                  $t_tags[] = $t_tag_row;
 222              }
 223          } else {
 224              continue;
 225          }
 226      }
 227      usort( $t_tags, 'tag_cmp_name' );
 228      return $t_tags;
 229  }
 230  
 231  # CRUD
 232  /**
 233   * Return a tag row for the given ID.
 234   * @param integer Tag ID
 235   * @return array Tag row
 236   */
 237  function tag_get( $p_tag_id ) {
 238      tag_ensure_exists( $p_tag_id );
 239  
 240      $c_tag_id = db_prepare_int( $p_tag_id );
 241  
 242      $t_tag_table = db_get_table( 'tag' );
 243  
 244      $query = "SELECT * FROM $t_tag_table
 245                      WHERE id=" . db_param();
 246      $result = db_query_bound( $query, Array( $c_tag_id ) );
 247  
 248      if( 0 == db_num_rows( $result ) ) {
 249          return false;
 250      }
 251      $row = db_fetch_array( $result );
 252  
 253      return $row;
 254  }
 255  
 256  /**
 257   * Return a tag row for the given name.
 258   * @param string Tag name
 259   * @return Tag row
 260   */
 261  function tag_get_by_name( $p_name ) {
 262      $t_tag_table = db_get_table( 'tag' );
 263  
 264      $query = "SELECT * FROM $t_tag_table
 265                      WHERE " . db_helper_like( 'name' );
 266      $result = db_query_bound( $query, Array( $p_name ) );
 267  
 268      if( 0 == db_num_rows( $result ) ) {
 269          return false;
 270      }
 271      $row = db_fetch_array( $result );
 272  
 273      return $row;
 274  }
 275  
 276  /**
 277   * Return a single field from a tag row for the given ID.
 278   * @param integer Tag ID
 279   * @param string Field name
 280   * @return mixed Field value
 281   */
 282  function tag_get_field( $p_tag_id, $p_field_name ) {
 283      $row = tag_get( $p_tag_id );
 284  
 285      if( isset( $row[$p_field_name] ) ) {
 286          return $row[$p_field_name];
 287      } else {
 288          error_parameters( $p_field_name );
 289          trigger_error( ERROR_DB_FIELD_NOT_FOUND, WARNING );
 290          return '';
 291      }
 292  }
 293  
 294  /**
 295   * Create a tag with the given name, creator, and description.
 296   * Defaults to the currently logged in user, and a blank description.
 297   * @param string Tag name
 298   * @param integer User ID
 299   * @param string Description
 300   * @return integer Tag ID
 301   */
 302  function tag_create( $p_name, $p_user_id = null, $p_description = '' ) {
 303      access_ensure_global_level( config_get( 'tag_create_threshold' ) );
 304  
 305      tag_ensure_name_is_valid( $p_name );
 306      tag_ensure_unique( $p_name );
 307  
 308      if( null == $p_user_id ) {
 309          $p_used_id = auth_get_current_user_id();
 310      } else {
 311          user_ensure_exists( $p_user_id );
 312      }
 313  
 314      $c_user_id = db_prepare_int( $p_user_id );
 315      $c_date_created = db_now();
 316  
 317      $t_tag_table = db_get_table( 'tag' );
 318  
 319      $query = "INSERT INTO $t_tag_table
 320                  ( user_id,
 321                    name,
 322                    description,
 323                    date_created,
 324                    date_updated
 325                  )
 326                  VALUES
 327                  ( " . db_param() . ",
 328                    " . db_param() . ",
 329                    " . db_param() . ",
 330                    " . db_param() . ",
 331                    " . db_param() . "
 332                  )";
 333  
 334      db_query_bound( $query, Array( $c_user_id, trim( $p_name ), trim( $p_description ), $c_date_created, $c_date_created ) );
 335      return db_insert_id( $t_tag_table );
 336  }
 337  
 338  /**
 339   * Update a tag with given name, creator, and description.
 340   * @param integer Tag ID
 341   * @param string Tag name
 342   * @param integer User ID
 343   * @param string Description
 344   */
 345  function tag_update( $p_tag_id, $p_name, $p_user_id, $p_description ) {
 346      user_ensure_exists( $p_user_id );
 347  
 348      if( auth_get_current_user_id() == tag_get_field( $p_tag_id, 'user_id' ) ) {
 349          $t_update_level = config_get( 'tag_edit_own_threshold' );
 350      } else {
 351          $t_update_level = config_get( 'tag_edit_threshold' );
 352      }
 353  
 354      access_ensure_global_level( $t_update_level );
 355  
 356      tag_ensure_name_is_valid( $p_name );
 357  
 358      $t_tag_name = tag_get_field( $p_tag_id, 'name' );
 359  
 360      $t_rename = false;
 361      if( utf8_strtolower( $p_name ) != utf8_strtolower( $t_tag_name ) ) {
 362          tag_ensure_unique( $p_name );
 363          $t_rename = true;
 364      }
 365  
 366      $c_tag_id = trim( db_prepare_int( $p_tag_id ) );
 367      $c_date_updated = db_now();
 368  
 369      $t_tag_table = db_get_table( 'tag' );
 370  
 371      $query = "UPDATE $t_tag_table
 372                      SET user_id=" . db_param() . ",
 373                          name=" . db_param() . ",
 374                          description=" . db_param() . ",
 375                          date_updated=" . db_param() . "
 376                      WHERE id=" . db_param();
 377      db_query_bound( $query, Array( (int)$p_user_id, $p_name, $p_description, $c_date_updated, $c_tag_id ) );
 378  
 379      if( $t_rename ) {
 380          $t_bugs = tag_get_bugs_attached( $p_tag_id );
 381  
 382          foreach( $t_bugs as $t_bug_id ) {
 383              history_log_event_special( $t_bug_id, TAG_RENAMED, $t_tag_name, $p_name );
 384          }
 385      }
 386  
 387      return true;
 388  }
 389  
 390  /**
 391   * Delete a tag with the given ID.
 392   * @param integer Tag ID
 393   */
 394  function tag_delete( $p_tag_id ) {
 395      tag_ensure_exists( $p_tag_id );
 396  
 397      access_ensure_global_level( config_get( 'tag_edit_threshold' ) );
 398  
 399      $t_bugs = tag_get_bugs_attached( $p_tag_id );
 400      foreach( $t_bugs as $t_bug_id ) {
 401          tag_bug_detach( $p_tag_id, $t_bug_id );
 402      }
 403  
 404      $c_tag_id = db_prepare_int( $p_tag_id );
 405  
 406      $t_tag_table = db_get_table( 'tag' );
 407      $t_bug_tag_table = db_get_table( 'bug_tag' );
 408  
 409      $query = "DELETE FROM $t_tag_table
 410                      WHERE id=" . db_param();
 411      db_query_bound( $query, Array( $c_tag_id ) );
 412  
 413      return true;
 414  }
 415  
 416  /**
 417   * Gets the candidates for the specified bug.  These are existing tags
 418   * that are not associated with the bug already.
 419   *
 420   * @param int $p_bug_id  The bug id, if 0 returns all tags.
 421   * @returns The array of tag rows, each with id, name, and description.
 422   */
 423  function tag_get_candidates_for_bug( $p_bug_id ) {
 424      $t_tag_table = db_get_table( 'tag' );
 425  
 426      $t_params = array();
 427      if ( 0 != $p_bug_id ) {
 428          $t_bug_tag_table = db_get_table( 'bug_tag' );
 429  
 430          if ( db_is_mssql() ) {
 431              $t_params[] = $p_bug_id;
 432              $query = "SELECT t.id FROM $t_tag_table t
 433                      LEFT JOIN $t_bug_tag_table b ON t.id=b.tag_id
 434                      WHERE b.bug_id IS NULL OR b.bug_id != " . db_param();
 435              $result = db_query_bound( $query, $t_params );
 436  
 437              $t_subquery_results = array();
 438  
 439              while( $row = db_fetch_array( $result ) ) {
 440                  $t_subquery_results[] = (int)$row;
 441              }
 442              $query = "SELECT id, name, description FROM $t_tag_table WHERE id IN ( " . implode( ', ', $t_subquery_results ) . ')';
 443          } else {
 444              $query = "SELECT id, name, description FROM $t_tag_table WHERE id IN (
 445                      SELECT t.id FROM $t_tag_table t
 446                      LEFT JOIN $t_bug_tag_table b ON t.id=b.tag_id
 447                      WHERE b.bug_id IS NULL OR b.bug_id != " . db_param() .
 448                  ')';
 449          }
 450          $t_params[] = $p_bug_id;
 451      } else {
 452          $query = 'SELECT id, name, description FROM ' . $t_tag_table;
 453      }
 454  
 455      $query .= ' ORDER BY name ASC ';
 456      $result = db_query_bound( $query, $t_params );
 457  
 458      $t_results_to_return = array();
 459  
 460      while( $row = db_fetch_array( $result ) ) {
 461          $t_results_to_return[] = $row;
 462      }
 463  
 464      return $t_results_to_return;
 465  }
 466  
 467  # Associative
 468  /**
 469   * Determine if a tag is attached to a bug.
 470   * @param integer Tag ID
 471   * @param integer Bug ID
 472   * @return boolean True if the tag is attached
 473   */
 474  function tag_bug_is_attached( $p_tag_id, $p_bug_id ) {
 475      $c_tag_id = db_prepare_int( $p_tag_id );
 476      $c_bug_id = db_prepare_int( $p_bug_id );
 477  
 478      $t_bug_tag_table = db_get_table( 'bug_tag' );
 479  
 480      $query = "SELECT * FROM $t_bug_tag_table
 481                      WHERE tag_id=" . db_param() . " AND bug_id=" . db_param();
 482      $result = db_query_bound( $query, Array( $c_tag_id, $c_bug_id ) );
 483      return( db_num_rows( $result ) > 0 );
 484  }
 485  
 486  /**
 487   * Return the tag attachment row.
 488   * @param integer Tag ID
 489   * @param integer Bug ID
 490   * @return array Tag attachment row
 491   */
 492  function tag_bug_get_row( $p_tag_id, $p_bug_id ) {
 493      $c_tag_id = db_prepare_int( $p_tag_id );
 494      $c_bug_id = db_prepare_int( $p_bug_id );
 495  
 496      $t_bug_tag_table = db_get_table( 'bug_tag' );
 497  
 498      $query = "SELECT * FROM $t_bug_tag_table
 499                      WHERE tag_id=" . db_param() . " AND bug_id=" . db_param();
 500      $result = db_query_bound( $query, Array( $c_tag_id, $c_bug_id ) );
 501  
 502      if( db_num_rows( $result ) == 0 ) {
 503          trigger_error( TAG_NOT_ATTACHED, ERROR );
 504      }
 505      return db_fetch_array( $result );
 506  }
 507  
 508  /**
 509   * Return an array of tags attached to a given bug sorted by tag name.
 510   * @param Bug ID
 511   * @return array Array of tag rows with attachement information
 512   */
 513  function tag_bug_get_attached( $p_bug_id ) {
 514      $c_bug_id = db_prepare_int( $p_bug_id );
 515  
 516      $t_tag_table = db_get_table( 'tag' );
 517      $t_bug_tag_table = db_get_table( 'bug_tag' );
 518  
 519      $query = "SELECT t.*, b.user_id as user_attached, b.date_attached
 520                      FROM $t_tag_table as t
 521                      LEFT JOIN $t_bug_tag_table as b
 522                          on t.id=b.tag_id
 523                      WHERE b.bug_id=" . db_param();
 524      $result = db_query_bound( $query, Array( $c_bug_id ) );
 525  
 526      $rows = array();
 527      while( $row = db_fetch_array( $result ) ) {
 528          $rows[] = $row;
 529      }
 530  
 531      usort( $rows, 'tag_cmp_name' );
 532      return $rows;
 533  }
 534  
 535  /**
 536   * Return an array of bugs that a tag is attached to.
 537   * @param integer Tag ID
 538   * @return array Array of bug ID's.
 539   */
 540  function tag_get_bugs_attached( $p_tag_id ) {
 541      $c_tag_id = db_prepare_int( $p_tag_id );
 542  
 543      $t_bug_tag_table = db_get_table( 'bug_tag' );
 544  
 545      $query = "SELECT bug_id FROM $t_bug_tag_table
 546                      WHERE tag_id=" . db_param();
 547      $result = db_query_bound( $query, Array( $c_tag_id ) );
 548  
 549      $bugs = array();
 550      while( $row = db_fetch_array( $result ) ) {
 551          $bugs[] = $row['bug_id'];
 552      }
 553  
 554      return $bugs;
 555  }
 556  
 557  /**
 558   * Attach a tag to a bug.
 559   * @param integer Tag ID
 560   * @param integer Bug ID
 561   * @param integer User ID
 562   */
 563  function tag_bug_attach( $p_tag_id, $p_bug_id, $p_user_id = null ) {
 564      access_ensure_bug_level( config_get( 'tag_attach_threshold' ), $p_bug_id, $p_user_id );
 565  
 566      tag_ensure_exists( $p_tag_id );
 567  
 568      if( tag_bug_is_attached( $p_tag_id, $p_bug_id ) ) {
 569          trigger_error( TAG_ALREADY_ATTACHED, ERROR );
 570      }
 571  
 572      if( null == $p_user_id ) {
 573          $p_used_id = auth_get_current_user_id();
 574      } else {
 575          user_ensure_exists( $p_user_id );
 576      }
 577  
 578      $c_tag_id = db_prepare_int( $p_tag_id );
 579      $c_bug_id = db_prepare_int( $p_bug_id );
 580      $c_user_id = db_prepare_int( $p_user_id );
 581  
 582      $t_bug_tag_table = db_get_table( 'bug_tag' );
 583  
 584      $query = "INSERT INTO $t_bug_tag_table
 585                      ( tag_id,
 586                        bug_id,
 587                        user_id,
 588                        date_attached
 589                      )
 590                      VALUES
 591                      ( " . db_param() . ",
 592                        " . db_param() . ",
 593                        " . db_param() . ",
 594                        " . db_param() . "
 595                      )";
 596      db_query_bound( $query, Array( $c_tag_id, $c_bug_id, $c_user_id, db_now() ) );
 597  
 598      $t_tag_name = tag_get_field( $p_tag_id, 'name' );
 599      history_log_event_special( $p_bug_id, TAG_ATTACHED, $t_tag_name );
 600  
 601      # updated the last_updated date
 602      bug_update_date( $p_bug_id );
 603  
 604      return true;
 605  }
 606  
 607  /**
 608   * Detach a tag from a bug.
 609   * @param integer Tag ID
 610   * @param integer Bug ID
 611   * @param boolean Add history entries to bug
 612   * @param integer User Id (or null for current logged in user)
 613   */
 614  function tag_bug_detach( $p_tag_id, $p_bug_id, $p_add_history = true, $p_user_id = null ) {
 615      if( $p_user_id === null ) {
 616          $t_user_id = auth_get_current_user_id();
 617      } else {
 618          $t_user_id = $p_user_id;
 619      }
 620  
 621      if( !tag_bug_is_attached( $p_tag_id, $p_bug_id ) ) {
 622          trigger_error( TAG_NOT_ATTACHED, ERROR );
 623      }
 624  
 625      $t_tag_row = tag_bug_get_row( $p_tag_id, $p_bug_id);
 626      if( $t_user_id == tag_get_field( $p_tag_id, 'user_id' ) || $t_user_id == $t_tag_row[ 'user_id' ] ) {
 627          $t_detach_level = config_get( 'tag_detach_own_threshold' );
 628      } else {
 629          $t_detach_level = config_get( 'tag_detach_threshold' );
 630      }
 631  
 632      access_ensure_bug_level( $t_detach_level, $p_bug_id, $t_user_id );
 633  
 634      $c_tag_id = db_prepare_int( $p_tag_id );
 635      $c_bug_id = db_prepare_int( $p_bug_id );
 636  
 637      $t_bug_tag_table = db_get_table( 'bug_tag' );
 638  
 639      $query = "DELETE FROM $t_bug_tag_table
 640                      WHERE tag_id=" . db_param() . ' AND bug_id=' . db_param();
 641      db_query_bound( $query, Array( $c_tag_id, $c_bug_id ) );
 642  
 643      if( $p_add_history ) {
 644          $t_tag_name = tag_get_field( $p_tag_id, 'name' );
 645          history_log_event_special( $p_bug_id, TAG_DETACHED, $t_tag_name );
 646      }
 647  
 648      # updated the last_updated date
 649      bug_update_date( $p_bug_id );
 650  
 651      return true;
 652  }
 653  
 654  /**
 655   * Detach all tags from a given bug.
 656   * @param integer Bug ID
 657   * @param boolean Add history entries to bug
 658   * @param integer User Id (or null for current logged in user)
 659   */
 660  function tag_bug_detach_all( $p_bug_id, $p_add_history = true, $p_user_id = null ) {
 661      $t_tags = tag_bug_get_attached( $p_bug_id );
 662      foreach( $t_tags as $t_tag_row ) {
 663          tag_bug_detach( $t_tag_row['id'], $p_bug_id, $p_add_history, $p_user_id );
 664      }
 665  }
 666  
 667  # Display
 668  /**
 669   * Display a tag hyperlink.
 670   * If a bug ID is passed, the tag link will include a detach link if the
 671   * user has appropriate privileges.
 672   * @param array Tag row
 673   * @param integer Bug ID
 674   */
 675  function tag_display_link( $p_tag_row, $p_bug_id = 0 ) {
 676      static $t_security_token = null;
 677      if( is_null( $t_security_token ) ) {
 678          $t_security_token = htmlspecialchars( form_security_param( 'tag_detach' ) );
 679      }
 680  
 681      if( auth_get_current_user_id() == $p_tag_row['user_attached'] || auth_get_current_user_id() == $p_tag_row['user_id'] ) {
 682          $t_detach = config_get( 'tag_detach_own_threshold' );
 683      } else {
 684          $t_detach = config_get( 'tag_detach_threshold' );
 685      }
 686  
 687      $t_name = string_display_line( $p_tag_row['name'] );
 688      $t_description = string_display_line( $p_tag_row['description'] );
 689  
 690      echo "<a href='tag_view_page.php?tag_id=$p_tag_row[id]' title='$t_description'>$t_name</a>";
 691  
 692      if( $p_bug_id > 0 && access_has_bug_level( $t_detach, $p_bug_id ) ) {
 693          $t_tooltip = string_html_specialchars( sprintf( lang_get( 'tag_detach' ), $t_name ) );
 694          echo " <a href='tag_detach.php?bug_id=$p_bug_id&amp;tag_id=$p_tag_row[id]$t_security_token'><img src='images/delete.png' class='delete-icon' title=\"$t_tooltip\" alt=\"X\"/></a>";
 695      }
 696  
 697      return true;
 698  }
 699  
 700  /**
 701   * Display a list of attached tag hyperlinks separated by the configured hyperlinks.
 702   * @param Bug ID
 703   */
 704  function tag_display_attached( $p_bug_id ) {
 705      $t_tag_rows = tag_bug_get_attached( $p_bug_id );
 706  
 707      if( count( $t_tag_rows ) == 0 ) {
 708          echo lang_get( 'tag_none_attached' );
 709      } else {
 710          $i = 0;
 711          foreach( $t_tag_rows as $t_tag ) {
 712              echo( $i > 0 ? config_get( 'tag_separator' ) . ' ' : '' );
 713              tag_display_link( $t_tag, $p_bug_id );
 714              $i++;
 715          }
 716      }
 717  
 718      return true;
 719  }
 720  
 721  # Statistics
 722  /**
 723   * Get the number of bugs a given tag is attached to.
 724   * @param integer Tag ID
 725   * @return integer Number of attached bugs
 726   */
 727  function tag_stats_attached( $p_tag_id ) {
 728      $c_tag_id = db_prepare_int( $p_tag_id );
 729      $t_bug_tag_table = db_get_table( 'bug_tag' );
 730  
 731      $query = "SELECT COUNT(*) FROM $t_bug_tag_table
 732                      WHERE tag_id=" . db_param();
 733      $result = db_query_bound( $query, Array( $c_tag_id ) );
 734  
 735      return db_result( $result );
 736  }
 737  
 738  /**
 739   * Get a list of related tags.
 740   * Returns a list of tags that are the most related to the given tag,
 741   * based on the number of times they have been attached to the same bugs.
 742   * Defaults to a list of five tags.
 743   * @param integer Tag ID
 744   * @param integer List size
 745   * @return array Array of tag rows, with share count added
 746   */
 747  function tag_stats_related( $p_tag_id, $p_limit = 5 ) {
 748      $t_bug_table = db_get_table( 'bug' );
 749      $t_tag_table = db_get_table( 'tag' );
 750      $t_bug_tag_table = db_get_table( 'bug_tag' );
 751      $t_project_user_list_table = db_get_table( 'project_user_list' );
 752      $t_user_table = db_get_table( 'user' );
 753  
 754      $c_tag_id = db_prepare_int( $p_tag_id );
 755      $c_user_id = auth_get_current_user_id();
 756  
 757      $subquery = "SELECT b.id FROM $t_bug_table AS b
 758                      LEFT JOIN $t_project_user_list_table AS p
 759                          ON p.project_id=b.project_id AND p.user_id=" . db_param() . "
 760                      JOIN $t_user_table AS u
 761                          ON u.id=" . db_param() . "
 762                      JOIN $t_bug_tag_table AS t
 763                          ON t.bug_id=b.id
 764                      WHERE ( p.access_level>b.view_state OR u.access_level>b.view_state )
 765                          AND t.tag_id=" . db_param();
 766  
 767      $query = "SELECT * FROM $t_bug_tag_table
 768                      WHERE tag_id != " . db_param() . "
 769                          AND bug_id IN ( $subquery ) ";
 770  
 771      $result = db_query_bound( $query, Array( /*query*/ $c_tag_id, /*subquery*/ $c_user_id, $c_user_id, $c_tag_id ) );
 772  
 773      $t_tag_counts = array();
 774      while( $row = db_fetch_array( $result ) ) {
 775          if( !isset( $t_tag_counts[$row['tag_id']] ) ) {
 776              $t_tag_counts[$row['tag_id']] = 1;
 777          } else {
 778              $t_tag_counts[$row['tag_id']]++;
 779          }
 780      }
 781  
 782      arsort( $t_tag_counts );
 783  
 784      $t_tags = array();
 785      $i = 1;
 786      foreach( $t_tag_counts as $t_tag_id => $t_count ) {
 787          $t_tag_row = tag_get( $t_tag_id );
 788          $t_tag_row['count'] = $t_count;
 789          $t_tags[] = $t_tag_row;
 790          $i++;
 791          if( $i > $p_limit ) {
 792              break;
 793          }
 794      }
 795  
 796      return $t_tags;
 797  }
 798  


Generated: Thu Jul 28 15:48:31 2011 Cross-referenced by PHPXref 0.7