mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 00:32:42 +01:00
Tweak Maniphest custom fields
Summary: - Fix a bug where 'caption' didn't do anything. - Provide an abstract base implementation for extensions. - Add some documentation. - Expose aux fields via conduit. Test Plan: Added some fields like "Dinosaur", "Kilograms" and "derp" on my local install. Read documentation. Reviewed By: jungejason Reviewers: hunterbridges, jungejason, tuomaspelkonen, aran CC: aran, philc, jungejason Differential Revision: 785
This commit is contained in:
parent
7aa1eff383
commit
e5ecd784ec
14 changed files with 147 additions and 17 deletions
|
@ -453,12 +453,13 @@ return array(
|
||||||
|
|
||||||
'maniphest.enabled' => true,
|
'maniphest.enabled' => true,
|
||||||
|
|
||||||
// Array of custom fields for Maniphest tasks. The array should contain
|
// Array of custom fields for Maniphest tasks. For details on adding custom
|
||||||
// arrays of field specifications keyed with 'type', 'label', 'caption',
|
// fields to Maniphest, see "Maniphest User Guide: Adding Custom Fields".
|
||||||
// 'required' and whatever specific options exist for the given field
|
|
||||||
// type.
|
|
||||||
'maniphest.custom-fields' => array(),
|
'maniphest.custom-fields' => array(),
|
||||||
|
|
||||||
|
// Class which drives custom field construction. See "Maniphest User Guide:
|
||||||
|
// Adding Custom Fields" in the documentation for more information.
|
||||||
|
'maniphest.custom-task-extensions-class' => 'ManiphestDefaultTaskExtensions',
|
||||||
|
|
||||||
// -- Remarkup -------------------------------------------------------------- //
|
// -- Remarkup -------------------------------------------------------------- //
|
||||||
|
|
||||||
|
|
|
@ -291,6 +291,7 @@ phutil_register_library_map(array(
|
||||||
'ManiphestTaskDescriptionChangeController' => 'applications/maniphest/controller/descriptionchange',
|
'ManiphestTaskDescriptionChangeController' => 'applications/maniphest/controller/descriptionchange',
|
||||||
'ManiphestTaskDetailController' => 'applications/maniphest/controller/taskdetail',
|
'ManiphestTaskDetailController' => 'applications/maniphest/controller/taskdetail',
|
||||||
'ManiphestTaskEditController' => 'applications/maniphest/controller/taskedit',
|
'ManiphestTaskEditController' => 'applications/maniphest/controller/taskedit',
|
||||||
|
'ManiphestTaskExtensions' => 'applications/maniphest/extensions/base',
|
||||||
'ManiphestTaskListController' => 'applications/maniphest/controller/tasklist',
|
'ManiphestTaskListController' => 'applications/maniphest/controller/tasklist',
|
||||||
'ManiphestTaskListView' => 'applications/maniphest/view/tasklist',
|
'ManiphestTaskListView' => 'applications/maniphest/view/tasklist',
|
||||||
'ManiphestTaskOwner' => 'applications/maniphest/constants/owner',
|
'ManiphestTaskOwner' => 'applications/maniphest/constants/owner',
|
||||||
|
@ -868,6 +869,7 @@ phutil_register_library_map(array(
|
||||||
'ManiphestAuxiliaryFieldDefaultSpecification' => 'ManiphestAuxiliaryFieldSpecification',
|
'ManiphestAuxiliaryFieldDefaultSpecification' => 'ManiphestAuxiliaryFieldSpecification',
|
||||||
'ManiphestController' => 'PhabricatorController',
|
'ManiphestController' => 'PhabricatorController',
|
||||||
'ManiphestDAO' => 'PhabricatorLiskDAO',
|
'ManiphestDAO' => 'PhabricatorLiskDAO',
|
||||||
|
'ManiphestDefaultTaskExtensions' => 'ManiphestTaskExtensions',
|
||||||
'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler',
|
'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||||
'ManiphestTask' => 'ManiphestDAO',
|
'ManiphestTask' => 'ManiphestDAO',
|
||||||
'ManiphestTaskAuxiliaryStorage' => 'ManiphestDAO',
|
'ManiphestTaskAuxiliaryStorage' => 'ManiphestDAO',
|
||||||
|
|
|
@ -49,6 +49,9 @@ class ConduitAPI_maniphest_info_Method extends ConduitAPIMethod {
|
||||||
throw new ConduitException('ERR_BAD_TASK');
|
throw new ConduitException('ERR_BAD_TASK');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$auxiliary = $task->loadAuxiliaryAttributes();
|
||||||
|
$auxiliary = mpull($auxiliary, 'getValue', 'getName');
|
||||||
|
|
||||||
$result = array(
|
$result = array(
|
||||||
'id' => $task->getID(),
|
'id' => $task->getID(),
|
||||||
'phid' => $task->getPHID(),
|
'phid' => $task->getPHID(),
|
||||||
|
@ -63,6 +66,8 @@ class ConduitAPI_maniphest_info_Method extends ConduitAPIMethod {
|
||||||
'projectPHIDs' => $task->getProjectPHIDs(),
|
'projectPHIDs' => $task->getProjectPHIDs(),
|
||||||
'uri' => PhabricatorEnv::getProductionURI('/T'.$task->getID()),
|
'uri' => PhabricatorEnv::getProductionURI('/T'.$task->getID()),
|
||||||
|
|
||||||
|
'auxiliary' => $auxiliary,
|
||||||
|
|
||||||
// Not sure what this is yet.
|
// Not sure what this is yet.
|
||||||
// 'attached' => array($task->getAttached()),
|
// 'attached' => array($task->getAttached()),
|
||||||
);
|
);
|
||||||
|
|
|
@ -38,6 +38,7 @@ class ManiphestAuxiliaryFieldDefaultSpecification
|
||||||
|
|
||||||
public function setFieldType($val) {
|
public function setFieldType($val) {
|
||||||
$this->fieldType = $val;
|
$this->fieldType = $val;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getError() {
|
public function getError() {
|
||||||
|
@ -46,6 +47,7 @@ class ManiphestAuxiliaryFieldDefaultSpecification
|
||||||
|
|
||||||
public function setError($val) {
|
public function setError($val) {
|
||||||
$this->error = $val;
|
$this->error = $val;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSelectOptions() {
|
public function getSelectOptions() {
|
||||||
|
@ -54,10 +56,12 @@ class ManiphestAuxiliaryFieldDefaultSpecification
|
||||||
|
|
||||||
public function setSelectOptions($array) {
|
public function setSelectOptions($array) {
|
||||||
$this->selectOptions = $array;
|
$this->selectOptions = $array;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRequired($bool) {
|
public function setRequired($bool) {
|
||||||
$this->required = $bool;
|
$this->required = $bool;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isRequired() {
|
public function isRequired() {
|
||||||
|
@ -91,6 +95,7 @@ class ManiphestAuxiliaryFieldDefaultSpecification
|
||||||
|
|
||||||
$control->setValue($this->getValue());
|
$control->setValue($this->getValue());
|
||||||
$control->setLabel($this->getLabel());
|
$control->setLabel($this->getLabel());
|
||||||
|
$control->setCaption($this->getCaption());
|
||||||
$control->setName('auxiliary['.$this->getAuxiliaryKey().']');
|
$control->setName('auxiliary['.$this->getAuxiliaryKey().']');
|
||||||
$control->setError($this->getError());
|
$control->setError($this->getError());
|
||||||
|
|
||||||
|
@ -99,10 +104,7 @@ class ManiphestAuxiliaryFieldDefaultSpecification
|
||||||
|
|
||||||
public function setValueFromRequest($request) {
|
public function setValueFromRequest($request) {
|
||||||
$aux_post_values = $request->getArr('auxiliary');
|
$aux_post_values = $request->getArr('auxiliary');
|
||||||
|
$this->setValue(idx($aux_post_values, $this->getAuxiliaryKey()));
|
||||||
$this->setValue(
|
|
||||||
$aux_post_values[$this->getAuxiliaryKey()]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getValueForStorage() {
|
public function getValueForStorage() {
|
||||||
|
|
|
@ -12,5 +12,7 @@ phutil_require_module('phabricator', 'applications/maniphest/auxiliaryfield/vali
|
||||||
phutil_require_module('phabricator', 'view/form/control/select');
|
phutil_require_module('phabricator', 'view/form/control/select');
|
||||||
phutil_require_module('phabricator', 'view/form/control/text');
|
phutil_require_module('phabricator', 'view/form/control/text');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
phutil_require_source('ManiphestAuxiliaryFieldDefaultSpecification.php');
|
phutil_require_source('ManiphestAuxiliaryFieldDefaultSpecification.php');
|
||||||
|
|
|
@ -47,8 +47,8 @@ class ManiphestTaskDetailController extends ManiphestController {
|
||||||
$parent_task = id(new ManiphestTask())->load($workflow);
|
$parent_task = id(new ManiphestTask())->load($workflow);
|
||||||
}
|
}
|
||||||
|
|
||||||
$aux_fields = id(new ManiphestDefaultTaskExtensions())
|
$extensions = ManiphestTaskExtensions::newExtensions();
|
||||||
->getAuxiliaryFieldSpecifications();
|
$aux_fields = $extensions->getAuxiliaryFieldSpecifications();
|
||||||
|
|
||||||
$transactions = id(new ManiphestTransaction())->loadAllWhere(
|
$transactions = id(new ManiphestTransaction())->loadAllWhere(
|
||||||
'taskID = %d ORDER BY id ASC',
|
'taskID = %d ORDER BY id ASC',
|
||||||
|
|
|
@ -13,7 +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/status');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
|
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
|
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/extensions/task');
|
phutil_require_module('phabricator', 'applications/maniphest/extensions/base');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
|
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
|
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/view/transactionlist');
|
phutil_require_module('phabricator', 'applications/maniphest/view/transactionlist');
|
||||||
|
|
|
@ -85,8 +85,8 @@ class ManiphestTaskEditController extends ManiphestController {
|
||||||
$errors = array();
|
$errors = array();
|
||||||
$e_title = true;
|
$e_title = true;
|
||||||
|
|
||||||
$aux_fields = id(new ManiphestDefaultTaskExtensions())
|
$extensions = ManiphestTaskExtensions::newExtensions();
|
||||||
->getAuxiliaryFieldSpecifications();
|
$aux_fields = $extensions->getAuxiliaryFieldSpecifications();
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
|
|
||||||
|
@ -394,8 +394,7 @@ class ManiphestTaskEditController extends ManiphestController {
|
||||||
$attributes = $task->loadAuxiliaryAttributes();
|
$attributes = $task->loadAuxiliaryAttributes();
|
||||||
$attributes = mpull($attributes, 'getValue', 'getName');
|
$attributes = mpull($attributes, 'getValue', 'getName');
|
||||||
|
|
||||||
foreach ($aux_fields as $aux_field)
|
foreach ($aux_fields as $aux_field) {
|
||||||
{
|
|
||||||
if (!$request->isFormPost()) {
|
if (!$request->isFormPost()) {
|
||||||
$attribute = null;
|
$attribute = null;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ phutil_require_module('phabricator', 'applications/maniphest/constants/status');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
|
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
|
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
|
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/extensions/task');
|
phutil_require_module('phabricator', 'applications/maniphest/extensions/base');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
|
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
|
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
|
||||||
phutil_require_module('phabricator', 'applications/phid/constants');
|
phutil_require_module('phabricator', 'applications/phid/constants');
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?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 ManiphestTaskExtensions {
|
||||||
|
|
||||||
|
final public function __construct() {
|
||||||
|
// <empty>
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function getAuxiliaryFieldSpecifications();
|
||||||
|
|
||||||
|
|
||||||
|
final public static function newExtensions() {
|
||||||
|
$key = 'maniphest.custom-task-extensions-class';
|
||||||
|
$class = PhabricatorEnv::getEnvConfig($key);
|
||||||
|
return newv($class, array());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
src/applications/maniphest/extensions/base/__init__.php
Normal file
14
src/applications/maniphest/extensions/base/__init__.php
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('ManiphestTaskExtensions.php');
|
|
@ -19,7 +19,8 @@
|
||||||
/**
|
/**
|
||||||
* @group maniphest
|
* @group maniphest
|
||||||
*/
|
*/
|
||||||
final class ManiphestDefaultTaskExtensions {
|
final class ManiphestDefaultTaskExtensions
|
||||||
|
extends ManiphestTaskExtensions {
|
||||||
|
|
||||||
public function getAuxiliaryFieldSpecifications() {
|
public function getAuxiliaryFieldSpecifications() {
|
||||||
$fields = PhabricatorEnv::getEnvConfig('maniphest.custom-fields');
|
$fields = PhabricatorEnv::getEnvConfig('maniphest.custom-fields');
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
|
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/auxiliaryfield/default');
|
phutil_require_module('phabricator', 'applications/maniphest/auxiliaryfield/default');
|
||||||
|
phutil_require_module('phabricator', 'applications/maniphest/extensions/base');
|
||||||
phutil_require_module('phabricator', 'infrastructure/env');
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
66
src/docs/userguide/maniphest_custom.diviner
Normal file
66
src/docs/userguide/maniphest_custom.diviner
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
@title Maniphest User Guide: Adding Custom Fields
|
||||||
|
@group userguide
|
||||||
|
|
||||||
|
How to add custom fields to Maniphest.
|
||||||
|
|
||||||
|
= Overview =
|
||||||
|
|
||||||
|
Maniphest provides some support for adding new fields to tasks, like an
|
||||||
|
"cost" field, a "milestone" field, etc.
|
||||||
|
|
||||||
|
NOTE: Currently, these fields are somewhat limited. They primarily give you a
|
||||||
|
structured way to record data on tasks, but there isn't much support for
|
||||||
|
bringing them into other interfaces (e.g., querying by them, aggregating them,
|
||||||
|
drawing graphs, etc.). If you have a use case, let us know what you want to do
|
||||||
|
and maybe we can figure something out. This data is also exposed via the Conduit
|
||||||
|
API, so you might be able to write your own interface if you want to do
|
||||||
|
something very custom.
|
||||||
|
|
||||||
|
= Simple Field Customization =
|
||||||
|
|
||||||
|
If you don't need complicated display controls or sophisticated validation, you
|
||||||
|
can add simple fields. These allow you to attach things like strings, numbers,
|
||||||
|
and dropdown menus to the task template.
|
||||||
|
|
||||||
|
Customize Maniphest fields by setting ##maniphest.custom-fields## in your
|
||||||
|
configuration. For example, suppose you want to add "Estimated Hours" and
|
||||||
|
"Actual Hours" fields. To do this, set your configuration like this:
|
||||||
|
|
||||||
|
'maniphest.custom-fields' => array(
|
||||||
|
'mycompany:estimated-hours' => array(
|
||||||
|
'label' => 'Estimated Hours',
|
||||||
|
'type' => 'int',
|
||||||
|
'caption' => 'Estimated number of hours this will take.',
|
||||||
|
'required' => false,
|
||||||
|
),
|
||||||
|
'mycompany:actual-hours' => array(
|
||||||
|
'label' => 'Actual Hours',
|
||||||
|
'type' => 'int',
|
||||||
|
'required' => false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
Each array key must be unique, and is used to organize the internal storage of
|
||||||
|
the field. These options are available:
|
||||||
|
|
||||||
|
- **label**: Display label for the field on the edit and detail interfaces.
|
||||||
|
- **type**: Field type, one of **int**, **string** or **select**.
|
||||||
|
- **caption**: A caption to display underneath the field (optional).
|
||||||
|
- **required**: True if the user should be required to provide a value.
|
||||||
|
- **options**: If type is set to **select**, provide options for the dropdown
|
||||||
|
as a dictionary.
|
||||||
|
|
||||||
|
= Advanced Field Customization =
|
||||||
|
|
||||||
|
If you want to add fields with more specialized validation, storage, or
|
||||||
|
rendering logic, you can do so with a little work:
|
||||||
|
|
||||||
|
- Extend @{class:ManiphestAuxiliaryFieldSpecification} and implement
|
||||||
|
your specialized rendering, validation, storage, etc., logic.
|
||||||
|
- Extend @{class:ManiphestTaskExtensions} and return a list of fields which
|
||||||
|
includes your custom field objects.
|
||||||
|
- Set 'maniphest.custom-extensions' to the name of your new extensions
|
||||||
|
class.
|
||||||
|
|
||||||
|
This is relatively advanced but should give you significant flexibility in
|
||||||
|
defining custom fields.
|
Loading…
Reference in a new issue