1 <?php # vim:ts=2:sw=2:et:
3 class MTrackTicket_CustomField {
12 static function canonName($name) {
13 if (!preg_match("/^x_/", $name)) {
19 /** load the field definition from the configuration file */
20 static function load($name) {
21 if (!preg_match("/^x_[a-z_]+$/", $name)) {
22 throw new Exception("invalid field name $name");
25 $field = new MTrackTicket_CustomField;
28 $field->type = MTrackConfig::get('ticket.custom', "$name.type");
29 $field->label = MTrackConfig::get('ticket.custom', "$name.label");
30 $field->group = MTrackConfig::get('ticket.custom', "$name.group");
31 $field->order = (int)MTrackConfig::get('ticket.custom', "$name.order");
32 $field->default = MTrackConfig::get('ticket.custom', "$name.default");
33 $field->options = MTrackConfig::get('ticket.custom', "$name.options");
39 if (!preg_match("/^x_[a-z_]+$/", $this->name)) {
40 throw new Exception("invalid field name $this->name");
43 MTrackConfig::set('ticket.custom', "$name.type", $this->type);
44 MTrackConfig::set('ticket.custom', "$name.label", $this->label);
45 MTrackConfig::set('ticket.custom', "$name.group", $this->group);
46 MTrackConfig::set('ticket.custom', "$name.order", (int)$this->order);
47 MTrackConfig::set('ticket.custom', "$name.default", $this->default);
48 MTrackConfig::set('ticket.custom', "$name.options", $this->options);
51 function ticketData() {
52 /* compatible with the $FIELDSET data used in web/ticket.php */
54 'label' => $this->label,
55 'type' => $this->type,
58 if (strlen($this->default)) {
59 $data['default'] = $this->default;
62 switch ($this->type) {
66 $data['ownrow'] = true;
72 $options = array('' => ' --- ');
73 foreach (explode('|', $this->options) as $opt) {
74 $options[$opt] = $opt;
76 $data['options'] = $options;
83 class MTrackTicket_CustomFields
84 implements IMTrackIssueListener
86 var $fields = array();
88 var $field_types = array(
89 'text' => 'Text (single line)',
90 'multi' => 'Text (multi-line)',
92 'shortwiki' => 'Wiki (shorter height)',
93 'select' => 'Select box (choice of one)',
94 // Don't allow multi-select for now; need a sane way to make the value
96 // 'multiselect' => 'Multiple select',
100 $this->alterSchema();
102 $fieldlist = join(',', array_keys($this->fields));
103 MTrackConfig::set('ticket', 'customfields', $fieldlist);
105 foreach ($this->fields as $field) {
110 function fieldByName($name, $create = false) {
111 $name = MTrackTicket_CustomField::canonName($name);
112 if (!isset($this->fields[$name]) && $create) {
113 $field = new MTrackTicket_CustomField;
114 $field->name = $name;
115 $this->fields[$name] = $field;
116 } else if (!isset($this->fields[$name])) {
119 return $this->fields[$name];
122 function deleteField($field) {
123 if (!($field instanceof MTrackTicket_CustomField)) {
124 $field = $this->fieldByName($field);
126 if (!($field instanceof MTrackTicket_CustomField)) {
127 throw new Exception("can't delete an unknown field");
129 unset($this->fields[$field->name]);
132 function vetoMilestone(MTrackIssue $issue,
133 MTrackMilestone $ms, $assoc = true) {
136 function vetoKeyword(MTrackIssue $issue,
137 MTrackKeyword $kw, $assoc = true) {
140 function vetoComponent(MTrackIssue $issue,
141 MTrackComponent $comp, $assoc = true) {
144 function vetoProject(MTrackIssue $issue,
145 MTrackProject $proj, $assoc = true) {
148 function vetoComment(MTrackIssue $issue, $comment) {
151 function vetoSave(MTrackIssue $issue, $oldFields) {
155 function _orderField($a, $b) {
156 $diff = $a->order - $b->order;
158 return strnatcasecmp($a->label, $b->label);
163 function getGroupedFields() {
165 foreach ($this->fields as $field) {
166 $grouped[$field->group][$field->name] = $field;
169 $names = array_keys($grouped);
171 foreach ($grouped as $name => $group) {
172 uasort($group, array($this, '_orderField'));
173 $result[$name] = $group;
178 function augmentFormFields(MTrackIssue $issue, &$fieldset) {
179 $grouped = $this->getGroupedFields();
180 foreach ($grouped as $group) {
181 foreach ($group as $field) {
182 $fieldset[$field->group][$field->name] = $field->ticketData();
187 function augmentSaveParams(MTrackIssue $issue, &$params) {
188 foreach ($this->fields as $field) {
189 $params[$field->name] = $issue->{$field->name};
192 function augmentIndexerFields(MTrackIssue $issue, &$idx) {
193 foreach ($this->fields as $field) {
194 $idx[$field->name] = $issue->{$field->name};
198 function applyPOSTData(MTrackIssue $issue, $post) {
199 foreach ($this->fields as $field) {
200 if ($field->type == 'multiselect') {
201 $issue->{$field->name} = join('|', $post[$field->name]);
203 $issue->{$field->name} = $post[$field->name];
208 function alterSchema() {
210 foreach ($this->fields as $field) {
211 $names[] = $field->name;
213 $db = MTrackDB::get();
215 $db->exec("select " . join(', ', $names) . ' from tickets limit 1');
216 } catch (Exception $e) {
217 foreach ($names as $name) {
219 $db->exec("ALTER TABLE tickets add column $name text");
220 } catch (Exception $e) {
226 function __construct() {
227 MTrackIssue::registerListener($this);
229 /* read in custom fields from ini */
230 $fieldlist = MTrackConfig::get('ticket', 'customfields');
232 $fieldlist = preg_split("/\s*,\s*/", $fieldlist);
233 foreach ($fieldlist as $fieldname) {
234 $field = MTrackTicket_CustomField::load($fieldname);
235 $this->fields[$field->name] = $field;
241 static function getInstance() {
242 if (self::$me !== null) {
245 self::$me = new MTrackTicket_CustomFields;
250 MTrackTicket_CustomFields::getInstance();