[ Index ]

PHP Cross Reference of Eventum

title

Body

[close]

/include/ -> class.custom_field.php (source)

   1  <?php
   2  /* vim: set expandtab tabstop=4 shiftwidth=4 encoding=utf-8: */
   3  // +----------------------------------------------------------------------+
   4  // | Eventum - Issue Tracking System                                      |
   5  // +----------------------------------------------------------------------+
   6  // | Copyright (c) 2003, 2004, 2005, 2006, 2007 MySQL AB                  |
   7  // |                                                                      |
   8  // | This program is free software; you can redistribute it and/or modify |
   9  // | it under the terms of the GNU General Public License as published by |
  10  // | the Free Software Foundation; either version 2 of the License, or    |
  11  // | (at your option) any later version.                                  |
  12  // |                                                                      |
  13  // | This program is distributed in the hope that it will be useful,      |
  14  // | but WITHOUT ANY WARRANTY; without even the implied warranty of       |
  15  // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        |
  16  // | GNU General Public License for more details.                         |
  17  // |                                                                      |
  18  // | You should have received a copy of the GNU General Public License    |
  19  // | along with this program; if not, write to:                           |
  20  // |                                                                      |
  21  // | Free Software Foundation, Inc.                                       |
  22  // | 59 Temple Place - Suite 330                                          |
  23  // | Boston, MA 02111-1307, USA.                                          |
  24  // +----------------------------------------------------------------------+
  25  // | Authors: João Prado Maia <jpm@mysql.com>                             |
  26  // +----------------------------------------------------------------------+
  27  //
  28  // @(#) $Id: class.custom_field.php 3389 2007-10-21 01:33:24Z balsdorf $
  29  //
  30  
  31  require_once (APP_INC_PATH . "class.error_handler.php");
  32  require_once (APP_INC_PATH . "class.misc.php");
  33  require_once (APP_INC_PATH . "class.issue.php");
  34  require_once (APP_INC_PATH . "class.user.php");
  35  require_once (APP_INC_PATH . "class.auth.php");
  36  require_once (APP_INC_PATH . "class.history.php");
  37  
  38  /**
  39   * Class to handle the business logic related to the administration
  40   * of custom fields in the system.
  41   *
  42   * @version 1.0
  43   * @author João Prado Maia <jpm@mysql.com>
  44   */
  45  
  46  class Custom_Field
  47  {
  48      /**
  49       * Method used to remove a group of custom field options.
  50       *
  51       * @access  public
  52       * @param   array $fld_id The list of custom field IDs
  53       * @param   array $fld_id The list of custom field option IDs
  54       * @return  boolean
  55       */
  56      function removeOptions($fld_id, $cfo_id)
  57      {
  58          $fld_id = Misc::escapeInteger($fld_id);
  59          $cfo_id = Misc::escapeInteger($cfo_id);
  60          if (!is_array($fld_id)) {
  61              $fld_id = array($fld_id);
  62          }
  63          if (!is_array($cfo_id)) {
  64              $cfo_id = array($cfo_id);
  65          }
  66          $stmt = "DELETE FROM
  67                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option
  68                   WHERE
  69                      cfo_id IN (" . implode(",", $cfo_id) . ")";
  70          $res = $GLOBALS["db_api"]->dbh->query($stmt);
  71          if (PEAR::isError($res)) {
  72              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
  73              return false;
  74          } else {
  75              // also remove any custom field option that is currently assigned to an issue
  76              // XXX: review this
  77              $stmt = "DELETE FROM
  78                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
  79                       WHERE
  80                          icf_fld_id IN (" . implode(", ", $fld_id) . ") AND
  81                          icf_value IN (" . implode(", ", $cfo_id) . ")";
  82              $GLOBALS["db_api"]->dbh->query($stmt);
  83              return true;
  84          }
  85      }
  86  
  87  
  88      /**
  89       * Method used to add possible options into a given custom field.
  90       *
  91       * @access  public
  92       * @param   integer $fld_id The custom field ID
  93       * @param   array $options The list of options that need to be added
  94       * @return  integer 1 if the insert worked, -1 otherwise
  95       */
  96      function addOptions($fld_id, $options)
  97      {
  98          $fld_id = Misc::escapeInteger($fld_id);
  99          if (!is_array($options)) {
 100              $options = array($options);
 101          }
 102          foreach ($options as $option) {
 103              $stmt = "INSERT INTO
 104                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option
 105                       (
 106                          cfo_fld_id,
 107                          cfo_value
 108                       ) VALUES (
 109                          $fld_id,
 110                          '" . Misc::escapeString($option) . "'
 111                       )";
 112              $res = $GLOBALS["db_api"]->dbh->query($stmt);
 113              if (PEAR::isError($res)) {
 114                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 115                  return -1;
 116              }
 117          }
 118          return 1;
 119      }
 120  
 121  
 122      /**
 123       * Method used to update an existing custom field option value.
 124       *
 125       * @access  public
 126       * @param   integer $cfo_id The custom field option ID
 127       * @param   string $cfo_value The custom field option value
 128       * @return  boolean
 129       */
 130      function updateOption($cfo_id, $cfo_value)
 131      {
 132          $cfo_id = Misc::escapeInteger($cfo_id);
 133          $stmt = "UPDATE
 134                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option
 135                   SET
 136                      cfo_value='" . Misc::escapeString($cfo_value) . "'
 137                   WHERE
 138                      cfo_id=" . $cfo_id;
 139          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 140          if (PEAR::isError($res)) {
 141              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 142              return false;
 143          } else {
 144              return true;
 145          }
 146      }
 147  
 148  
 149      /**
 150       * Method used to update the values stored in the database.
 151       *
 152       * @access  public
 153       * @return  integer 1 if the update worked properly, any other value otherwise
 154       */
 155      function updateValues()
 156      {
 157          $prj_id = Auth::getCurrentProject();
 158          $issue_id = Misc::escapeInteger($_POST["issue_id"]);
 159  
 160          $old_values = Custom_Field::getValuesByIssue($prj_id, $issue_id);
 161  
 162  
 163          if ((isset($_POST['custom_fields'])) && (count($_POST['custom_fields']) > 0)) {
 164              // get the types for all of the custom fields being submitted
 165              $stmt = "SELECT
 166                          fld_id,
 167                          fld_type
 168                       FROM
 169                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
 170                       WHERE
 171                          fld_id IN (" . implode(", ", Misc::escapeInteger(@array_keys($_POST['custom_fields']))) . ")";
 172              $field_types = $GLOBALS["db_api"]->dbh->getAssoc($stmt);
 173  
 174              // get the titles for all of the custom fields being submitted
 175              $stmt = "SELECT
 176                          fld_id,
 177                          fld_title
 178                       FROM
 179                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
 180                       WHERE
 181                          fld_id IN (" . implode(", ", Misc::escapeInteger(@array_keys($_POST['custom_fields']))) . ")";
 182              $field_titles = $GLOBALS["db_api"]->dbh->getAssoc($stmt);
 183  
 184              $updated_fields = array();
 185              foreach ($_POST["custom_fields"] as $fld_id => $value) {
 186  
 187                  $fld_id = Misc::escapeInteger($fld_id);
 188  
 189                  // security check
 190                  $sql = "SELECT
 191                              fld_min_role
 192                          FROM
 193                              " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
 194                          WHERE
 195                              fld_id = $fld_id";
 196                  $min_role = $GLOBALS["db_api"]->dbh->getOne($sql);
 197                  if ($min_role > Auth::getCurrentRole()) {
 198                      continue;
 199                  }
 200  
 201                  $option_types = array(
 202                      'multiple',
 203                      'combo'
 204                  );
 205                  if (!in_array($field_types[$fld_id], $option_types)) {
 206                      // check if this is a date field
 207                      if ($field_types[$fld_id] == 'date') {
 208                          $value = $value['Year'] . "-" . $value['Month'] . "-" . $value['Day'];
 209                          if ($value == '--') {
 210                              $value = '';
 211                          }
 212                      } elseif ($field_types[$fld_id] == 'integer') {
 213                          $value = Misc::escapeInteger($value);
 214                      }
 215                      $fld_db_name = Custom_Field::getDBValueFieldNameByType($field_types[$fld_id]);
 216  
 217                      // first check if there is actually a record for this field for the issue
 218                      $stmt = "SELECT
 219                                  icf_id,
 220                                  $fld_db_name as value
 221                               FROM
 222                                  " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
 223                               WHERE
 224                                  icf_iss_id=" . $issue_id . " AND
 225                                  icf_fld_id=$fld_id";
 226                      $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC);
 227                      if (PEAR::isError($res)) {
 228                          Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 229                          return -1;
 230                      }
 231                      $icf_id = $res['icf_id'];
 232                      $old_value = $res['value'];
 233  
 234                      if ($old_value == $value) {
 235                          continue;
 236                      }
 237  
 238                      if (empty($value)) {
 239                          $value = 'NULL';
 240                      } else {
 241                          $value = "'" . Misc::escapeString($value) . "'";
 242                      }
 243  
 244                      if (empty($icf_id)) {
 245                          // record doesn't exist, insert new record
 246                          $stmt = "INSERT INTO
 247                                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
 248                                   (
 249                                      icf_iss_id,
 250                                      icf_fld_id,
 251                                      $fld_db_name
 252                                   ) VALUES (
 253                                      " . $issue_id . ",
 254                                      $fld_id,
 255                                      $value
 256                                   )";
 257                          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 258                          if (PEAR::isError($res)) {
 259                              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 260                              return -1;
 261                          }
 262                      } else {
 263                          // record exists, update it
 264                          $stmt = "UPDATE
 265                                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
 266                                   SET
 267                                      $fld_db_name=$value
 268                                   WHERE
 269                                      icf_id=$icf_id";
 270                          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 271                          if (PEAR::isError($res)) {
 272                              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 273                              return -1;
 274                          }
 275                      }
 276                      if ($field_types[$fld_id] == 'textarea') {
 277                          $updated_fields[$field_titles[$fld_id]] = '';
 278                      } else {
 279                          $updated_fields[$field_titles[$fld_id]] = History::formatChanges($old_value, $value);
 280                      }
 281                  } else {
 282                      $old_value = Custom_Field::getDisplayValue($_POST['issue_id'], $fld_id, true);
 283  
 284                      if (!is_array($old_value)) {
 285                          $old_value = array($old_value);
 286                      }
 287                      if (!is_array($value)) {
 288                          $value = array($value);
 289                      }
 290                      if ((count(array_diff($old_value, $value)) > 0) || (count(array_diff($value, $old_value)) > 0)) {
 291  
 292                          $old_display_value = Custom_Field::getDisplayValue($_POST['issue_id'], $fld_id);
 293                          // need to remove all associated options from issue_custom_field and then
 294                          // add the selected options coming from the form
 295                          Custom_Field::removeIssueAssociation($fld_id, $_POST["issue_id"]);
 296                          if (@count($value) > 0) {
 297                              Custom_Field::associateIssue($_POST["issue_id"], $fld_id, $value);
 298                          }
 299                          $new_display_value = Custom_Field::getDisplayValue($_POST['issue_id'], $fld_id);
 300                          $updated_fields[$field_titles[$fld_id]] = History::formatChanges($old_display_value, $new_display_value);
 301                      }
 302                  }
 303              }
 304  
 305              Workflow::handleCustomFieldsUpdated($prj_id, $issue_id, $old_values, Custom_Field::getValuesByIssue($prj_id, $issue_id));
 306              Issue::markAsUpdated($_POST["issue_id"]);
 307              // need to save a history entry for this
 308  
 309              if (count($updated_fields) > 0) {
 310                  // log the changes
 311                  $changes = '';
 312                  $i = 0;
 313                  foreach ($updated_fields as $key => $value) {
 314                      if ($i > 0) {
 315                          $changes .= "; ";
 316                      }
 317                      if (!empty($value)) {
 318                          $changes .= "$key: $value";
 319                      } else {
 320                          $changes .= "$key";
 321                      }
 322                      $i++;
 323                  }
 324                  History::add($_POST["issue_id"], Auth::getUserID(), History::getTypeID('custom_field_updated'), ev_gettext('Custom field updated (%1$s) by %2$s', $changes, User::getFullName(Auth::getUserID())));
 325              }
 326          }
 327          return 1;
 328      }
 329  
 330  
 331      /**
 332       * Method used to associate a custom field value to a given
 333       * issue ID.
 334       *
 335       * @access  public
 336       * @param   integer $iss_id The issue ID
 337       * @param   integer $fld_id The custom field ID
 338       * @param   string  $value The custom field value
 339       * @return  boolean Whether the association worked or not
 340       */
 341      function associateIssue($iss_id, $fld_id, $value)
 342      {
 343          // check if this is a date field
 344          $fld_details = Custom_Field::getDetails($fld_id);
 345          if (($fld_details['fld_type'] == 'date') && (!empty($value))) {
 346              $value= $value['Year'] . "-" . $value['Month'] . "-" . $value['Day'];
 347          }
 348          if (!is_array($value)) {
 349              $value = array($value);
 350          }
 351          foreach ($value as $item) {
 352              if ($fld_details['fld_type'] == 'integer') {
 353                  $item = Misc::escapeInteger($item);
 354              } else {
 355                  $item = "'" . Misc::escapeString($item) . "'";
 356              }
 357              $stmt = "INSERT INTO
 358                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
 359                       (
 360                          icf_iss_id,
 361                          icf_fld_id,
 362                          " . Custom_Field::getDBValueFieldNameByType($fld_details['fld_type']) . "
 363                       ) VALUES (
 364                          " . Misc::escapeInteger($iss_id) . ",
 365                          " . Misc::escapeInteger($fld_id) . ",
 366                          $item
 367                       )";
 368              $res = $GLOBALS["db_api"]->dbh->query($stmt);
 369              if (PEAR::isError($res)) {
 370                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 371                  return false;
 372              }
 373          }
 374          return true;
 375      }
 376  
 377  
 378      /**
 379       * Method used to get the list of custom fields associated with
 380       * a given project.
 381       *
 382       * @access  public
 383       * @param   integer $prj_id The project ID
 384       * @param   string $form_type The type of the form
 385       * @param   string $fld_type The type of field (optional)
 386       * @return  array The list of custom fields
 387       */
 388      function getListByProject($prj_id, $form_type, $fld_type = false)
 389      {
 390          $stmt = "SELECT
 391                      fld_id,
 392                      fld_title,
 393                      fld_description,
 394                      fld_type,
 395                      fld_report_form_required,
 396                      fld_anonymous_form_required,
 397                      fld_min_role
 398                   FROM
 399                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field,
 400                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_custom_field
 401                   WHERE
 402                      pcf_fld_id=fld_id AND
 403                      pcf_prj_id=" . Misc::escapeInteger($prj_id);
 404          if ($form_type != 'anonymous_form') {
 405              $stmt .= " AND
 406                      fld_min_role <= " . Auth::getCurrentRole();
 407          }
 408          if ($form_type != '') {
 409              $stmt .= " AND\nfld_" .  Misc::escapeString($form_type) . "=1";
 410          }
 411          if ($fld_type != '') {
 412              $stmt .= " AND\nfld_type='" .  Misc::escapeString($fld_type) . "'";
 413          }
 414          $stmt .= "
 415                   ORDER BY
 416                      fld_rank ASC";
 417          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
 418          if (PEAR::isError($res)) {
 419              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 420              return array();
 421          } else {
 422              if (count($res) == 0) {
 423                  return array();
 424              } else {
 425                  for ($i = 0; $i < count($res); $i++) {
 426                      // check if this has a dynamic field custom backend
 427                      $backend = Custom_Field::getBackend($res[$i]['fld_id']);
 428                      if ((is_object($backend)) && (is_subclass_of($backend, "Dynamic_Custom_Field_Backend"))) {
 429                          $res[$i]['dynamic_options'] = $backend->getStructuredData();
 430                          $res[$i]['controlling_field_id'] = $backend->getControllingCustomFieldID();
 431                          $res[$i]['controlling_field_name'] = $backend->getControllingCustomFieldName();
 432                          $res[$i]['hide_when_no_options'] = $backend->hideWhenNoOptions();
 433                      }
 434                      // check if the backend implements "isRequired"
 435                      if ((is_object($backend)) && (method_exists($backend, 'isRequired'))) {
 436                          $res[$i]['fld_report_form_required'] = $backend->isRequired($res[$i]['fld_id'], 'report');
 437                          $res[$i]['fld_anonymous_form_required'] = $backend->isRequired($res[$i]['fld_id'], 'anonymous');
 438                          $res[$i]['fld_close_form_required'] = $backend->isRequired($res[$i]['fld_id'], 'close');
 439                      }
 440                      if ((is_object($backend)) && (method_exists($backend, 'getValidationJS'))) {
 441                          $res[$i]['validation_js'] = $backend->getValidationJS($res[$i]['fld_id'], $form_type);
 442                      } else {
 443                          $res[$i]['validation_js'] = '';
 444                      }
 445  
 446  
 447                      $res[$i]["field_options"] = Custom_Field::getOptions($res[$i]["fld_id"]);
 448                  }
 449                  return $res;
 450              }
 451          }
 452      }
 453  
 454  
 455      /**
 456       * Method used to get the custom field option value.
 457       *
 458       * @access  public
 459       * @param   integer $fld_id The custom field ID
 460       * @param   integer $value The custom field option ID
 461       * @return  string The custom field option value
 462       */
 463      function getOptionValue($fld_id, $value)
 464      {
 465          static $returns;
 466  
 467          if (empty($value)) {
 468              return "";
 469          }
 470  
 471          if (isset($returns[$fld_id . $value])) {
 472              return $returns[$fld_id . $value];
 473          }
 474  
 475          $backend = Custom_Field::getBackend($fld_id);
 476          if ((is_object($backend)) && (method_exists($backend, 'getList'))) {
 477              $values = $backend->getList($fld_id, false);
 478              $returns[$fld_id . $value] = @$values[$value];
 479              return @$values[$value];
 480          } else {
 481              $stmt = "SELECT
 482                          cfo_value
 483                       FROM
 484                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option
 485                       WHERE
 486                          cfo_fld_id=" .  Misc::escapeInteger($fld_id) . " AND
 487                          cfo_id=" .  Misc::escapeInteger($value);
 488              $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
 489              if (PEAR::isError($res)) {
 490                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 491                  return "";
 492              } else {
 493                  if ($res == NULL) {
 494                      $returns[$fld_id . $value] = '';
 495                      return "";
 496                  } else {
 497                      $returns[$fld_id . $value] = $res;
 498                      return $res;
 499                  }
 500              }
 501          }
 502      }
 503  
 504  
 505      /**
 506       * Method used to get the custom field key based on the value.
 507       *
 508       * @access  public
 509       * @param   integer $fld_id The custom field ID
 510       * @param   integer $value The custom field option ID
 511       * @return  string The custom field option value
 512       */
 513      function getOptionKey($fld_id, $value)
 514      {
 515          static $returns;
 516  
 517          if (empty($value)) {
 518              return "";
 519          }
 520  
 521          if (isset($returns[$fld_id . $value])) {
 522              return $returns[$fld_id . $value];
 523          }
 524  
 525          $backend = Custom_Field::getBackend($fld_id);
 526          if ((is_object($backend)) && (method_exists($backend, 'getList'))) {
 527              $values = $backend->getList($fld_id, false);
 528              $key = array_search($value, $values);
 529              $returns[$fld_id . $value] = $key;
 530              return $key;
 531          } else {
 532              $stmt = "SELECT
 533                          cfo_id
 534                       FROM
 535                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option
 536                       WHERE
 537                          cfo_fld_id=" .  Misc::escapeInteger($fld_id) . " AND
 538                          cfo_value='" .  Misc::escapeString($value) . "'";
 539              $res = $GLOBALS["db_api"]->dbh->getOne($stmt);
 540              if (PEAR::isError($res)) {
 541                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 542                  return "";
 543              } else {
 544                  if ($res == NULL) {
 545                      $returns[$fld_id . $value] = '';
 546                      return "";
 547                  } else {
 548                      $returns[$fld_id . $value] = $res;
 549                      return $res;
 550                  }
 551              }
 552          }
 553      }
 554  
 555  
 556      /**
 557       * Method used to get the list of custom fields and custom field
 558       * values associated with a given issue ID. If usr_id is false method
 559       * defaults to current user.
 560       *
 561       * @access  public
 562       * @param   integer $prj_id The project ID
 563       * @param   integer $iss_id The issue ID
 564       * @param   integer $usr_id The ID of the user who is going to be viewing this list.
 565       * @return  array The list of custom fields
 566       */
 567      function getListByIssue($prj_id, $iss_id, $usr_id = false, $form_type = false)
 568      {
 569          if ($usr_id == false) {
 570              $usr_id = Auth::getUserID();
 571          }
 572  
 573          $usr_role = User::getRoleByUser($usr_id, $prj_id);
 574          if (empty($usr_role)) {
 575              $usr_role = 0;
 576          }
 577  
 578          $stmt = "SELECT
 579                      fld_id,
 580                      fld_title,
 581                      fld_type,
 582                      fld_report_form_required,
 583                      fld_anonymous_form_required,
 584                      fld_close_form_required,
 585                      " . Custom_Field::getDBValueFieldSQL() . " as value,
 586                      fld_min_role
 587                   FROM
 588                      (
 589                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field,
 590                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_custom_field
 591                      )
 592                   LEFT JOIN
 593                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
 594                   ON
 595                      pcf_fld_id=icf_fld_id AND
 596                      icf_iss_id=" .  Misc::escapeInteger($iss_id) . "
 597                   WHERE
 598                      pcf_fld_id=fld_id AND
 599                      pcf_prj_id=" .  Misc::escapeInteger($prj_id) . " AND
 600                      fld_min_role <= " . $usr_role;
 601          if ($form_type != '') {
 602              $stmt .= " AND\nfld_" .  Misc::escapeString($form_type) . "=1";
 603          }
 604          $stmt .= "
 605                   ORDER BY
 606                      fld_rank ASC";
 607          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
 608          if (PEAR::isError($res)) {
 609              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 610              return array();
 611          } else {
 612              if (count($res) == 0) {
 613                  return array();
 614              } else {
 615                  $fields = array();
 616                  for ($i = 0; $i < count($res); $i++) {
 617                      if ($res[$i]["fld_type"] == "combo") {
 618                          $res[$i]["selected_cfo_id"] = $res[$i]["value"];
 619                          $res[$i]["original_value"] = $res[$i]["value"];
 620                          $res[$i]["value"] = Custom_Field::getOptionValue($res[$i]["fld_id"], $res[$i]["value"]);
 621                          $res[$i]["field_options"] = Custom_Field::getOptions($res[$i]["fld_id"]);
 622  
 623                          // add the select option to the list of values if it isn't on the list (useful for fields with active and non-active items)
 624                          if (!in_array($res[$i]['value'], $res[$i]['field_options'])) {
 625                              $res[$i]['field_options'][$res[$i]['original_value']] = Custom_Field::getOptionValue($res[$i]['fld_id'], $res[$i]['original_value']);
 626                          }
 627  
 628                          $fields[] = $res[$i];
 629  
 630                      } elseif ($res[$i]['fld_type'] == 'multiple') {
 631                          // check whether this field is already in the array
 632                          $found = 0;
 633                          for ($y = 0; $y < count($fields); $y++) {
 634                              if ($fields[$y]['fld_id'] == $res[$i]['fld_id']) {
 635                                  $found = 1;
 636                                  $found_index = $y;
 637                              }
 638                          }
 639                          $original_value = $res[$i]['value'];
 640                          if (!$found) {
 641                              $res[$i]["selected_cfo_id"] = array($res[$i]["value"]);
 642                              $res[$i]["value"] = Custom_Field::getOptionValue($res[$i]["fld_id"], $res[$i]["value"]);
 643                              $res[$i]["field_options"] = Custom_Field::getOptions($res[$i]["fld_id"]);
 644                              $fields[] = $res[$i];
 645                              $found_index = count($fields) - 1;
 646                          } else {
 647                              $fields[$found_index]['value'] .= ', ' . Custom_Field::getOptionValue($res[$i]["fld_id"], $res[$i]["value"]);
 648                              $fields[$found_index]['selected_cfo_id'][] = $res[$i]["value"];
 649                          }
 650  
 651                          // add the select option to the list of values if it isn't on the list (useful for fields with active and non-active items)
 652                          if (!in_array($original_value, $fields[$found_index]['field_options'])) {
 653                              $fields[$found_index]['field_options'][$original_value] = Custom_Field::getOptionValue($res[$i]['fld_id'], $original_value);
 654                          }
 655                      } else {
 656                          $fields[] = $res[$i];
 657                      }
 658                  }
 659                  foreach ($fields as $key => $field) {
 660                      $backend = Custom_Field::getBackend($field['fld_id']);
 661                      if ((is_object($backend)) && (is_subclass_of($backend, "Dynamic_Custom_Field_Backend"))) {
 662                          $fields[$key]['dynamic_options'] = $backend->getStructuredData();
 663                          $fields[$key]['controlling_field_id'] = $backend->getControllingCustomFieldID();
 664                          $fields[$key]['controlling_field_name'] = $backend->getControllingCustomFieldName();
 665                          $fields[$key]['hide_when_no_options'] = $backend->hideWhenNoOptions();
 666                      }
 667  
 668                      // check if the backend implements "isRequired"
 669                      if ((is_object($backend)) && (method_exists($backend, 'isRequired'))) {
 670                          $fields[$key]['fld_report_form_required'] = $backend->isRequired($fields[$key]['fld_id'], 'report', $iss_id);
 671                          $fields[$key]['fld_anonymous_form_required'] = $backend->isRequired($fields[$key]['fld_id'], 'anonymous', $iss_id);
 672                          $fields[$key]['fld_close_form_required'] = $backend->isRequired($fields[$key]['fld_id'], 'close', $iss_id);
 673                      }
 674                      if ((is_object($backend)) && (method_exists($backend, 'getValidationJS'))) {
 675                          $fields[$key]['validation_js'] = $backend->getValidationJS($fields[$key]['fld_id'], $form_type, $iss_id);
 676                      } else {
 677                          $fields[$key]['validation_js'] = '';
 678                      }
 679                  }
 680                  return $fields;
 681              }
 682          }
 683      }
 684  
 685  
 686      /**
 687       * Returns an array of fields and values for a specific issue
 688       *
 689       * @access  public
 690       * @param   integer $prj_id The ID of the project
 691       * @param   integer $iss_id The ID of the issue to return values for
 692       * @return  array An array containging fld_id => value
 693       */
 694      function getValuesByIssue($prj_id, $iss_id)
 695      {
 696          $values = array();
 697          $list = Custom_Field::getListByIssue($prj_id, $iss_id);
 698          foreach ($list as $field) {
 699              if ($field['fld_type'] == 'combo') {
 700                  $values[$field['fld_id']] = array(
 701                      $field['selected_cfo_id'] => $field['value']
 702                  );
 703              } elseif ($field['fld_type'] == 'multiple') {
 704                  $selected = $field['selected_cfo_id'];
 705                  foreach ($selected as $cfo_id) {
 706                      $values[$field['fld_id']][$cfo_id] = @$field['field_options'][$cfo_id];
 707                  }
 708              } else {
 709                  $values[$field['fld_id']] = $field['value'];
 710              }
 711          }
 712          return $values;
 713      }
 714  
 715  
 716      /**
 717       * Method used to remove a given list of custom fields.
 718       *
 719       * @access  public
 720       * @return  boolean
 721       */
 722      function remove()
 723      {
 724          $items = @implode(", ", Misc::escapeInteger($_POST["items"]));
 725          $stmt = "DELETE FROM
 726                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
 727                   WHERE
 728                      fld_id IN ($items)";
 729          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 730          if (PEAR::isError($res)) {
 731              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 732              return false;
 733          } else {
 734              $stmt = "DELETE FROM
 735                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_custom_field
 736                       WHERE
 737                          pcf_fld_id IN ($items)";
 738              $res = $GLOBALS["db_api"]->dbh->query($stmt);
 739              if (PEAR::isError($res)) {
 740                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 741                  return false;
 742              } else {
 743                  $stmt = "DELETE FROM
 744                              " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
 745                           WHERE
 746                              icf_fld_id IN ($items)";
 747                  $res = $GLOBALS["db_api"]->dbh->query($stmt);
 748                  if (PEAR::isError($res)) {
 749                      Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 750                      return false;
 751                  } else {
 752                      $stmt = "DELETE FROM
 753                                  " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option
 754                               WHERE
 755                                  cfo_fld_id IN ($items)";
 756                      $res = $GLOBALS["db_api"]->dbh->query($stmt);
 757                      if (PEAR::isError($res)) {
 758                          Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 759                          return false;
 760                      } else {
 761                          return true;
 762                      }
 763                  }
 764              }
 765          }
 766      }
 767  
 768  
 769      /**
 770       * Method used to add a new custom field to the system.
 771       *
 772       * @access  public
 773       * @return  integer 1 if the insert worked, -1 otherwise
 774       */
 775      function insert()
 776      {
 777          if (empty($_POST["report_form"])) {
 778              $_POST["report_form"] = 0;
 779          }
 780          if (empty($_POST["report_form_required"])) {
 781              $_POST["report_form_required"] = 0;
 782          }
 783          if (empty($_POST["anon_form"])) {
 784              $_POST["anon_form"] = 0;
 785          }
 786          if (empty($_POST["anon_form_required"])) {
 787              $_POST["anon_form_required"] = 0;
 788          }
 789          if (empty($_POST["close_form"])) {
 790              $_POST["close_form"] = 0;
 791          }
 792          if (empty($_POST["close_form_required"])) {
 793              $_POST["close_form_required"] = 0;
 794          }
 795          if (empty($_POST["list_display"])) {
 796              $_POST["list_display"] = 0;
 797          }
 798          if (empty($_POST["min_role"])) {
 799              $_POST["min_role"] = 1;
 800          }
 801          if (!isset($_POST["rank"])) {
 802              $_POST["rank"] = (Custom_Field::getMaxRank() + 1);
 803          }
 804          $stmt = "INSERT INTO
 805                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
 806                   (
 807                      fld_title,
 808                      fld_description,
 809                      fld_type,
 810                      fld_report_form,
 811                      fld_report_form_required,
 812                      fld_anonymous_form,
 813                      fld_anonymous_form_required,
 814                      fld_close_form,
 815                      fld_close_form_required,
 816                      fld_list_display,
 817                      fld_min_role,
 818                      fld_rank,
 819                      fld_backend
 820                   ) VALUES (
 821                      '" . Misc::escapeString($_POST["title"]) . "',
 822                      '" . Misc::escapeString($_POST["description"]) . "',
 823                      '" . Misc::escapeString($_POST["field_type"]) . "',
 824                      " . Misc::escapeInteger($_POST["report_form"]) . ",
 825                      " . Misc::escapeInteger($_POST["report_form_required"]) . ",
 826                      " . Misc::escapeInteger($_POST["anon_form"]) . ",
 827                      " . Misc::escapeInteger($_POST["anon_form_required"]) . ",
 828                      " . Misc::escapeInteger($_POST["close_form"]) . ",
 829                      " . Misc::escapeInteger($_POST["close_form_required"]) . ",
 830                      " . Misc::escapeInteger($_POST["list_display"]) . ",
 831                      " . Misc::escapeInteger($_POST["min_role"]) . ",
 832                      " . Misc::escapeInteger($_POST['rank']) . ",
 833                      '" . Misc::escapeString(@$_POST['custom_field_backend']) . "'
 834                   )";
 835          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 836          if (PEAR::isError($res)) {
 837              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 838              return -1;
 839          } else {
 840              $new_id = $GLOBALS["db_api"]->get_last_insert_id();
 841              if (($_POST["field_type"] == 'combo') || ($_POST["field_type"] == 'multiple')) {
 842                  foreach ($_POST["field_options"] as $option_value) {
 843                      $params = Custom_Field::parseParameters($option_value);
 844                      Custom_Field::addOptions($new_id, $params["value"]);
 845                  }
 846              }
 847              // add the project associations!
 848              for ($i = 0; $i < count($_POST["projects"]); $i++) {
 849                  Custom_Field::associateProject($_POST["projects"][$i], $new_id);
 850              }
 851              return 1;
 852          }
 853      }
 854  
 855  
 856      /**
 857       * Method used to associate a custom field to a project.
 858       *
 859       * @access  public
 860       * @param   integer $prj_id The project ID
 861       * @param   integer $fld_id The custom field ID
 862       * @return  boolean
 863       */
 864      function associateProject($prj_id, $fld_id)
 865      {
 866          $stmt = "INSERT INTO
 867                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_custom_field
 868                   (
 869                      pcf_prj_id,
 870                      pcf_fld_id
 871                   ) VALUES (
 872                      " . Misc::escapeInteger($prj_id) . ",
 873                      " . Misc::escapeInteger($fld_id) . "
 874                   )";
 875          $res = $GLOBALS["db_api"]->dbh->query($stmt);
 876          if (PEAR::isError($res)) {
 877              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 878              return false;
 879          } else {
 880              return true;
 881          }
 882      }
 883  
 884  
 885      /**
 886       * Method used to get the list of custom fields available in the
 887       * system.
 888       *
 889       * @access  public
 890       * @return  array The list of custom fields
 891       */
 892      function getList()
 893      {
 894          $stmt = "SELECT
 895                      *
 896                   FROM
 897                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
 898                   ORDER BY
 899                      fld_rank ASC";
 900          $res = $GLOBALS["db_api"]->dbh->getAll($stmt, DB_FETCHMODE_ASSOC);
 901          if (PEAR::isError($res)) {
 902              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 903              return "";
 904          } else {
 905              for ($i = 0; $i < count($res); $i++) {
 906                  $res[$i]["projects"] = @implode(", ", array_values(Custom_Field::getAssociatedProjects($res[$i]["fld_id"])));
 907                  if (($res[$i]["fld_type"] == "combo") || ($res[$i]["fld_type"] == "multiple")) {
 908                      if (!empty($res[$i]['fld_backend'])) {
 909                          $res[$i]["field_options"] = @implode(", ", array_values(Custom_Field::getOptions($res[$i]["fld_id"])));
 910                      }
 911                  }
 912                  if (!empty($res[$i]['fld_backend'])) {
 913                      $res[$i]['field_options'] = 'Backend: ' . Custom_Field::getBackendName($res[$i]['fld_backend']);
 914                  }
 915                  $res[$i]['min_role_name'] = @User::getRole($res[$i]['fld_min_role']);
 916              }
 917              return $res;
 918          }
 919      }
 920  
 921  
 922      /**
 923       * Method used to get the list of associated projects with a given
 924       * custom field ID.
 925       *
 926       * @access  public
 927       * @param   integer $fld_id The project ID
 928       * @return  array The list of associated projects
 929       */
 930      function getAssociatedProjects($fld_id)
 931      {
 932          $stmt = "SELECT
 933                      prj_id,
 934                      prj_title
 935                   FROM
 936                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project,
 937                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_custom_field
 938                   WHERE
 939                      pcf_prj_id=prj_id AND
 940                      pcf_fld_id=" . Misc::escapeInteger($fld_id) . "
 941                   ORDER BY
 942                      prj_title ASC";
 943          $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt);
 944          if (PEAR::isError($res)) {
 945              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 946              return "";
 947          } else {
 948              return $res;
 949          }
 950      }
 951  
 952  
 953      /**
 954       * Method used to get the details of a specific custom field.
 955       *
 956       * @access  public
 957       * @param   integer $fld_id The custom field ID
 958       * @param   boolean $force_refresh If the details must be loaded again from the database
 959       * @return  array The custom field details
 960       */
 961      function getDetails($fld_id, $force_refresh = false)
 962      {
 963          static $returns;
 964  
 965          if ((isset($returns[$fld_id])) && ($force_refresh == false)) {
 966              return $returns[$fld_id];
 967          }
 968  
 969          $stmt = "SELECT
 970                      *
 971                   FROM
 972                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
 973                   WHERE
 974                      fld_id=" . Misc::escapeInteger($fld_id);
 975          $res = $GLOBALS["db_api"]->dbh->getRow($stmt, DB_FETCHMODE_ASSOC);
 976          if (PEAR::isError($res)) {
 977              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
 978              return "";
 979          } else {
 980              $res["projects"] = @array_keys(Custom_Field::getAssociatedProjects($fld_id));
 981              $t = array();
 982              $options = Custom_Field::getOptions($fld_id);
 983              foreach ($options as $cfo_id => $cfo_value) {
 984                  $res["field_options"]["existing:" . $cfo_id . ":" . $cfo_value] = $cfo_value;
 985              }
 986              $returns[$fld_id] = $res;
 987              return $res;
 988          }
 989      }
 990  
 991  
 992      /**
 993       * Method used to get the list of custom field options associated
 994       * with a given custom field ID.
 995       *
 996       * @access  public
 997       * @param   integer $fld_id The custom field ID
 998       * @param   array $ids An array of ids to return values for.
 999       * @return  array The list of custom field options
1000       */
1001      function getOptions($fld_id, $ids = false)
1002      {
1003          static $returns;
1004  
1005          $return_key = $fld_id . serialize($ids);
1006  
1007          if (isset($returns[$return_key])) {
1008              return $returns[$return_key];
1009          }
1010          $backend = Custom_Field::getBackend($fld_id);
1011          if ((is_object($backend)) && (method_exists($backend, 'getList'))) {
1012              $list = $backend->getList($fld_id);
1013              if ($ids != false) {
1014                  foreach ($list as $id => $value) {
1015                      if (!in_array($id, $ids)) {
1016                          unset($list[$id]);
1017                      }
1018                  }
1019              }
1020              $returns[$return_key] = $list;
1021              return $list;
1022          } else {
1023              $stmt = "SELECT
1024                          cfo_id,
1025                          cfo_value
1026                       FROM
1027                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option
1028                       WHERE
1029                          cfo_fld_id=" . Misc::escapeInteger($fld_id);
1030              if ($ids != false) {
1031                  $stmt .= " AND
1032                          cfo_id IN(" . join(', ', Misc::escapeInteger($ids)) . ")";
1033              }
1034              $stmt .= "
1035                       ORDER BY
1036                          cfo_id ASC";
1037              $res = $GLOBALS["db_api"]->dbh->getAssoc($stmt);
1038              if (PEAR::isError($res)) {
1039                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1040                  return "";
1041              } else {
1042                  asort($res);
1043                  $returns[$return_key] = $res;
1044                  return $res;
1045              }
1046          }
1047      }
1048  
1049  
1050      /**
1051       * Method used to parse the special format used in the combo boxes
1052       * in the administration section of the system, in order to be
1053       * used as a way to flag the system for whether the custom field
1054       * option is a new one or one that should be updated.
1055       *
1056       * @access  private
1057       * @param   string $value The custom field option format string
1058       * @return  array Parameters used by the update/insert methods
1059       */
1060      function parseParameters($value)
1061      {
1062          if (substr($value, 0, 4) == 'new:') {
1063              return array(
1064                  "type"  => "new",
1065                  "value" => substr($value, 4)
1066              );
1067          } else {
1068              $value = substr($value, strlen("existing:"));
1069              return array(
1070                  "type"  => "existing",
1071                  "id"    => substr($value, 0, strpos($value, ":")),
1072                  "value" => substr($value, strpos($value, ":")+1)
1073              );
1074          }
1075      }
1076  
1077  
1078      /**
1079       * Method used to update the details for a specific custom field.
1080       *
1081       * @access  public
1082       * @return  integer 1 if the update worked, -1 otherwise
1083       */
1084      function update()
1085      {
1086          if (empty($_POST["report_form"])) {
1087              $_POST["report_form"] = 0;
1088          }
1089          if (empty($_POST["report_form_required"])) {
1090              $_POST["report_form_required"] = 0;
1091          }
1092          if (empty($_POST["anon_form"])) {
1093              $_POST["anon_form"] = 0;
1094          }
1095          if (empty($_POST["anon_form_required"])) {
1096              $_POST["anon_form_required"] = 0;
1097          }
1098          if (empty($_POST["list_display"])) {
1099              $_POST["list_display"] = 0;
1100          }
1101          if (empty($_POST["close_form"])) {
1102              $_POST["close_form"] = 0;
1103          }
1104          if (empty($_POST["close_form_required"])) {
1105              $_POST["close_form_required"] = 0;
1106          }
1107          if (empty($_POST["min_role"])) {
1108              $_POST["min_role"] = 1;
1109          }
1110          if (!isset($_POST["rank"])) {
1111              $_POST["rank"] = (Custom_Field::getMaxRank() + 1);
1112          }
1113          $old_details = Custom_Field::getDetails($_POST["id"]);
1114          $stmt = "UPDATE
1115                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
1116                   SET
1117                      fld_title='" . Misc::escapeString($_POST["title"]) . "',
1118                      fld_description='" . Misc::escapeString($_POST["description"]) . "',
1119                      fld_type='" . Misc::escapeString($_POST["field_type"]) . "',
1120                      fld_report_form=" . Misc::escapeInteger($_POST["report_form"]) . ",
1121                      fld_report_form_required=" . Misc::escapeInteger($_POST["report_form_required"]) . ",
1122                      fld_anonymous_form=" . Misc::escapeInteger($_POST["anon_form"]) . ",
1123                      fld_anonymous_form_required=" . Misc::escapeInteger($_POST["anon_form_required"]) . ",
1124                      fld_close_form=" . Misc::escapeInteger($_POST["close_form"]) . ",
1125                      fld_close_form_required=" . Misc::escapeInteger($_POST["close_form_required"]) . ",
1126                      fld_list_display=" . Misc::escapeInteger($_POST["list_display"]) . ",
1127                      fld_min_role=" . Misc::escapeInteger($_POST['min_role']) . ",
1128                      fld_rank = " . Misc::escapeInteger($_POST['rank']) . ",
1129                      fld_backend = '" . Misc::escapeString(@$_POST['custom_field_backend']) . "'
1130                   WHERE
1131                      fld_id=" . Misc::escapeInteger($_POST["id"]);
1132          $res = $GLOBALS["db_api"]->dbh->query($stmt);
1133          if (PEAR::isError($res)) {
1134              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1135              return -1;
1136          } else {
1137              // if the current custom field is a combo box, get all of the current options
1138              if (in_array($_POST["field_type"], array('combo', 'multiple'))) {
1139                  $stmt = "SELECT
1140                              cfo_id
1141                           FROM
1142                              " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option
1143                           WHERE
1144                              cfo_fld_id=" . Misc::escapeInteger($_POST["id"]);
1145                  $current_options = $GLOBALS["db_api"]->dbh->getCol($stmt);
1146              }
1147              if ($old_details["fld_type"] != $_POST["field_type"]) {
1148                  // gotta remove all custom field options if the field is being changed from a combo box to a text field
1149                  if ((!in_array($old_details['fld_type'], array('text', 'textarea'))) &&
1150                        (!in_array($_POST["field_type"], array('combo', 'multiple')))) {
1151                     Custom_Field::removeOptionsByFields($_POST["id"]);
1152                  }
1153                  if (in_array($_POST['field_type'], array('text', 'textarea', 'date', 'integer'))) {
1154                      // update values for all other option types
1155                      Custom_Field::updateValuesForNewType($_POST['id']);
1156                  }
1157              }
1158              // update the custom field options, if any
1159              if (($_POST["field_type"] == "combo") || ($_POST["field_type"] == "multiple")) {
1160                  $updated_options = array();
1161                  if (empty($_POST['custom_field_backend'])) {
1162                      foreach ($_POST["field_options"] as $option_value) {
1163                          $params = Custom_Field::parseParameters($option_value);
1164                          if ($params["type"] == 'new') {
1165                              Custom_Field::addOptions($_POST["id"], $params["value"]);
1166                          } else {
1167                              $updated_options[] = $params["id"];
1168                              // check if the user is trying to update the value of this option
1169                              if ($params["value"] != Custom_Field::getOptionValue($_POST["id"], $params["id"])) {
1170                                  Custom_Field::updateOption($params["id"], $params["value"]);
1171                              }
1172                          }
1173                      }
1174                  }
1175              }
1176              // get the diff between the current options and the ones posted by the form
1177              // and then remove the options not found in the form submissions
1178              if (in_array($_POST["field_type"], array('combo', 'multiple'))) {
1179                  $diff_ids = @array_diff($current_options, $updated_options);
1180                  if (@count($diff_ids) > 0) {
1181                      Custom_Field::removeOptions($_POST['id'], array_values($diff_ids));
1182                  }
1183              }
1184              // now we need to check for any changes in the project association of this custom field
1185              // and update the mapping table accordingly
1186              $old_proj_ids = @array_keys(Custom_Field::getAssociatedProjects($_POST["id"]));
1187              // COMPAT: this next line requires PHP > 4.0.4
1188              $diff_ids = array_diff($old_proj_ids, $_POST["projects"]);
1189              if (count($diff_ids) > 0) {
1190                  foreach ($diff_ids as $removed_prj_id) {
1191                      Custom_Field::removeIssueAssociation($_POST["id"], false, $removed_prj_id );
1192                  }
1193              }
1194              // update the project associations now
1195              $stmt = "DELETE FROM
1196                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_custom_field
1197                       WHERE
1198                          pcf_fld_id=" . Misc::escapeInteger($_POST["id"]);
1199              $res = $GLOBALS["db_api"]->dbh->query($stmt);
1200              if (PEAR::isError($res)) {
1201                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1202                  return -1;
1203              } else {
1204                  for ($i = 0; $i < count($_POST["projects"]); $i++) {
1205                      Custom_Field::associateProject($_POST["projects"][$i], $_POST["id"]);
1206                  }
1207              }
1208              return 1;
1209          }
1210      }
1211  
1212  
1213      /**
1214       * Method used to get the list of custom fields associated with a
1215       * given project.
1216       *
1217       * @access  public
1218       * @param   integer $prj_id The project ID
1219       * @return  array The list of custom fields
1220       */
1221      function getFieldsByProject($prj_id)
1222      {
1223          $stmt = "SELECT
1224                      pcf_fld_id
1225                   FROM
1226                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_custom_field
1227                   WHERE
1228                      pcf_prj_id=" . Misc::escapeInteger($prj_id);
1229          $res = $GLOBALS["db_api"]->dbh->getCol($stmt);
1230          if (PEAR::isError($res)) {
1231              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1232              return array();
1233          } else {
1234              return $res;
1235          }
1236      }
1237  
1238  
1239      /**
1240       * Method used to remove the issue associations related to a given
1241       * custom field ID.
1242       *
1243       * @access  public
1244       * @param   integer $fld_id The custom field ID
1245       * @param   integer $issue_id The issue ID (not required)
1246       * @param   integer $prj_id The project ID (not required)
1247       * @return  boolean
1248       */
1249      function removeIssueAssociation($fld_id, $issue_id = FALSE, $prj_id = false)
1250      {
1251          if (is_array($fld_id)) {
1252              $fld_id = implode(", ",  Misc::escapeInteger($fld_id));
1253          }
1254          $issues = array();
1255          if ($issue_id != false) {
1256              $issues = array($issue_id);
1257          } elseif ($prj_id != false) {
1258              $sql = "SELECT
1259                          iss_id
1260                      FROM
1261                          " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue
1262                      WHERE
1263                          iss_prj_id = " . Misc::escapeInteger($prj_id);
1264              $res = $GLOBALS['db_api']->dbh->getCol($sql);
1265              if (PEAR::isError($res)) {
1266                  Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1267                  return false;
1268              } else {
1269                  $issues = $res;
1270              }
1271          }
1272          $stmt = "DELETE FROM
1273                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
1274                   WHERE
1275                      icf_fld_id IN (" . $fld_id . ")";
1276          if (count($issues) > 0) {
1277              $stmt .= " AND icf_iss_id IN(" . join(', ', Misc::escapeInteger($issues)) . ")";
1278          }
1279          $res = $GLOBALS["db_api"]->dbh->query($stmt);
1280          if (PEAR::isError($res)) {
1281              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1282              return false;
1283          } else {
1284              return true;
1285          }
1286      }
1287  
1288  
1289      /**
1290       * Method used to remove the custom field options associated with
1291       * a given list of custom field IDs.
1292       *
1293       * @access  public
1294       * @param   array $ids The list of custom field IDs
1295       * @return  boolean
1296       */
1297      function removeOptionsByFields($ids)
1298      {
1299          if (!is_array($ids)) {
1300              $ids = array($ids);
1301          }
1302          $items = implode(", ", Misc::escapeInteger($ids));
1303          $stmt = "SELECT
1304                      cfo_id
1305                   FROM
1306                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field_option
1307                   WHERE
1308                      cfo_fld_id IN ($items)";
1309          $res = $GLOBALS["db_api"]->dbh->getCol($stmt);
1310          if (PEAR::isError($res)) {
1311              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1312              return false;
1313          } else {
1314              Custom_Field::removeOptions($ids, $res);
1315              return true;
1316          }
1317      }
1318  
1319  
1320      /**
1321       * Method used to remove all custom field entries associated with
1322       * a given set of issues.
1323       *
1324       * @access  public
1325       * @param   array $ids The array of issue IDs
1326       * @return  boolean
1327       */
1328      function removeByIssues($ids)
1329      {
1330          $items = implode(", ", Misc::escapeInteger($ids));
1331          $stmt = "DELETE FROM
1332                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
1333                   WHERE
1334                      icf_iss_id IN ($items)";
1335          $res = $GLOBALS["db_api"]->dbh->query($stmt);
1336          if (PEAR::isError($res)) {
1337              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1338              return false;
1339          } else {
1340              return true;
1341          }
1342      }
1343  
1344  
1345      /**
1346       * Method used to remove all custom fields associated with
1347       * a given set of projects.
1348       *
1349       * @access  public
1350       * @param   array $ids The array of project IDs
1351       * @return  boolean
1352       */
1353      function removeByProjects($ids)
1354      {
1355          $items = implode(", ", Misc::escapeInteger($ids));
1356          $stmt = "DELETE FROM
1357                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_custom_field
1358                   WHERE
1359                      pcf_prj_id IN ($items)";
1360          $res = $GLOBALS["db_api"]->dbh->query($stmt);
1361          if (PEAR::isError($res)) {
1362              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1363              return false;
1364          } else {
1365              return true;
1366          }
1367      }
1368  
1369  
1370      /**
1371       * Method to return the names of the fields which should be displayed on the list issues page.
1372       *
1373       * @access  public
1374       * @param   integer $prj_id The ID of the project.
1375       * @return  array An array of custom field names.
1376       */
1377      function getFieldsToBeListed($prj_id)
1378      {
1379          $sql = "SELECT
1380                      fld_id,
1381                      fld_title
1382                  FROM
1383                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field,
1384                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "project_custom_field
1385                  WHERE
1386                      fld_id = pcf_fld_id AND
1387                      pcf_prj_id = " . Misc::escapeInteger($prj_id) . " AND
1388                      fld_list_display = 1  AND
1389                      fld_min_role <= " . Auth::getCurrentRole() . "
1390                  ORDER BY
1391                      fld_rank ASC";
1392          $res = $GLOBALS["db_api"]->dbh->getAssoc($sql);
1393          if (PEAR::isError($res)) {
1394              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1395              return array();
1396          } else {
1397              return $res;
1398          }
1399      }
1400  
1401  
1402      /**
1403       * Returns the fld_id of the field with the specified title
1404       *
1405       * @access  public
1406       * @param   string $title The title of the field
1407       * @return  integer The fld_id
1408       */
1409      function getIDByTitle($title)
1410      {
1411          $sql = "SELECT
1412                      fld_id
1413                  FROM
1414                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
1415                  WHERE
1416                      fld_title = '" . Misc::escapeString($title) . "'";
1417          $res = $GLOBALS["db_api"]->dbh->getOne($sql);
1418          if (PEAR::isError($res)) {
1419              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1420              return 0;
1421          } else {
1422              if (empty($res)) {
1423                  return 0;
1424              } else {
1425                  return $res;
1426              }
1427          }
1428      }
1429  
1430  
1431      /**
1432       * Returns the value for the specified field
1433       *
1434       * @access  public
1435       * @param   integer $iss_id The ID of the issue
1436       * @param   integer $fld_id The ID of the field
1437       * @param   boolean $raw If the raw value should be displayed
1438       * @param   mixed an array or string containing the value
1439       */
1440      function getDisplayValue($iss_id, $fld_id, $raw = false)
1441      {
1442          $sql = "SELECT
1443                      fld_id,
1444                      fld_type,
1445                      " . Custom_Field::getDBValueFieldSQL() . " as value
1446                  FROM
1447                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field,
1448                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
1449                  WHERE
1450                      fld_id=icf_fld_id AND
1451                      icf_iss_id=" .  Misc::escapeInteger($iss_id) . " AND
1452                      fld_id = " . Misc::escapeInteger($fld_id);
1453          $res = $GLOBALS["db_api"]->dbh->getAll($sql, DB_FETCHMODE_ASSOC);
1454          if (PEAR::isError($res)) {
1455              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1456              return '';
1457          } else {
1458              $values = array();
1459              for ($i = 0; $i < count($res); $i++) {
1460                  if (($res[$i]["fld_type"] == "combo") || ($res[$i]['fld_type'] == 'multiple')) {
1461                      if ($raw) {
1462                          $values[] = $res[$i]['value'];
1463                      } else {
1464                          $values[] = Custom_Field::getOptionValue($res[$i]["fld_id"], $res[$i]["value"]);
1465                      }
1466                  } else {
1467                      $values[] = $res[$i]['value'];
1468                  }
1469              }
1470              if ($raw) {
1471                  return $values;
1472              } else {
1473                  return join(', ', $values);
1474              }
1475          }
1476      }
1477  
1478  
1479      /**
1480       * Returns the current maximum rank of any custom fields.
1481       *
1482       * @access  public
1483       * @return  integer The highest rank
1484       */
1485      function getMaxRank()
1486      {
1487          $sql = "SELECT
1488                      max(fld_rank)
1489                  FROM
1490                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field";
1491          return $GLOBALS["db_api"]->dbh->getOne($sql);
1492      }
1493  
1494  
1495      /**
1496       * Changes the rank of a custom field
1497       *
1498       * @access  public
1499       */
1500      function changeRank()
1501      {
1502          $fld_id = $_REQUEST['id'];
1503          $direction = $_REQUEST['direction'];
1504          // get array of all fields and current ranks
1505          $fields = Custom_Field::getList();
1506          for ($i = 0;$i < count($fields); $i++) {
1507              if ($fields[$i]['fld_id'] == $fld_id) {
1508                  // this is the field we want to mess with
1509                  if ((($i == 0) && ($direction == -1)) ||
1510                      ((($i+1) == count($fields)) && ($direction == +1))) {
1511                      // trying to move first entry lower or last entry higher will not work
1512                      break;
1513                  }
1514  
1515                  $target_index = ($i + $direction);
1516                  $target_row = $fields[$target_index];
1517                  if (empty($target_row)) {
1518                      break;
1519                  }
1520                  // update this entry
1521                  Custom_Field::setRank($fld_id, $target_row['fld_rank']);
1522  
1523                  // update field we stole this rank from
1524                  Custom_Field::setRank($target_row['fld_id'], $fields[$i]['fld_rank']);
1525              }
1526          }
1527  
1528          // re-order everything starting from 1
1529          $fields = Custom_Field::getList();
1530          $rank = 1;
1531          foreach ($fields as $field) {
1532              Custom_Field::setRank($field['fld_id'], $rank++);
1533          }
1534          return 1;
1535      }
1536  
1537  
1538      /**
1539       * Sets the rank of a custom field
1540       *
1541       * @access  public
1542       * @param   integer $fld_id The ID of the field
1543       * @param   integer $rank The new rank for this field
1544       * @return  integer 1 if successful, -1 otherwise
1545       */
1546      function setRank($fld_id, $rank)
1547      {
1548          $sql = "UPDATE
1549                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
1550                  SET
1551                      fld_rank = $rank
1552                  WHERE
1553                      fld_id = $fld_id";
1554          $res = $GLOBALS["db_api"]->dbh->query($sql);
1555          if (PEAR::isError($res)) {
1556              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1557              return -1;
1558          }
1559          return 1;
1560      }
1561  
1562  
1563      /**
1564       * Returns the list of available custom field backends by listing the class
1565       * files in the backend directory.
1566       *
1567       * @access  public
1568       * @return  array Associative array of filename => name
1569       */
1570      function getBackendList()
1571      {
1572          $files = Misc::getFileList(APP_INC_PATH . "custom_field");
1573          $list = array();
1574          for ($i = 0; $i < count($files); $i++) {
1575              // make sure we only list the backends
1576              if (preg_match('/^class\.(.*)\.php$/', $files[$i])) {
1577                  // display a prettyfied backend name in the admin section
1578                  $list[$files[$i]] = Custom_Field::getBackendName($files[$i]);
1579              }
1580          }
1581          return $list;
1582      }
1583  
1584  
1585      /**
1586       * Returns the 'pretty' name of the backend
1587       *
1588       * @access  public
1589       * @param   string $backend The full backend file name
1590       * @return  string The pretty name of the backend.
1591       */
1592      function getBackendName($backend)
1593      {
1594          preg_match('/^class\.(.*)\.php$/', $backend, $matches);
1595          return ucwords(str_replace('_', ' ', $matches[1]));
1596      }
1597  
1598  
1599      /**
1600       * Returns an instance of custom field backend class if it exists for the
1601       * specified field.
1602       *
1603       * @access  public
1604       * @param   integer $fld_id The ID of the field
1605       * @return  mixed false if there is no backend or an instance of the backend class
1606       */
1607      function &getBackend($fld_id)
1608      {
1609          static $returns;
1610  
1611          // poor mans caching
1612          if (isset($returns[$fld_id])) {
1613              return $returns[$fld_id];
1614          }
1615  
1616          $sql = "SELECT
1617                      fld_backend
1618                  FROM
1619                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "custom_field
1620                  WHERE
1621                      fld_id = " . Misc::escapeInteger($fld_id);
1622          $res = $GLOBALS["db_api"]->dbh->getOne($sql);
1623          if (PEAR::isError($res)) {
1624              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1625              return false;
1626          } elseif (!empty($res)) {
1627              require_once(APP_INC_PATH . "custom_field/$res");
1628  
1629              $file_name_chunks = explode(".", $res);
1630              $class_name = $file_name_chunks[1] . "_Custom_Field_Backend";
1631  
1632              $returns[$fld_id] = new $class_name;
1633          } else {
1634              $returns[$fld_id] = false;
1635          }
1636          return $returns[$fld_id];
1637      }
1638  
1639  
1640      /**
1641       * Searches a specified custom field for a string and returns any issues that match
1642       *
1643       * @access  public
1644       * @param   integer $fld_id The ID of the custom field
1645       * @param   string  $search The string to search for
1646       * @return  array An array of issue IDs
1647       */
1648      function getIssuesByString($fld_id, $search)
1649      {
1650          $sql = "SELECT
1651                      icf_iss_id
1652                  FROM
1653                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
1654                  WHERE
1655                      icf_fld_id = " . Misc::escapeInteger($fld_id) . " AND
1656                      (
1657                          icf_value LIKE '%" . Misc::escapeString($search) . "%' OR
1658                          icf_value_integer LIKE '%" . Misc::escapeInteger($search) . "%' OR
1659                          icf_value_date LIKE '%" . Misc::escapeString($search) . "%'
1660                      )";
1661          $res = $GLOBALS["db_api"]->dbh->getCol($sql);
1662          if (PEAR::isError($res)) {
1663              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1664              return array();
1665          }
1666          return $res;
1667      }
1668  
1669  
1670      /**
1671       * Formats the return value
1672       *
1673       * @access  public
1674       * @param   mixed   $value The value to format
1675       * @param   integer $fld_id The ID of the field
1676       * @param   integer $issue_id The ID of the issue
1677       * @return  mixed   the formatted value.
1678       */
1679      function formatValue($value, $fld_id, $issue_id)
1680      {
1681          $backend = Custom_Field::getBackend($fld_id);
1682          if ((is_object($backend)) && (method_exists($backend, 'formatValue'))) {
1683              return $backend->formatValue($value, $fld_id, $issue_id);
1684          } else {
1685              return Link_Filter::processText(Auth::getCurrentProject(), htmlspecialchars($value));
1686          }
1687      }
1688  
1689  
1690      /**
1691       * This method inserts a blank value for all custom fields that do not already have a record.
1692       * It currently is not called by the main code, but is included to be called from workflow classes.
1693       *
1694       * @access  public
1695       * @param   integer $issue_id The Issue ID
1696       */
1697      function populateAllFields($issue_id)
1698      {
1699          $prj_id = Issue::getProjectID($issue_id);
1700          $fields = Custom_Field::getListByIssue($prj_id, $issue_id, APP_SYSTEM_USER_ID);
1701          foreach ($fields as $field) {
1702              if (empty($field['value'])) {
1703                  Custom_Field::removeIssueAssociation($field['fld_id'], $issue_id);
1704                  Custom_Field::associateIssue($issue_id, $field['fld_id'], '');
1705              }
1706          }
1707      }
1708  
1709  
1710      /**
1711       * Returns the name of the db field this custom field uses based on the type.
1712       *
1713       * @param   string $type
1714       * @return  string
1715       */
1716      function getDBValueFieldNameByType($type)
1717      {
1718          switch ($type) {
1719              case 'date':
1720                  return 'icf_value_date';
1721              case 'integer':
1722                  return 'icf_value_integer';
1723              default:
1724                  return 'icf_value';
1725          }
1726      }
1727  
1728  
1729      function getDBValueFieldSQL()
1730      {
1731          return "(IF(fld_type = 'date', icf_value_date, IF(fld_type = 'integer', icf_value_integer, icf_value)))";
1732      }
1733  
1734  
1735      /**
1736       * Analyzes the contents of the issue_custom_field and updates
1737       * contents based on the fld_type.
1738       *
1739       * @param   integer $fld_id
1740       */
1741      function updateValuesForNewType($fld_id)
1742      {
1743          $details = Custom_Field::getDetails($fld_id, true);
1744          $db_field_name = Custom_Field::getDBValueFieldNameByType($details['fld_type']);
1745  
1746  
1747          $sql = "UPDATE
1748                      " . APP_DEFAULT_DB . "." . APP_TABLE_PREFIX . "issue_custom_field
1749                  SET
1750                      ";
1751          if ($details['fld_type'] == 'integer') {
1752              $sql .= "$db_field_name = IFNULL(icf_value, IFNULL(icf_value_date, NULL)),
1753                      icf_value = NULL,
1754                      icf_value_date = NULL";
1755          } elseif ($details['fld_type'] == 'date') {
1756              $sql .= "$db_field_name = IFNULL(icf_value, IFNULL(icf_value_date, NULL)),
1757                      icf_value = NULL,
1758                      icf_value_integer = NULL";
1759          } else {
1760              $sql .= "$db_field_name = IFNULL(icf_value_integer, IFNULL(icf_value_date, NULL)),
1761                      icf_value_integer = NULL,
1762                      icf_value_date = NULL";
1763          }
1764          $sql .= "
1765                  WHERE
1766                      $db_field_name IS NULL AND
1767                      icf_fld_id = " . Misc::escapeInteger($fld_id);
1768          $res = $GLOBALS["db_api"]->dbh->query($sql);
1769          if (PEAR::isError($res)) {
1770              Error_Handler::logError(array($res->getMessage(), $res->getDebugInfo()), __FILE__, __LINE__);
1771              return false;
1772          }
1773          return true;
1774      }
1775  }
1776  
1777  // benchmarking the included file (aka setup time)
1778  if (APP_BENCHMARK) {
1779      $GLOBALS['bench']->setMarker('Included Custom_Field Class');
1780  }


Generated: Wed Dec 19 21:21:33 2007 Cross-referenced by PHPXref 0.7