1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-09 16:32:39 +01:00

Support for config-based custom fields in Maniphest

Test Plan: Add fields to config based on specification on T335. View on Task
Edit and Task Detail. Supported types are string, int and select
Reviewed By: epriestley
Reviewers: epriestley
CC: aran, epriestley, hunterbridges
Differential Revision: 753
This commit is contained in:
hunterbridges 2011-07-27 11:49:50 -05:00 committed by epriestley
parent b5ada76ab0
commit 4903038940
16 changed files with 464 additions and 0 deletions

View file

@ -427,6 +427,12 @@ return array(
'maniphest.enabled' => true,
// Array of custom fields for Maniphest tasks. The array should contain
// arrays of field specifications keyed with 'type', 'label', 'caption',
// 'required' and whatever specific options exist for the given field
// type.
'maniphest.custom-fields' => array(),
// -- Remarkup -------------------------------------------------------------- //
// If you enable this, linked YouTube videos will be embeded inline. This has

View file

@ -267,9 +267,14 @@ phutil_register_library_map(array(
'LiskIsolationTestCase' => 'storage/lisk/dao/__tests__',
'LiskIsolationTestDAO' => 'storage/lisk/dao/__tests__',
'LiskIsolationTestDAOException' => 'storage/lisk/dao/__tests__',
'ManiphestAuxiliaryFieldDefaultSpecification' => 'applications/maniphest/auxiliaryfield/default',
'ManiphestAuxiliaryFieldSpecification' => 'applications/maniphest/auxiliaryfield/base',
'ManiphestAuxiliaryFieldTypeException' => 'applications/maniphest/auxiliaryfield/typeexception',
'ManiphestAuxiliaryFieldValidationException' => 'applications/maniphest/auxiliaryfield/validationexception',
'ManiphestConstants' => 'applications/maniphest/constants/base',
'ManiphestController' => 'applications/maniphest/controller/base',
'ManiphestDAO' => 'applications/maniphest/storage/base',
'ManiphestDefaultTaskExtensions' => 'applications/maniphest/extensions/task',
'ManiphestReplyHandler' => 'applications/maniphest/replyhandler',
'ManiphestTask' => 'applications/maniphest/storage/task',
'ManiphestTaskAuxiliaryStorage' => 'applications/maniphest/storage/auxiliary',
@ -832,6 +837,7 @@ phutil_register_library_map(array(
'HeraldTranscriptListController' => 'HeraldController',
'LiskIsolationTestCase' => 'PhabricatorTestCase',
'LiskIsolationTestDAO' => 'LiskDAO',
'ManiphestAuxiliaryFieldDefaultSpecification' => 'ManiphestAuxiliaryFieldSpecification',
'ManiphestController' => 'PhabricatorController',
'ManiphestDAO' => 'PhabricatorLiskDAO',
'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler',

View file

@ -0,0 +1,86 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group maniphest
*/
abstract class ManiphestAuxiliaryFieldSpecification {
private $label;
private $auxiliaryKey;
private $caption;
private $value;
public function setLabel($val) {
$this->label = $val;
}
public function getLabel() {
return $this->label;
}
public function setAuxiliaryKey($val) {
$this->auxiliaryKey = $val;
}
public function getAuxiliaryKey() {
return $this->auxiliaryKey;
}
public function setCaption($val) {
$this->caption = $val;
}
public function getCaption() {
return $this->caption;
}
public function setValue($val) {
$this->value = $val;
}
public function getValue() {
return $this->value;
}
public function validate() {
return TRUE;
}
public function isRequired() {
return false;
}
public function setType($val)
{
$this->type = $val;
}
public function getType() {
return $this->type;
}
public function renderControl() {
return null;
}
public function renderForDetailView() {
return phutil_escape_html($this->getValue());
}
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phutil', 'markup');
phutil_require_source('ManiphestAuxiliaryFieldSpecification.php');

View file

@ -0,0 +1,137 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group maniphest
*/
class ManiphestAuxiliaryFieldDefaultSpecification
extends ManiphestAuxiliaryFieldSpecification {
private $required;
private $fieldType;
private $selectOptions;
private $error;
const TYPE_SELECT = 'select';
const TYPE_STRING = 'string';
const TYPE_INT = 'int';
public function getFieldType() {
return $this->fieldType;
}
public function setFieldType($val) {
$this->fieldType = $val;
}
public function getError() {
return $this->error;
}
public function setError($val) {
$this->error = $val;
}
public function getSelectOptions() {
return $this->selectOptions;
}
public function setSelectOptions($array) {
$this->selectOptions = $array;
}
public function setRequired($bool) {
$this->required = $bool;
}
public function isRequired() {
return $this->required;
}
public function renderControl() {
$control = null;
switch ($this->getFieldType()) {
case self::TYPE_INT:
$control = new AphrontFormTextControl();
break;
case self::TYPE_STRING:
$control = new AphrontFormTextControl();
break;
case self::TYPE_SELECT:
$control = new AphrontFormSelectControl();
$control->setOptions($this->getSelectOptions());
break;
default:
throw new ManiphestAuxiliaryFieldTypeException(
$this->getFieldType().' is not a valid type for '.$this->getLabel()
);
break;
}
$control->setValue($this->getValue());
$control->setLabel($this->getLabel());
$control->setName('auxiliary['.$this->getAuxiliaryKey().']');
$control->setError($this->getError());
return $control;
}
public function setValueFromRequest($request) {
$aux_post_values = $request->getArr('auxiliary');
$this->setValue(
$aux_post_values[$this->getAuxiliaryKey()]
);
}
public function getValueForStorage() {
return $this->getValue();
}
public function setValueFromStorage($value) {
$this->setValue($value);
}
public function validate() {
switch ($this->getFieldType()) {
case self::TYPE_INT:
if (!is_numeric($this->getValue())) {
throw new ManiphestAuxiliaryFieldValidationException(
$this->getLabel().' must be an integer value.'
);
}
break;
case self::TYPE_STRING:
return true;
break;
case self::TYPE_SELECT:
return true;
break;
}
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/maniphest/auxiliaryfield/base');
phutil_require_module('phabricator', 'applications/maniphest/auxiliaryfield/typeexception');
phutil_require_module('phabricator', 'applications/maniphest/auxiliaryfield/validationexception');
phutil_require_module('phabricator', 'view/form/control/select');
phutil_require_module('phabricator', 'view/form/control/text');
phutil_require_source('ManiphestAuxiliaryFieldDefaultSpecification.php');

View file

@ -0,0 +1,24 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group maniphest
*/
class ManiphestAuxiliaryFieldTypeException extends Exception {
}

View file

@ -0,0 +1,10 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_source('ManiphestAuxiliaryFieldTypeException.php');

View file

@ -0,0 +1,24 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group maniphest
*/
class ManiphestAuxiliaryFieldValidationException extends Exception {
}

View file

@ -0,0 +1,10 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_source('ManiphestAuxiliaryFieldValidationException.php');

View file

@ -41,6 +41,9 @@ class ManiphestTaskDetailController extends ManiphestController {
return new Aphront404Response();
}
$aux_fields = id(new ManiphestDefaultTaskExtensions())
->getAuxiliaryFieldSpecifications();
$transactions = id(new ManiphestTransaction())->loadAllWhere(
'taskID = %d ORDER BY id ASC',
$task->getID());
@ -112,6 +115,20 @@ class ManiphestTaskDetailController extends ManiphestController {
$dict['Projects'] = '<em>None</em>';
}
if ($aux_fields) {
foreach ($aux_fields as $aux_field) {
$attribute = $task->loadAuxiliaryAttribute(
$aux_field->getAuxiliaryKey()
);
if ($attribute) {
$aux_field->setValue($attribute->getValue());
}
$dict[$aux_field->getLabel()] = $aux_field->renderForDetailView();
}
}
if (idx($attached, PhabricatorPHIDConstants::PHID_TYPE_DREV)) {
$revs = idx($attached, PhabricatorPHIDConstants::PHID_TYPE_DREV);
$rev_links = array();

View file

@ -13,6 +13,7 @@ phutil_require_module('phabricator', 'applications/maniphest/constants/priority'
phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
phutil_require_module('phabricator', 'applications/maniphest/extensions/task');
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
phutil_require_module('phabricator', 'applications/maniphest/view/transactionlist');

View file

@ -75,6 +75,9 @@ class ManiphestTaskEditController extends ManiphestController {
$errors = array();
$e_title = true;
$aux_fields = id(new ManiphestDefaultTaskExtensions())
->getAuxiliaryFieldSpecifications();
if ($request->isFormPost()) {
$changes = array();
@ -112,6 +115,24 @@ class ManiphestTaskEditController extends ManiphestController {
$errors[] = 'Title is required.';
}
foreach ($aux_fields as $aux_field) {
$aux_field->setValueFromRequest($request);
if ($aux_field->isRequired() && !strlen($aux_field->getValue())) {
$errors[] = $aux_field->getLabel() . ' is required.';
$aux_field->setError('Required');
}
if (strlen($aux_field->getValue())) {
try {
$aux_field->validate();
} catch (Exception $e) {
$errors[] = $e->getMessage();
$aux_field->setError('Invalid');
}
}
}
if ($errors) {
$task->setPriority($request->getInt('priority'));
$task->setOwnerPHID($owner_phid);
@ -143,6 +164,14 @@ class ManiphestTaskEditController extends ManiphestController {
$changes[ManiphestTransactionType::TYPE_PROJECTS] = $new_proj_arr;
}
// TODO: Capture auxiliary field changes in a transaction
foreach ($aux_fields as $aux_field) {
$task->setAuxiliaryAttribute(
$aux_field->getAuxiliaryKey(),
$aux_field->getValueForStorage()
);
}
if ($files) {
$file_map = mpull($files, 'getPHID');
$file_map = array_fill_keys($file_map, true);
@ -312,6 +341,30 @@ class ManiphestTaskEditController extends ManiphestController {
'Create New Project'))
->setDatasource('/typeahead/common/projects/'));
$attributes = $task->loadAuxiliaryAttributes();
$attributes = mpull($attributes, 'getValue', 'getName');
foreach ($aux_fields as $aux_field)
{
if (!$request->isFormPost()) {
$attribute = null;
if (isset($attributes[$aux_field->getAuxiliaryKey()])) {
$attribute = $attributes[$aux_field->getAuxiliaryKey()];
$aux_field->setValueFromStorage($attribute);
}
}
if ($aux_field->isRequired() && !$aux_field->getError()
&& !$aux_field->getValue()) {
$aux_field->setError(true);
}
$aux_control = $aux_field->renderControl();
$form->appendChild($aux_control);
}
require_celerity_resource('aphront-error-view-css');
Javelin::initBehavior('maniphest-project-create', array(

View file

@ -14,6 +14,7 @@ phutil_require_module('phabricator', 'applications/maniphest/constants/status');
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
phutil_require_module('phabricator', 'applications/maniphest/extensions/task');
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
phutil_require_module('phabricator', 'applications/phid/constants');

View file

@ -0,0 +1,46 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group maniphest
*/
final class ManiphestDefaultTaskExtensions {
public function getAuxiliaryFieldSpecifications() {
$fields = PhabricatorEnv::getEnvConfig('maniphest.custom-fields');
$specs = array();
foreach ($fields as $aux => $info) {
$spec = new ManiphestAuxiliaryFieldDefaultSpecification();
$spec->setAuxiliaryKey($aux);
$spec->setLabel(idx($info, 'label'));
$spec->setCaption(idx($info, 'caption'));
$spec->setFieldType(idx($info, 'type'));
$spec->setRequired(idx($info, 'required'));
if ($spec->getFieldType() ==
ManiphestAuxiliaryFieldDefaultSpecification::TYPE_SELECT) {
$spec->setSelectOptions(idx($info, 'options'));
}
$specs[] = $spec;
}
return $specs;
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/maniphest/auxiliaryfield/default');
phutil_require_module('phabricator', 'infrastructure/env');
phutil_require_module('phutil', 'utils');
phutil_require_source('ManiphestDefaultTaskExtensions.php');