1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-18 12:52:42 +01:00

Add CustomField support to Owners

Summary: Fixes T9351. This is straightforward since this application is now relatively modern and doesn't have any bizarre craziness.

Test Plan:
{F787981}

{F787982}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9351

Differential Revision: https://secure.phabricator.com/D14093
This commit is contained in:
epriestley 2015-09-10 13:32:31 -07:00
parent 093a625698
commit f8080ce931
12 changed files with 233 additions and 40 deletions

View file

@ -0,0 +1,25 @@
CREATE TABLE {$NAMESPACE}_owners.owners_customfieldstorage (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectPHID VARBINARY(64) NOT NULL,
fieldIndex BINARY(12) NOT NULL,
fieldValue LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
UNIQUE KEY (objectPHID, fieldIndex)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
CREATE TABLE {$NAMESPACE}_owners.owners_customfieldstringindex (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectPHID VARBINARY(64) NOT NULL,
indexKey BINARY(12) NOT NULL,
indexValue LONGTEXT NOT NULL COLLATE {$COLLATE_SORT},
KEY `key_join` (objectPHID, indexKey, indexValue(64)),
KEY `key_find` (indexKey, indexValue(64))
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
CREATE TABLE {$NAMESPACE}_owners.owners_customfieldnumericindex (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
objectPHID VARBINARY(64) NOT NULL,
indexKey BINARY(12) NOT NULL,
indexValue BIGINT NOT NULL,
KEY `key_join` (objectPHID, indexKey, indexValue),
KEY `key_find` (indexKey, indexValue)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -2437,7 +2437,12 @@ phutil_register_library_map(array(
'PhabricatorOwnerPathQuery' => 'applications/owners/query/PhabricatorOwnerPathQuery.php', 'PhabricatorOwnerPathQuery' => 'applications/owners/query/PhabricatorOwnerPathQuery.php',
'PhabricatorOwnersApplication' => 'applications/owners/application/PhabricatorOwnersApplication.php', 'PhabricatorOwnersApplication' => 'applications/owners/application/PhabricatorOwnersApplication.php',
'PhabricatorOwnersConfigOptions' => 'applications/owners/config/PhabricatorOwnersConfigOptions.php', 'PhabricatorOwnersConfigOptions' => 'applications/owners/config/PhabricatorOwnersConfigOptions.php',
'PhabricatorOwnersConfiguredCustomField' => 'applications/owners/customfield/PhabricatorOwnersConfiguredCustomField.php',
'PhabricatorOwnersController' => 'applications/owners/controller/PhabricatorOwnersController.php', 'PhabricatorOwnersController' => 'applications/owners/controller/PhabricatorOwnersController.php',
'PhabricatorOwnersCustomField' => 'applications/owners/customfield/PhabricatorOwnersCustomField.php',
'PhabricatorOwnersCustomFieldNumericIndex' => 'applications/owners/storage/PhabricatorOwnersCustomFieldNumericIndex.php',
'PhabricatorOwnersCustomFieldStorage' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStorage.php',
'PhabricatorOwnersCustomFieldStringIndex' => 'applications/owners/storage/PhabricatorOwnersCustomFieldStringIndex.php',
'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php', 'PhabricatorOwnersDAO' => 'applications/owners/storage/PhabricatorOwnersDAO.php',
'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php', 'PhabricatorOwnersDetailController' => 'applications/owners/controller/PhabricatorOwnersDetailController.php',
'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php', 'PhabricatorOwnersEditController' => 'applications/owners/controller/PhabricatorOwnersEditController.php',
@ -6425,7 +6430,15 @@ phutil_register_library_map(array(
'PhabricatorOwnerPathQuery' => 'Phobject', 'PhabricatorOwnerPathQuery' => 'Phobject',
'PhabricatorOwnersApplication' => 'PhabricatorApplication', 'PhabricatorOwnersApplication' => 'PhabricatorApplication',
'PhabricatorOwnersConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorOwnersConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorOwnersConfiguredCustomField' => array(
'PhabricatorOwnersCustomField',
'PhabricatorStandardCustomFieldInterface',
),
'PhabricatorOwnersController' => 'PhabricatorController', 'PhabricatorOwnersController' => 'PhabricatorController',
'PhabricatorOwnersCustomField' => 'PhabricatorCustomField',
'PhabricatorOwnersCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage',
'PhabricatorOwnersCustomFieldStorage' => 'PhabricatorCustomFieldStorage',
'PhabricatorOwnersCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO', 'PhabricatorOwnersDAO' => 'PhabricatorLiskDAO',
'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController', 'PhabricatorOwnersDetailController' => 'PhabricatorOwnersController',
'PhabricatorOwnersEditController' => 'PhabricatorOwnersController', 'PhabricatorOwnersEditController' => 'PhabricatorOwnersController',
@ -6435,6 +6448,7 @@ phutil_register_library_map(array(
'PhabricatorOwnersDAO', 'PhabricatorOwnersDAO',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorCustomFieldInterface',
), ),
'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorOwnersPackageFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',

View file

@ -20,9 +20,35 @@ final class PhabricatorOwnersConfigOptions
} }
public function getOptions() { public function getOptions() {
$custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType';
$default_fields = array();
$field_base_class = id(new PhabricatorOwnersPackage())
->getCustomFieldBaseClass();
$fields_example = array(
'mycompany:lore' => array(
'name' => pht('Package Lore'),
'type' => 'remarkup',
'caption' => pht('Tales of adventure for this package.'),
),
);
$fields_example = id(new PhutilJSON())->encodeFormatted($fields_example);
return array( return array(
$this->newOption('metamta.package.subject-prefix', 'string', '[Package]') $this->newOption('metamta.package.subject-prefix', 'string', '[Package]')
->setDescription(pht('Subject prefix for Owners email.')), ->setDescription(pht('Subject prefix for Owners email.')),
$this->newOption('owners.fields', $custom_field_type, $default_fields)
->setCustomData($field_base_class)
->setDescription(pht('Select and reorder package fields.')),
$this->newOption('owners.custom-field-definitions', 'wild', array())
->setSummary(pht('Custom Owners fields.'))
->setDescription(
pht(
'Map of custom fields for Owners packages. For details on '.
'adding custom fields to Owners, see "Configuring Custom '.
'Fields" in the documentation.'))
->addExample($fields_example, pht('Valid Setting')),
); );
} }

View file

@ -37,8 +37,15 @@ final class PhabricatorOwnersDetailController
$repositories = array(); $repositories = array();
} }
$field_list = PhabricatorCustomField::getObjectFields(
$package,
PhabricatorCustomField::ROLE_VIEW);
$field_list
->setViewer($viewer)
->readFieldsFromStorage($package);
$actions = $this->buildPackageActionView($package); $actions = $this->buildPackageActionView($package);
$properties = $this->buildPackagePropertyView($package); $properties = $this->buildPackagePropertyView($package, $field_list);
$properties->setActionList($actions); $properties->setActionList($actions);
if ($package->isArchived()) { if ($package->isArchived()) {
@ -156,7 +163,10 @@ final class PhabricatorOwnersDetailController
} }
private function buildPackagePropertyView(PhabricatorOwnersPackage $package) { private function buildPackagePropertyView(
PhabricatorOwnersPackage $package,
PhabricatorCustomFieldList $field_list) {
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$view = id(new PHUIPropertyListView()) $view = id(new PHUIPropertyListView())
@ -187,6 +197,13 @@ final class PhabricatorOwnersDetailController
$viewer)); $viewer));
} }
$view->invokeWillRenderEvent();
$field_list->appendFieldsToPropertyList(
$package,
$viewer,
$view);
return $view; return $view;
} }

