2 /*=====================================================================*\
3 || ###################################################################
5 || # Copyright (c)2002-2013 Blue Static
7 || # This program is free software; you can redistribute it and/or modify
8 || # it under the terms of the GNU General Public License as published by
9 || # the Free Software Foundation; version 2 of the License.
11 || # This program is distributed in the hope that it will be useful, but
12 || # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 || # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 || # You should have received a copy of the GNU General Public License along
17 || # with this program; if not, write to the Free Software Foundation, Inc.,
18 || # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 || ###################################################################
20 \*=====================================================================*/
24 require_once HOPLITE_ROOT
. '/data/model.php';
25 require_once './includes/api_user.php';
27 class Field
extends \hoplite\data\Model
30 protected $table_prefix = TABLE_PREFIX
;
31 protected $table = 'field';
32 protected $primary_key = 'title';
33 protected $condition = 'title = :title';
36 protected $fields = array(
39 'type', // See constants below.
40 'validator_pattern', // Stores list options and string regex.
42 'default_value', // String. Or TRUE for TYPE_DATE to mean today.
49 const TYPE_TEXT
= 'text';
50 const TYPE_BOOL
= 'boolean';
51 const TYPE_LIST
= 'list';
52 const TYPE_DATE
= 'date';
53 const TYPE_USER
= 'user';
56 // Usergroup access controls {{
57 const ACCESS_NONE
= 0;
58 const ACCESS_READ
= 1;
59 const ACCESS_WRITE
= 2;
62 public function is_tag()
64 $title = $this->title
;
67 public function is_field()
69 $title = $this->title
;
70 return !empty($title);
73 // Returns the access level that |user| has for this field for |bug|.
74 public function CheckAccess(User
$user, $bug)
76 return self
::ACCESS_READ
| self
::ACCESS_WRITE
;
79 // Validates the value of a field. Returns a 2-Tuple<bool,mixed>. The
80 // first item is whether or not the value validated. The second item is the
81 // validated value, if any transformation took place.
82 public function Validate($value)
84 switch ($this->type
) {
85 case self
::TYPE_TEXT
: return $this->_ValidateText($value);
86 case self
::TYPE_BOOL
: return $this->_ValidateBoolean($value);
87 case self
::TYPE_LIST
: return $this->_ValidateList($value);
88 case self
::TYPE_DATE
: return $this->_ValidateDate($value);
89 case self
::TYPE_USER
: return $this->_ValidateUser($value);
90 default: throw new FieldException('Unknown field type "' . $this->type
. '"');
94 protected function _ValidateText($value)
96 $value = trim($value);
98 // Handle empty strings, including the default value.
99 if ($this->required
&& empty($value) && !$this->default_value
) {
100 return array(FALSE, $value);
101 } else if ($this->default_value
) {
102 return array(TRUE, $this->default_value
);
105 // Validate using pattern.
106 if ($this->validator_pattern
) {
107 $valid = preg_match("/{$this->validator_pattern}/", $value);
108 return array($valid !== FALSE && $valid > 0, $value);
111 // All other values are valid.
112 return array(TRUE, $value);
115 protected function _ValidateBoolean($value)
117 // Booleans are technically tri-state: true, false, and unset. The only
118 // time the default value can be used is in the unset state.
119 if ($value === NULL && $this->default_value
!== NULL) {
120 return array(TRUE, $this->default_value
);
123 // Parse booleans in a bunch of different ways.
124 $value = trim(strtolower($value));
125 if (intval($value[0]) == 1 || $value[0] === TRUE ||
126 $value[0] == 'y' || $value[0] == 't') {
127 return array(TRUE, TRUE);
129 // Everything else will assume false. Don't bother failing validation.
130 return array(TRUE, FALSE);
133 protected function _ValidateList($value)
135 // Handle empty values, including the default value.
136 if ($this->required
&& empty($value) && !$this->default_value
) {
137 return array(FALSE, $value);
138 } else if ($this->default_value
) {
139 return array(TRUE, $this->default_value
);
142 // Otherwise, iterate over the possible values.
143 $options = $this->GetListOptions();
144 $value = trim($value);
145 foreach ($options as $option) {
146 if (strcasecmp($option, $value) == 0) {
147 // Return the proper case from the canonical option value.
148 return array(TRUE, $option);
151 return array(FALSE, $value);
154 protected function _ValidateDate($value)
156 // Handle the one default value (now).
157 if ($this->required
&& empty($value) && !$this->default_value
) {
158 return array(FALSE, $value);
159 } else if ($this->default_value
) {
160 return array(TRUE, time());
163 $time = strtotime($value);
164 if ($time === FALSE) {
165 return array(FALSE, $value);
167 return array(TRUE, $time);
171 protected function _ValidateUser($value)
173 // Handle the default value.
174 if ($this->required
&& empty($value) && !$this->default_value
) {
175 return array(FALSE, $value);
176 } else if ($this->default_value
) {
177 return array(TRUE, $this->default_value
);
180 // Look the user up by alias to get the user ID.
182 $user->set('displayname', $value);
183 $user->set_condition('displayname = :displayname');
186 return array(TRUE, $user->userid
);
187 } catch (\phalanx\data\ModelException
$e) {
188 return array(FALSE, $value);
192 // If this Field is TYPE_LIST, this will return an array of options for
193 // the list. Note that bugs store values rather than indices, so comparison
194 // is case-insensitive string compare to determine if a value is a member
196 public function GetListOptions()
198 if ($this->type
!= self
::TYPE_LIST
) {
199 throw new FieldException('"' . $this->title
. '" is not a list');
201 return explode("\n", $this->validator_pattern
);
204 // Sets the valid options for the list. This will replace all current
205 // options. Note that bugs will retain their current values if an option is
206 // removed, as they store the actual value, rather than a reference to the
208 public function SetListOptions(Array $options)
210 if ($this->type
!= self
::TYPE_LIST
) {
211 throw new FieldException('"' . $this->title
. '" is not a list');
213 $str_filter = '/[^a-z0-9_\-\.,]/i';
214 foreach ($options as $i => $option) {
215 $options[$i] = preg_replace($str_filter, '', $option);
217 $this->validator_pattern
= implode("\n", $options);
221 class FieldException
extends \Exception