From 722ab62833c5d56f167daaed2123db9502fedfb0 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Sun, 26 May 2013 17:52:10 -0400 Subject: [PATCH] Start working on the new fields v2 system. Import the new Model class from the greenfield tree and create a placeholder page for the admin section. --- admin/field2.php | 37 +++++ includes/class_admin_navigation.php | 5 +- includes/init.php | 2 + includes/model_field.php | 222 ++++++++++++++++++++++++++++ 4 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 admin/field2.php create mode 100644 includes/model_field.php diff --git a/admin/field2.php b/admin/field2.php new file mode 100644 index 0000000..ab8e5fb --- /dev/null +++ b/admin/field2.php @@ -0,0 +1,37 @@ +set_focus('tab', 'fields', NULL); +$navigator->set_focus('link', 'fields-pages-v2', 'fields-pages'); + +if (!can_perform('canadminfields')) + admin_login(); + +$admin->page_start('Fields V2'); + +$admin->page_end(); \ No newline at end of file diff --git a/includes/class_admin_navigation.php b/includes/class_admin_navigation.php index 29e4fb4..9bd6f2a 100644 --- a/includes/class_admin_navigation.php +++ b/includes/class_admin_navigation.php @@ -138,7 +138,7 @@ class NavLinks function fieldsPages() { global $navigator; - + $navigator->add_component('section', 'fields-pages', 'fields', T('Fields'), null); $navigator->add_component('link', 'fields-pages-fields', 'fields-pages', T('Custom Fields'), 'field.php'); $navigator->add_component('link', 'fields-pages-priorities', 'fields-pages', T('Priorities'), 'priority.php'); @@ -146,6 +146,9 @@ class NavLinks $navigator->add_component('link', 'fields-pages-severities', 'fields-pages', T('Severities'), 'severity.php'); $navigator->add_component('link', 'fields-pages-statuses', 'fields-pages', T('Statuses'), 'status.php'); $navigator->add_component('link', 'fields-pages-automations', 'fields-pages', T('Automations'), 'automation.php'); + + // TODO(port): remove this. + $navigator->add_component('link', 'fields-pages-v2', 'fields-pages', 'FIELDS - V2', 'field2.php'); } // ################################################################### diff --git a/includes/init.php b/includes/init.php index 9eb39c0..0bd6738 100755 --- a/includes/init.php +++ b/includes/init.php @@ -91,6 +91,8 @@ class bugdar static $datastore = array(); } +define('BUGDAR_ROOT', dirname(dirname(__FILE__))); + // ################################################################### // Initialize Hoplite concurrently with ISSO 2.x. diff --git a/includes/model_field.php b/includes/model_field.php new file mode 100644 index 0000000..c8e5fc9 --- /dev/null +++ b/includes/model_field.php @@ -0,0 +1,222 @@ +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 +{} \ No newline at end of file -- 2.22.5