View file

@ -36,6 +36,11 @@ final class PhabricatorOwnersEditController
$v_description = $package->getDescription(); $v_description = $package->getDescription();
$v_status = $package->getStatus(); $v_status = $package->getStatus();
$field_list = PhabricatorCustomField::getObjectFields(
$package,
PhabricatorCustomField::ROLE_EDIT);
$field_list->setViewer($viewer);
$field_list->readFieldsFromStorage($package);
$errors = array(); $errors = array();
if ($request->isFormPost()) { if ($request->isFormPost()) {
@ -75,6 +80,12 @@ final class PhabricatorOwnersEditController
->setNewValue($v_status); ->setNewValue($v_status);
} }
$field_xactions = $field_list->buildFieldTransactionsFromRequest(
new PhabricatorOwnersPackageTransaction(),
$request);
$xactions = array_merge($xactions, $field_xactions);
$editor = id(new PhabricatorOwnersPackageTransactionEditor()) $editor = id(new PhabricatorOwnersPackageTransactionEditor())
->setActor($viewer) ->setActor($viewer)
->setContentSourceFromRequest($request) ->setContentSourceFromRequest($request)
@ -126,41 +137,44 @@ final class PhabricatorOwnersEditController
->setName('owners') ->setName('owners')
->setValue($v_owners)); ->setValue($v_owners));
if (!$is_new) { if (!$is_new) {
$form->appendChild(
id(new AphrontFormSelectControl())
->setLabel(pht('Status'))
->setName('status')
->setValue($v_status)
->setOptions($package->getStatusNameMap()));
}
$form->appendChild( $form->appendChild(
id(new AphrontFormSelectControl()) id(new AphrontFormSelectControl())
->setName('auditing') ->setLabel(pht('Status'))
->setLabel(pht('Auditing')) ->setName('status')
->setCaption( ->setValue($v_status)
pht( ->setOptions($package->getStatusNameMap()));
'With auditing enabled, all future commits that touch '. }
'this package will be reviewed to make sure an owner '.
'of the package is involved and the commit message has '. $form->appendChild(
'a valid revision, reviewed by, and author.')) id(new AphrontFormSelectControl())
->setOptions( ->setName('auditing')
array( ->setLabel(pht('Auditing'))
'disabled' => pht('Disabled'), ->setCaption(
'enabled' => pht('Enabled'), pht(
)) 'With auditing enabled, all future commits that touch '.
->setValue(($v_auditing ? 'enabled' : 'disabled'))) 'this package will be reviewed to make sure an owner '.
'of the package is involved and the commit message has '.
'a valid revision, reviewed by, and author.'))
->setOptions(
array(
'disabled' => pht('Disabled'),
'enabled' => pht('Enabled'),
))
->setValue(($v_auditing ? 'enabled' : 'disabled')))
->appendChild( ->appendChild(
id(new PhabricatorRemarkupControl()) id(new PhabricatorRemarkupControl())
->setUser($viewer) ->setUser($viewer)
->setLabel(pht('Description')) ->setLabel(pht('Description'))
->setName('description') ->setName('description')
->setValue($v_description)) ->setValue($v_description));
->appendChild(
id(new AphrontFormSubmitControl()) $field_list->appendFieldsToForm($form);
->addCancelButton($cancel_uri)
->setValue($button_text)); $form->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($cancel_uri)
->setValue($button_text));
$form_box = id(new PHUIObjectBoxView()) $form_box = id(new PHUIObjectBoxView())
->setHeaderText($title) ->setHeaderText($title)

