diff --git a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php index f1916cffec..077fc511ce 100644 --- a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php +++ b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php @@ -1,498 +1,503 @@ array( 'name' => pht('Unbreak Now!'), 'keywords' => array('unbreak'), 'short' => pht('Unbreak!'), 'color' => 'pink', ), 90 => array( 'name' => pht('Needs Triage'), 'keywords' => array('triage'), 'short' => pht('Triage'), 'color' => 'violet', ), 80 => array( 'name' => pht('High'), 'keywords' => array('high'), 'short' => pht('High'), 'color' => 'red', ), 50 => array( 'name' => pht('Normal'), 'keywords' => array('normal'), 'short' => pht('Normal'), 'color' => 'orange', ), 25 => array( 'name' => pht('Low'), 'keywords' => array('low'), 'short' => pht('Low'), 'color' => 'yellow', ), 0 => array( 'name' => pht('Wishlist'), 'keywords' => array('wish', 'wishlist'), 'short' => pht('Wish'), 'color' => 'sky', ), ); $status_type = 'maniphest.statuses'; $status_defaults = array( 'open' => array( 'name' => pht('Open'), 'special' => ManiphestTaskStatus::SPECIAL_DEFAULT, 'prefixes' => array( 'open', 'opens', 'reopen', 'reopens', ), ), 'resolved' => array( 'name' => pht('Resolved'), 'name.full' => pht('Closed, Resolved'), 'closed' => true, 'special' => ManiphestTaskStatus::SPECIAL_CLOSED, 'transaction.icon' => 'fa-check-circle', 'prefixes' => array( 'closed', 'closes', 'close', 'fix', 'fixes', 'fixed', 'resolve', 'resolves', 'resolved', ), 'suffixes' => array( 'as resolved', 'as fixed', ), 'keywords' => array('closed', 'fixed', 'resolved'), ), 'wontfix' => array( 'name' => pht('Wontfix'), 'name.full' => pht('Closed, Wontfix'), 'transaction.icon' => 'fa-ban', 'closed' => true, 'prefixes' => array( 'wontfix', 'wontfixes', 'wontfixed', ), 'suffixes' => array( 'as wontfix', ), ), 'invalid' => array( 'name' => pht('Invalid'), 'name.full' => pht('Closed, Invalid'), 'transaction.icon' => 'fa-minus-circle', 'closed' => true, 'claim' => false, 'prefixes' => array( 'invalidate', 'invalidates', 'invalidated', ), 'suffixes' => array( 'as invalid', ), ), 'duplicate' => array( 'name' => pht('Duplicate'), 'name.full' => pht('Closed, Duplicate'), 'transaction.icon' => 'fa-files-o', 'special' => ManiphestTaskStatus::SPECIAL_DUPLICATE, 'closed' => true, 'claim' => false, ), 'spite' => array( 'name' => pht('Spite'), 'name.full' => pht('Closed, Spite'), 'name.action' => pht('Spited'), 'transaction.icon' => 'fa-thumbs-o-down', 'silly' => true, 'closed' => true, 'prefixes' => array( 'spite', 'spites', 'spited', ), 'suffixes' => array( 'out of spite', 'as spite', ), ), ); $status_description = $this->deformat(pht(<<.// Allows you to specify a list of text prefixes which will trigger a task transition into this status when mentioned in a commit message. For example, providing "closes" here will allow users to move tasks to this status by writing `Closes T123` in commit messages. - `suffixes` //Optional list.// Allows you to specify a list of text suffixes which will trigger a task transition into this status when mentioned in a commit message, after a valid prefix. For example, providing "as invalid" here will allow users to move tasks to this status by writing `Closes T123 as invalid`, even if another status is selected by the "Closes" prefix. - `keywords` //Optional list.// Allows you to specify a list of keywords which can be used with `!status` commands in email to select this status. - `disabled` //Optional bool.// Marks this status as no longer in use so tasks can not be created or edited to have this status. Existing tasks with this status will not be affected, but you can batch edit them or let them die out on their own. - `claim` //Optional bool.// By default, closing an unassigned task claims it. You can set this to `false` to disable this behavior for a particular status. - `locked` //Optional string.// Lock tasks in this status. Specify "comments" to lock comments (users who can edit the task may override this lock). Specify "edits" to prevent anyone except the task owner from making edits. - `mfa` //Optional bool.// Require all edits to this task to be signed with multi-factor authentication. Statuses will appear in the UI in the order specified. Note the status marked `special` as `duplicate` is not settable directly and will not appear in UI elements, and that any status marked `silly` does not appear if Phabricator is configured with `phabricator.serious-business` set to true. Examining the default configuration and examples below will probably be helpful in understanding these options. EOTEXT )); $status_example = array( 'open' => array( 'name' => pht('Open'), 'special' => 'default', ), 'closed' => array( 'name' => pht('Closed'), 'special' => 'closed', 'closed' => true, ), 'duplicate' => array( 'name' => pht('Duplicate'), 'special' => 'duplicate', 'closed' => true, ), ); $json = new PhutilJSON(); $status_example = $json->encodeFormatted($status_example); // This is intentionally blank for now, until we can move more Maniphest // logic to custom fields. $default_fields = array(); foreach ($default_fields as $key => $enabled) { $default_fields[$key] = array( 'disabled' => !$enabled, ); } $custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType'; $fields_example = array( 'mycompany.estimated-hours' => array( 'name' => pht('Estimated Hours'), 'type' => 'int', 'caption' => pht('Estimated number of hours this will take.'), ), ); $fields_json = id(new PhutilJSON())->encodeFormatted($fields_example); $points_type = 'maniphest.points'; $points_example_1 = array( 'enabled' => true, 'label' => pht('Story Points'), 'action' => pht('Change Story Points'), ); $points_json_1 = id(new PhutilJSON())->encodeFormatted($points_example_1); $points_example_2 = array( 'enabled' => true, 'label' => pht('Estimated Hours'), 'action' => pht('Change Estimate'), ); $points_json_2 = id(new PhutilJSON())->encodeFormatted($points_example_2); $points_description = $this->deformat(pht(<< $subtype_default_key, 'name' => pht('Task'), ), array( 'key' => 'bug', 'name' => pht('Bug'), ), array( 'key' => 'feature', 'name' => pht('Feature Request'), ), ); $subtype_example = id(new PhutilJSON())->encodeAsList($subtype_example); $subtype_default = array( array( 'key' => $subtype_default_key, 'name' => pht('Task'), ), ); $subtype_description = $this->deformat(pht(<<.// Show users creation forms for these task subtypes. - `forms`: //Optional list.// Show users these specific forms, in order. If you don't specify either constraint, users will be shown creation forms for the same subtype. For example, if you have a "quest" subtype and do not configure `children`, users who click "Create Subtask" will be presented with all create forms for "quest" tasks. If you want to present them with forms for a different task subtype or set of subtypes instead, use `subtypes`: ``` { ... "children": { "subtypes": ["objective", "boss", "reward"] } ... } ``` If you want to present them with specific forms, use `forms` and specify form IDs: ``` { ... "children": { "forms": [12, 16] } ... } ``` When specifying forms by ID explicitly, the order you specify the forms in will be used when presenting options to the user. If only one option would be presented, the user will be taken directly to the appropriate form instead of being prompted to choose a form. The `fields` key can configure the behavior of custom fields on specific task subtypes. For example: ``` { ... "fields": { "custom.some-field": { "disabled": true } } ... } ``` Each field supports these options: - `disabled` //Optional bool.// Allows you to disable fields on certain subtypes. - `name` //Optional string.// Custom name of this field for the subtype. EOTEXT , $subtype_default_key)); $priorities_description = $this->deformat(pht(<<.// List of unique keywords which identify this priority, like "high" or "low". Each priority must have at least one keyword and two priorities may not share the same keyword. - `short` //Optional string.// Alternate shorter name, used in UIs where there is less space available. - `color` //Optional string.// Color for this priority, like "red" or "blue". - `disabled` //Optional bool.// Set to true to prevent users from choosing this priority when creating or editing tasks. Existing tasks will not be affected, and can be batch edited to a different priority or left to eventually die out. You can choose the default priority for newly created tasks with "maniphest.default-priority". EOTEXT )); + $fields_description = $this->deformat(pht(<<newOption('maniphest.custom-field-definitions', 'wild', array()) ->setSummary(pht('Custom Maniphest fields.')) - ->setDescription( - pht( - 'Array of custom fields for Maniphest tasks. For details on '. - 'adding custom fields to Maniphest, see "Configuring Custom '. - 'Fields" in the documentation.')) + ->setDescription($fields_description) ->addExample($fields_json, pht('Valid setting')), $this->newOption('maniphest.fields', $custom_field_type, $default_fields) ->setCustomData(id(new ManiphestTask())->getCustomFieldBaseClass()) ->setDescription(pht('Select and reorder task fields.')), $this->newOption( 'maniphest.priorities', $priority_type, $priority_defaults) ->setSummary(pht('Configure Maniphest priority names.')) ->setDescription($priorities_description), $this->newOption('maniphest.statuses', $status_type, $status_defaults) ->setSummary(pht('Configure Maniphest task statuses.')) ->setDescription($status_description) ->addExample($status_example, pht('Minimal Valid Config')), $this->newOption('maniphest.default-priority', 'int', 90) ->setSummary(pht('Default task priority for create flows.')) ->setDescription( pht( 'Choose a default priority for newly created tasks. You can '. 'review and adjust available priorities by using the '. '%s configuration option. The default value (`90`) '. 'corresponds to the default "Needs Triage" priority.', 'maniphest.priorities')), $this->newOption('maniphest.points', $points_type, array()) ->setSummary(pht('Configure point values for tasks.')) ->setDescription($points_description) ->addExample($points_json_1, pht('Points Config')) ->addExample($points_json_2, pht('Hours Config')), $this->newOption('maniphest.subtypes', $subtype_type, $subtype_default) ->setSummary(pht('Define task subtypes.')) ->setDescription($subtype_description) ->addExample($subtype_example, pht('Simple Subtypes')), ); } } diff --git a/src/docs/user/configuration/custom_fields.diviner b/src/docs/user/configuration/custom_fields.diviner index ecb7382648..75d83fc8ba 100644 --- a/src/docs/user/configuration/custom_fields.diviner +++ b/src/docs/user/configuration/custom_fields.diviner @@ -1,212 +1,216 @@ @title Configuring Custom Fields @group config How to add custom fields to applications which support them. = Overview = Several Phabricator applications allow the configuration of custom fields. These fields allow you to add more information to objects, and in some cases reorder or remove builtin fields. For example, you could use custom fields to add an "Estimated Hours" field to tasks, a "Lead" field to projects, or a "T-Shirt Size" field to users. These applications currently support custom fields: | Application | Support | |-------------|---------| | Differential | Partial Support | | Diffusion | Limited Support | | Maniphest | Full Support | | Owners | Full Support | | People | Full Support | | Projects | Full Support | Custom fields can appear in many interfaces and support search, editing, and other features. = Basic Custom Fields = To get started with custom fields, you can use configuration to select and reorder fields and to add new simple fields. If you don't need complicated display controls or sophisticated validation, these simple fields should cover most use cases. They allow you to attach things like strings, numbers, and dropdown menus to objects. The relevant configuration settings are: | Application | Add Fields | Select Fields | |-------------|------------|---------------| | Differential | Planned | `differential.fields` | | Diffusion | Planned | Planned | | Maniphest | `maniphest.custom-field-definitions` | `maniphest.fields` | | Owners | `owners.custom-field-definitions` | `owners.fields` | | People | `user.custom-field-definitions` | `user.fields` | | Projects | `projects.custom-field-definitions` | `projects.fields` | When adding fields, you'll specify a JSON blob like this (for example, as the value of `maniphest.custom-field-definitions`): { "mycompany:estimated-hours": { "name": "Estimated Hours", "type": "int", "caption": "Estimated number of hours this will take.", "required": true }, "mycompany:actual-hours": { "name": "Actual Hours", "type": "int", "caption": "Actual number of hours this took." }, "mycompany:company-jobs": { "name": "Job Role", "type": "select", "options": { "mycompany:engineer": "Engineer", "mycompany:nonengineer": "Other" } }, "mycompany:favorite-dinosaur": { "name": "Favorite Dinosaur", "type": "text" } } The fields will then appear in the other config option for the application (for example, in `maniphest.fields`) and you can enable, disable, or reorder them. For details on how to define a field, see the next section. = Custom Field Configuration = When defining custom fields using a configuration option like `maniphest.custom-field-definitions`, these options are available: - **name**: Display label for the field on the edit and detail interfaces. - **description**: Optional text shown when managing the field. - **type**: Field type. The supported field types are: - **int**: An integer, rendered as a text field. - **text**: A string, rendered as a text field. - **bool**: A boolean value, rendered as a checkbox. - **select**: Allows the user to select from several options as defined by **options**, rendered as a dropdown. - **remarkup**: A text area which allows the user to enter markup. - **users**: A typeahead which allows multiple users to be input. - **date**: A date/time picker. - **header**: Renders a visual divider which you can use to group fields. - **link**: A text field which allows the user to enter a link. - **edit**: Show this field on the application's edit interface (this defaults to `true`). - **view**: Show this field on the application's view interface (this defaults to `true`). (Note: Empty fields are not shown.) - **search**: Show this field on the application's search interface, allowing users to filter objects by the field value. - **fulltext**: Index the text in this field as part of the object's global full-text index. This allows users to find the object by searching for the field's contents using global search. - **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. - **default**: Default field value. - **strings**: Allows you to override specific strings based on the field type. See below. - **instructions**: Optional block of remarkup text which will appear above the control when rendered on the edit view. - **placeholder**: A placeholder text that appears on text boxes. Only supported in text, int and remarkup fields (optional). - **copy**: If true, this field's value will be copied when an object is created using another object as a template. + - **limit**: For control types which use a tokenizer control to let the user + select a list of values, this limits how many values can be selected. For + example, a "users" field with a limit of "1" will behave like the "Owner" + field in Maniphest and only allow selection of a single user. The `strings` value supports different strings per control type. They are: - **bool** - **edit.checkbox** Text for the edit interface, no default. - **view.yes** Text for the view interface, defaults to "Yes". - **search.default** Text for the search interface, defaults to "(Any)". - **search.require** Text for the search interface, defaults to "Require". Internally, Phabricator implements some additional custom field types and options. These are not intended for general use and are subject to abrupt change, but are documented here for completeness: - **Credentials**: Controls with type `credential` allow selection of a Passphrase credential which provides `credential.provides`, and creation of credentials of `credential.type`. - **Datasource**: Controls with type `datasource` allow selection of tokens from an arbitrary datasource, controlled with `datasource.class` and `datasource.parameters`. = Advanced Custom Fields = If you want custom fields to have advanced behaviors (sophisticated rendering, advanced validation, complicated controls, interaction with other systems, etc), you can write a custom field as an extension and add it to Phabricator. NOTE: This API is somewhat new and fairly large. You should expect that there will be occasional changes to the API requiring minor updates in your code. To do this, extend the appropriate `CustomField` class for the application you want to add a field to: | Application | Extend | |-------------|---------| | Differential | @{class:DifferentialCustomField} | | Diffusion | @{class:PhabricatorCommitCustomField} | | Maniphest | @{class:ManiphestCustomField} | | Owners | @{class:PhabricatorOwnersCustomField} | | People | @{class:PhabricatorUserCustomField} | | Projects | @{class:PhabricatorProjectCustomField} | The easiest way to get started is to drop your subclass into `phabricator/src/extensions/`. If Phabricator is configured in development mode, the class should immediately be available in the UI. If not, you can restart Phabricator (for help, see @{article:Restarting Phabricator}). For example, this is a simple template which adds a custom field to Maniphest: name=ExampleManiphestCustomField.php 'color: #ff00ff', ), pht('It worked!')); } } Broadly, you can then add features by overriding more methods and implementing them. Many of the native fields are implemented on the custom field architecture, and it may be useful to look at them. For details on available integrations, see the base class for your application and @{class:PhabricatorCustomField}. = Next Steps = Continue by: - learning more about extending Phabricator with custom code in @{article@phabcontrib:Adding New Classes}; - or returning to the @{article: Configuration Guide}.