Start working on the new fields v2 system.
authorRobert Sesek <rsesek@bluestatic.org>
Sun, 26 May 2013 21:52:10 +0000 (17:52 -0400)
committerRobert Sesek <rsesek@bluestatic.org>
Tue, 28 May 2013 13:59:26 +0000 (09:59 -0400)
Import the new Model class from the greenfield tree and create a placeholder page
for the admin section.

admin/field2.php [new file with mode: 0644]
includes/class_admin_navigation.php
includes/init.php
includes/model_field.php [new file with mode: 0644]

diff --git a/admin/field2.php b/admin/field2.php
new file mode 100644 (file)
index 0000000..ab8e5fb
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/*=====================================================================*\
+|| ###################################################################
+|| # Bugdar
+|| # Copyright (c)2002-2013 Blue Static
+|| #
+|| # This program is free software; you can redistribute it and/or modify
+|| # it under the terms of the GNU General Public License as published by
+|| # the Free Software Foundation; version 2 of the License.
+|| #
+|| # This program is distributed in the hope that it will be useful, but
+|| # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+|| # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+|| # more details.
+|| #
+|| # You should have received a copy of the GNU General Public License along
+|| # with this program; if not, write to the Free Software Foundation, Inc.,
+|| # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+|| ###################################################################
+\*=====================================================================*/
+
+namespace bugdar\admin;
+
+require_once './global.php';
+require_once HOPLITE_ROOT . '/http/input.php';
+require_once BUGDAR_ROOT . '/includes/model_field.php';
+
+\NavLinks::fieldsPages();
+$navigator->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
index 29e4fb4b2a0f8e11a085d641e2d731c82428d827..9bd6f2adcf160d16bf566767af805a840611bd44 100644 (file)
@@ -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');
        }
        
        // ###################################################################
index 9eb39c05f2bb7c8749fdbbefab6c63ad0f35fc68..0bd673882c85144575d8fd279bd39216421bfd86 100755 (executable)
@@ -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 (file)
index 0000000..c8e5fc9
--- /dev/null
@@ -0,0 +1,222 @@
+<?php
+/*=====================================================================*\
+|| ###################################################################
+|| # Bugdar
+|| # Copyright (c)2002-2013 Blue Static
+|| #
+|| # This program is free software; you can redistribute it and/or modify
+|| # it under the terms of the GNU General Public License as published by
+|| # the Free Software Foundation; version 2 of the License.
+|| #
+|| # This program is distributed in the hope that it will be useful, but
+|| # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+|| # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+|| # more details.
+|| #
+|| # You should have received a copy of the GNU General Public License along
+|| # with this program; if not, write to the Free Software Foundation, Inc.,
+|| # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+|| ###################################################################
+\*=====================================================================*/
+
+namespace bugdar;
+
+require_once HOPLITE_ROOT . '/data/model.php';
+require_once './includes/api_user.php';
+
+class Field extends \hoplite\data\Model
+{
+       // Model properties.
+       protected $table_prefix = TABLE_PREFIX;
+       protected $table = 'field';
+       protected $primary_key = 'title';
+       protected $condition = 'title = :title';
+
+       // Struct properties.
+       protected $fields = array(
+               'title',
+               'description',
+               'type',  // See constants below.
+               'validator_pattern',  // Stores list options and string regex.
+               'required',
+               'default_value',  // String. Or TRUE for TYPE_DATE to mean today.
+               'can_search',
+               'color_foreground',
+               'color_background'
+       );
+
+       // Types of fields {{
+               const TYPE_TEXT = 'text';
+               const TYPE_BOOL = 'boolean';
+               const TYPE_LIST = 'list';
+               const TYPE_DATE = 'date';
+               const TYPE_USER = 'user';
+       // }}
+
+       // Usergroup access controls {{
+               const ACCESS_NONE  = 0;
+               const ACCESS_READ  = 1;
+               const ACCESS_WRITE = 2;
+       // }}
+
+       public function is_tag()
+       {
+               $title = $this->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<bool,mixed>. 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