View file

@ -0,0 +1,23 @@
<?php
final class PhabricatorOwnersConfiguredCustomField
extends PhabricatorOwnersCustomField
implements PhabricatorStandardCustomFieldInterface {
public function getStandardCustomFieldNamespace() {
return 'owners';
}
public function createFields($object) {
$config = PhabricatorEnv::getEnvConfig(
'owners.custom-field-definitions',
array());
$fields = PhabricatorStandardCustomField::buildStandardFields(
$this,
$config);
return $fields;
}
}

View file

@ -0,0 +1,18 @@
<?php
abstract class PhabricatorOwnersCustomField
extends PhabricatorCustomField {
public function newStorageObject() {
return new PhabricatorOwnersCustomFieldStorage();
}
protected function newStringIndexStorage() {
return new PhabricatorOwnersCustomFieldStringIndex();
}
protected function newNumericIndexStorage() {
return new PhabricatorOwnersCustomFieldNumericIndex();
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorOwnersCustomFieldNumericIndex
extends PhabricatorCustomFieldNumericIndexStorage {
public function getApplicationName() {
return 'owners';
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorOwnersCustomFieldStorage
extends PhabricatorCustomFieldStorage {
public function getApplicationName() {
return 'owners';
}
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorOwnersCustomFieldStringIndex
extends PhabricatorCustomFieldStringIndexStorage {
public function getApplicationName() {
return 'owners';
}
}

View file

@ -4,7 +4,8 @@ final class PhabricatorOwnersPackage
extends PhabricatorOwnersDAO extends PhabricatorOwnersDAO
implements implements
PhabricatorPolicyInterface, PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface { PhabricatorApplicationTransactionInterface,
PhabricatorCustomFieldInterface {
protected $name; protected $name;
protected $originalName; protected $originalName;
@ -16,6 +17,7 @@ final class PhabricatorOwnersPackage
private $paths = self::ATTACHABLE; private $paths = self::ATTACHABLE;
private $owners = self::ATTACHABLE; private $owners = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
const STATUS_ACTIVE = 'active'; const STATUS_ACTIVE = 'active';
const STATUS_ARCHIVED = 'archived'; const STATUS_ARCHIVED = 'archived';
@ -304,4 +306,25 @@ final class PhabricatorOwnersPackage
return $timeline; return $timeline;
} }
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return PhabricatorEnv::getEnvConfig('owners.fields');
}
public function getCustomFieldBaseClass() {
return 'PhabricatorOwnersCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
} }

View file

@ -16,11 +16,12 @@ These applications currently support custom fields:
| Application | Support | | Application | Support |
|-------------|---------| |-------------|---------|
| Maniphest | Full Support |
| Projects | Full Support |
| People | Full Support |
| Differential | Partial Support | | Differential | Partial Support |
| Diffusion | Limited 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 Custom fields can appear in many interfaces and support search, editing, and
other features. other features.
@ -38,11 +39,12 @@ The relevant configuration settings are:
| Application | Add Fields | Select Fields | | Application | Add Fields | Select Fields |
|-------------|------------|---------------| |-------------|------------|---------------|
| Maniphest | `maniphest.custom-field-definitions` | `maniphest.fields` |
| Projects | `projects.custom-field-definitions` | `projects.fields` |
| People | `user.custom-field-definitions` | `user.fields` |
| Differential | Planned | `differential.fields` | | Differential | Planned | `differential.fields` |
| Diffusion | Planned | Planned | | 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 When adding fields, you'll specify a JSON blob like this (for example, as the
value of `maniphest.custom-field-definitions`): value of `maniphest.custom-field-definitions`):
@ -157,11 +159,12 @@ want to add a field to:
| Application | Extend | | Application | Extend |
|-------------|---------| |-------------|---------|
| Maniphest | @{class:ManiphestCustomField} |
| Projects | @{class:PhabricatorProjectCustomField} |
| People | @{class:PhabricatorUserCustomField} |
| Differential | @{class:DifferentialCustomField} | | Differential | @{class:DifferentialCustomField} |
| Diffusion | @{class:PhabricatorCommitCustomField} | | 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 The easiest way to get started is to drop your subclass into
`phabricator/src/extensions/`, which should make it immediately available in the `phabricator/src/extensions/`, which should make it immediately available in the