title; return empty($title); } public function is_field() { $title = $this->title; return !empty($title); } // Returns the access level that |user| has for this field for |bug|. public function CheckAccess(User $user, $bug) { return self::ACCESS_READ | self::ACCESS_WRITE; } // Validates the value of a field. Returns a 2-Tuple. The // first item is whether or not the value validated. The second item is the // validated value, if any transformation took place. public function Validate($value) { switch ($this->type) { case self::TYPE_TEXT: return $this->_ValidateText($value); case self::TYPE_BOOL: return $this->_ValidateBoolean($value); case self::TYPE_LIST: return $this->_ValidateList($value); case self::TYPE_DATE: return $this->_ValidateDate($value); case self::TYPE_USER: return $this->_ValidateUser($value); default: throw new FieldException('Unknown field type "' . $this->type . '"'); } } protected function _ValidateText($value) { $value = trim($value); // Handle empty strings, including the default value. if ($this->required && empty($value) && !$this->default_value) { return array(FALSE, $value); } else if ($this->default_value) { return array(TRUE, $this->default_value); } // Validate using pattern. if ($this->validator_pattern) { $valid = preg_match("/{$this->validator_pattern}/", $value); return array($valid !== FALSE && $valid > 0, $value); } // All other values are valid. return array(TRUE, $value); } protected function _ValidateBoolean($value) { // Booleans are technically tri-state: true, false, and unset. The only // time the default value can be used is in the unset state. if ($value === NULL && $this->default_value !== NULL) { return array(TRUE, $this->default_value); } // Parse booleans in a bunch of different ways. $value = trim(strtolower($value)); if (intval($value[0]) == 1 || $value[0] === TRUE || $value[0] == 'y' || $value[0] == 't') { return array(TRUE, TRUE); } // Everything else will assume false. Don't bother failing validation. return array(TRUE, FALSE); } protected function _ValidateList($value) { // Handle empty values, including the default value. if ($this->required && empty($value) && !$this->default_value) { return array(FALSE, $value); } else if ($this->default_value) { return array(TRUE, $this->default_value); } // Otherwise, iterate over the possible values. $options = $this->GetListOptions(); $value = trim($value); foreach ($options as $option) { if (strcasecmp($option, $value) == 0) { // Return the proper case from the canonical option value. return array(TRUE, $option); } } return array(FALSE, $value); } protected function _ValidateDate($value) { // Handle the one default value (now). if ($this->required && empty($value) && !$this->default_value) { return array(FALSE, $value); } else if ($this->default_value) { return array(TRUE, time()); } $time = strtotime($value); if ($time === FALSE) { return array(FALSE, $value); } else { return array(TRUE, $time); } } protected function _ValidateUser($value) { // Handle the default value. if ($this->required && empty($value) && !$this->default_value) { return array(FALSE, $value); } else if ($this->default_value) { return array(TRUE, $this->default_value); } // Look the user up by alias to get the user ID. $user = new User(); $user->set('displayname', $value); $user->set_condition('displayname = :displayname'); try { $user->FetchInto(); return array(TRUE, $user->userid); } catch (\phalanx\data\ModelException $e) { return array(FALSE, $value); } } // If this Field is TYPE_LIST, this will return an array of options for // the list. Note that bugs store values rather than indices, so comparison // is case-insensitive string compare to determine if a value is a member // of the set. public function GetListOptions() { if ($this->type != self::TYPE_LIST) { throw new FieldException('"' . $this->title . '" is not a list'); } return explode("\n", $this->validator_pattern); } // Sets the valid options for the list. This will replace all current // options. Note that bugs will retain their current values if an option is // removed, as they store the actual value, rather than a reference to the // value. public function SetListOptions(Array $options) { if ($this->type != self::TYPE_LIST) { throw new FieldException('"' . $this->title . '" is not a list'); } $str_filter = '/[^a-z0-9_\-\.,]/i'; foreach ($options as $i => $option) { $options[$i] = preg_replace($str_filter, '', $option); } $this->validator_pattern = implode("\n", $options); } } class FieldException extends \Exception {}