mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-23 22:10:55 +01:00
Modularize application extensions to EditEngine
Summary: Ref T9132. Currently, EditEngine had some branchy-`instanceof` code like this: ``` if ($object instanceof Whatever) { do_magic(); } if ($object instanceof SomethingElse) { do_other_magic(); } ``` ...where `Whatever` and `SomethingElse` are first-party applications like ProjectsInterface and SubscribersInterface. This kind of code is generally bad because third-parties can't add new stuff, and it suggest something is kind of hacky in its architecture. Ideally, we would eventually get rid of almost all of this. T9789 is a similar discussion of this for the next layer down (`TransactionEditor`) and plans to get rid of branchy-instanceofs there too. Since I'm about to add more stuff here (for Custom Fields), split it out first so I'm not digging us any deeper than I already dug us. Broadly, this allows third-party extensions to add fields to every EditEngine UI if they want, like we do for Policies, Subscribers, Projects and Comments today (and CustomFields soon). Test Plan: {F1007575} - Observed that all fields still appear on the form and seem to work correctly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9132 Differential Revision: https://secure.phabricator.com/D14599
This commit is contained in:
parent
9a19309345
commit
acd955c6c9
10 changed files with 477 additions and 146 deletions
|
@ -1844,6 +1844,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorChatLogQuery' => 'applications/chatlog/query/PhabricatorChatLogQuery.php',
|
||||
'PhabricatorChunkedFileStorageEngine' => 'applications/files/engine/PhabricatorChunkedFileStorageEngine.php',
|
||||
'PhabricatorClusterConfigOptions' => 'applications/config/option/PhabricatorClusterConfigOptions.php',
|
||||
'PhabricatorCommentEditEngineExtension' => 'applications/transactions/editengineextension/PhabricatorCommentEditEngineExtension.php',
|
||||
'PhabricatorCommentEditField' => 'applications/transactions/editfield/PhabricatorCommentEditField.php',
|
||||
'PhabricatorCommentEditType' => 'applications/transactions/edittype/PhabricatorCommentEditType.php',
|
||||
'PhabricatorCommitBranchesField' => 'applications/repository/customfield/PhabricatorCommitBranchesField.php',
|
||||
|
@ -2132,6 +2133,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditEngineConfigurationTransactionQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php',
|
||||
'PhabricatorEditEngineConfigurationViewController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php',
|
||||
'PhabricatorEditEngineController' => 'applications/transactions/controller/PhabricatorEditEngineController.php',
|
||||
'PhabricatorEditEngineExtension' => 'applications/transactions/editengineextension/PhabricatorEditEngineExtension.php',
|
||||
'PhabricatorEditEngineExtensionModule' => 'applications/transactions/editengineextension/PhabricatorEditEngineExtensionModule.php',
|
||||
'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php',
|
||||
'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php',
|
||||
'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php',
|
||||
|
@ -2706,6 +2709,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyDAO' => 'applications/policy/storage/PhabricatorPolicyDAO.php',
|
||||
'PhabricatorPolicyDataTestCase' => 'applications/policy/__tests__/PhabricatorPolicyDataTestCase.php',
|
||||
'PhabricatorPolicyEditController' => 'applications/policy/controller/PhabricatorPolicyEditController.php',
|
||||
'PhabricatorPolicyEditEngineExtension' => 'applications/policy/editor/PhabricatorPolicyEditEngineExtension.php',
|
||||
'PhabricatorPolicyEditField' => 'applications/transactions/editfield/PhabricatorPolicyEditField.php',
|
||||
'PhabricatorPolicyException' => 'applications/policy/exception/PhabricatorPolicyException.php',
|
||||
'PhabricatorPolicyExplainController' => 'applications/policy/controller/PhabricatorPolicyExplainController.php',
|
||||
|
@ -2797,6 +2801,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php',
|
||||
'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php',
|
||||
'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
|
||||
'PhabricatorProjectsEditEngineExtension' => 'applications/project/editor/PhabricatorProjectsEditEngineExtension.php',
|
||||
'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
|
||||
'PhabricatorProjectsPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsPolicyRule.php',
|
||||
'PhabricatorProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php',
|
||||
|
@ -3087,6 +3092,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsAddSubscribersHeraldAction.php',
|
||||
'PhabricatorSubscriptionsApplication' => 'applications/subscriptions/application/PhabricatorSubscriptionsApplication.php',
|
||||
'PhabricatorSubscriptionsEditController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php',
|
||||
'PhabricatorSubscriptionsEditEngineExtension' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditEngineExtension.php',
|
||||
'PhabricatorSubscriptionsEditor' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php',
|
||||
'PhabricatorSubscriptionsHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsHeraldAction.php',
|
||||
'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php',
|
||||
|
@ -5896,6 +5902,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorChatLogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorChunkedFileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||
'PhabricatorClusterConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||
'PhabricatorCommentEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorCommentEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorCommentEditType' => 'PhabricatorEditType',
|
||||
'PhabricatorCommitBranchesField' => 'PhabricatorCommitCustomField',
|
||||
|
@ -6232,6 +6239,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController',
|
||||
'PhabricatorEditEngineController' => 'PhabricatorApplicationTransactionController',
|
||||
'PhabricatorEditEngineExtension' => 'Phobject',
|
||||
'PhabricatorEditEngineExtensionModule' => 'PhabricatorConfigModule',
|
||||
'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController',
|
||||
'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
|
@ -6898,6 +6907,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorPolicyDataTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorPolicyEditController' => 'PhabricatorPolicyController',
|
||||
'PhabricatorPolicyEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorPolicyEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorPolicyException' => 'Exception',
|
||||
'PhabricatorPolicyExplainController' => 'PhabricatorPolicyController',
|
||||
|
@ -7014,6 +7024,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||
'PhabricatorProjectViewController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
|
||||
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
|
||||
'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule',
|
||||
'PhabricatorProtocolAdapter' => 'Phobject',
|
||||
|
@ -7360,6 +7371,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSubscriptionsAddSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
|
||||
'PhabricatorSubscriptionsApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorSubscriptionsEditController' => 'PhabricatorController',
|
||||
'PhabricatorSubscriptionsEditEngineExtension' => 'PhabricatorEditEngineExtension',
|
||||
'PhabricatorSubscriptionsEditor' => 'PhabricatorEditor',
|
||||
'PhabricatorSubscriptionsHeraldAction' => 'HeraldAction',
|
||||
'PhabricatorSubscriptionsListController' => 'PhabricatorController',
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPolicyEditEngineExtension
|
||||
extends PhabricatorEditEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'policy.policy';
|
||||
|
||||
public function getExtensionPriority() {
|
||||
return 250;
|
||||
}
|
||||
|
||||
public function isExtensionEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getExtensionName() {
|
||||
return pht('Policies');
|
||||
}
|
||||
|
||||
public function supportsObject(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object) {
|
||||
return ($object instanceof PhabricatorPolicyInterface);
|
||||
}
|
||||
|
||||
public function buildCustomEditFields(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object) {
|
||||
|
||||
$viewer = $engine->getViewer();
|
||||
|
||||
$editor = $object->getApplicationTransactionEditor();
|
||||
$types = $editor->getTransactionTypesForObject($object);
|
||||
$types = array_fuse($types);
|
||||
|
||||
$policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($viewer)
|
||||
->setObject($object)
|
||||
->execute();
|
||||
|
||||
$map = array(
|
||||
PhabricatorTransactions::TYPE_VIEW_POLICY => array(
|
||||
'key' => 'policy.view',
|
||||
'aliases' => array('view'),
|
||||
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
|
||||
'label' => pht('View Policy'),
|
||||
'description' => pht('Controls who can view the object.'),
|
||||
'edit' => 'view',
|
||||
),
|
||||
PhabricatorTransactions::TYPE_EDIT_POLICY => array(
|
||||
'key' => 'policy.edit',
|
||||
'aliases' => array('edit'),
|
||||
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
|
||||
'label' => pht('Edit Policy'),
|
||||
'description' => pht('Controls who can edit the object.'),
|
||||
'edit' => 'edit',
|
||||
),
|
||||
PhabricatorTransactions::TYPE_JOIN_POLICY => array(
|
||||
'key' => 'policy.join',
|
||||
'aliases' => array('join'),
|
||||
'capability' => PhabricatorPolicyCapability::CAN_JOIN,
|
||||
'label' => pht('Join Policy'),
|
||||
'description' => pht('Controls who can join the object.'),
|
||||
'edit' => 'join',
|
||||
),
|
||||
);
|
||||
|
||||
$fields = array();
|
||||
foreach ($map as $type => $spec) {
|
||||
if (empty($types[$type])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$capability = $spec['capability'];
|
||||
$key = $spec['key'];
|
||||
$aliases = $spec['aliases'];
|
||||
$label = $spec['label'];
|
||||
$description = $spec['description'];
|
||||
$edit = $spec['edit'];
|
||||
|
||||
$policy_field = id(new PhabricatorPolicyEditField())
|
||||
->setKey($key)
|
||||
->setLabel($label)
|
||||
->setDescription($description)
|
||||
->setAliases($aliases)
|
||||
->setCapability($capability)
|
||||
->setPolicies($policies)
|
||||
->setTransactionType($type)
|
||||
->setEditTypeKey($edit)
|
||||
->setValue($object->getPolicy($capability));
|
||||
$fields[] = $policy_field;
|
||||
|
||||
if (!($object instanceof PhabricatorSpacesInterface)) {
|
||||
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
|
||||
$type_space = PhabricatorTransactions::TYPE_SPACE;
|
||||
if (isset($types[$type_space])) {
|
||||
$space_field = id(new PhabricatorSpaceEditField())
|
||||
->setKey('spacePHID')
|
||||
->setLabel(pht('Space'))
|
||||
->setEditTypeKey('space')
|
||||
->setDescription(
|
||||
pht('Shifts the object in the Spaces application.'))
|
||||
->setIsReorderable(false)
|
||||
->setAliases(array('space', 'policy.space'))
|
||||
->setTransactionType($type_space)
|
||||
->setValue($object->getSpacePHID());
|
||||
$fields[] = $space_field;
|
||||
|
||||
$policy_field->setSpaceField($space_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorProjectsEditEngineExtension
|
||||
extends PhabricatorEditEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'projects.projects';
|
||||
|
||||
public function getExtensionPriority() {
|
||||
return 500;
|
||||
}
|
||||
|
||||
public function isExtensionEnabled() {
|
||||
return PhabricatorApplication::isClassInstalled(
|
||||
'PhabricatorProjectApplication');
|
||||
}
|
||||
|
||||
public function getExtensionName() {
|
||||
return pht('Projects');
|
||||
}
|
||||
|
||||
public function supportsObject(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object) {
|
||||
|
||||
return ($object instanceof PhabricatorProjectInterface);
|
||||
}
|
||||
|
||||
public function buildCustomEditFields(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object) {
|
||||
|
||||
$edge_type = PhabricatorTransactions::TYPE_EDGE;
|
||||
$project_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||
|
||||
$object_phid = $object->getPHID();
|
||||
if ($object_phid) {
|
||||
$project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||
$object_phid,
|
||||
$project_edge_type);
|
||||
$project_phids = array_reverse($project_phids);
|
||||
} else {
|
||||
$project_phids = array();
|
||||
}
|
||||
|
||||
$projects_field = id(new PhabricatorProjectsEditField())
|
||||
->setKey('projectPHIDs')
|
||||
->setLabel(pht('Projects'))
|
||||
->setEditTypeKey('projects')
|
||||
->setDescription(pht('Add or remove associated projects.'))
|
||||
->setAliases(array('project', 'projects'))
|
||||
->setTransactionType($edge_type)
|
||||
->setMetadataValue('edge:type', $project_edge_type)
|
||||
->setValue($project_phids);
|
||||
|
||||
return array(
|
||||
$projects_field,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSubscriptionsEditEngineExtension
|
||||
extends PhabricatorEditEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'subscriptions.subscribers';
|
||||
|
||||
public function getExtensionPriority() {
|
||||
return 750;
|
||||
}
|
||||
|
||||
public function isExtensionEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getExtensionName() {
|
||||
return pht('Subscriptions');
|
||||
}
|
||||
|
||||
public function supportsObject(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object) {
|
||||
return ($object instanceof PhabricatorSubscribableInterface);
|
||||
}
|
||||
|
||||
public function buildCustomEditFields(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object) {
|
||||
|
||||
$subscribers_type = PhabricatorTransactions::TYPE_SUBSCRIBERS;
|
||||
|
||||
$object_phid = $object->getPHID();
|
||||
if ($object_phid) {
|
||||
$sub_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||
$object_phid);
|
||||
} else {
|
||||
// TODO: Allow applications to provide default subscribers? Maniphest
|
||||
// does this at a minimum.
|
||||
$sub_phids = array();
|
||||
}
|
||||
|
||||
$subscribers_field = id(new PhabricatorSubscribersEditField())
|
||||
->setKey('subscriberPHIDs')
|
||||
->setLabel(pht('Subscribers'))
|
||||
->setEditTypeKey('subscribers')
|
||||
->setDescription(pht('Manage subscribers.'))
|
||||
->setAliases(array('subscriber', 'subscribers'))
|
||||
->setTransactionType($subscribers_type)
|
||||
->setValue($sub_phids);
|
||||
|
||||
return array(
|
||||
$subscribers_field,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -73,163 +73,27 @@ abstract class PhabricatorEditEngine
|
|||
|
||||
final protected function buildEditFields($object) {
|
||||
$viewer = $this->getViewer();
|
||||
$editor = $object->getApplicationTransactionEditor();
|
||||
|
||||
$types = $editor->getTransactionTypesForObject($object);
|
||||
$types = array_fuse($types);
|
||||
|
||||
$fields = $this->buildCustomEditFields($object);
|
||||
|
||||
if ($object instanceof PhabricatorPolicyInterface) {
|
||||
$policies = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($viewer)
|
||||
->setObject($object)
|
||||
->execute();
|
||||
$extensions = PhabricatorEditEngineExtension::getAllEnabledExtensions();
|
||||
foreach ($extensions as $extension) {
|
||||
$extension->setViewer($viewer);
|
||||
|
||||
$map = array(
|
||||
PhabricatorTransactions::TYPE_VIEW_POLICY => array(
|
||||
'key' => 'policy.view',
|
||||
'aliases' => array('view'),
|
||||
'capability' => PhabricatorPolicyCapability::CAN_VIEW,
|
||||
'label' => pht('View Policy'),
|
||||
'description' => pht('Controls who can view the object.'),
|
||||
'edit' => 'view',
|
||||
),
|
||||
PhabricatorTransactions::TYPE_EDIT_POLICY => array(
|
||||
'key' => 'policy.edit',
|
||||
'aliases' => array('edit'),
|
||||
'capability' => PhabricatorPolicyCapability::CAN_EDIT,
|
||||
'label' => pht('Edit Policy'),
|
||||
'description' => pht('Controls who can edit the object.'),
|
||||
'edit' => 'edit',
|
||||
),
|
||||
PhabricatorTransactions::TYPE_JOIN_POLICY => array(
|
||||
'key' => 'policy.join',
|
||||
'aliases' => array('join'),
|
||||
'capability' => PhabricatorPolicyCapability::CAN_JOIN,
|
||||
'label' => pht('Join Policy'),
|
||||
'description' => pht('Controls who can join the object.'),
|
||||
'edit' => 'join',
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($map as $type => $spec) {
|
||||
if (empty($types[$type])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$capability = $spec['capability'];
|
||||
$key = $spec['key'];
|
||||
$aliases = $spec['aliases'];
|
||||
$label = $spec['label'];
|
||||
$description = $spec['description'];
|
||||
$edit = $spec['edit'];
|
||||
|
||||
$policy_field = id(new PhabricatorPolicyEditField())
|
||||
->setKey($key)
|
||||
->setLabel($label)
|
||||
->setDescription($description)
|
||||
->setAliases($aliases)
|
||||
->setCapability($capability)
|
||||
->setPolicies($policies)
|
||||
->setTransactionType($type)
|
||||
->setEditTypeKey($edit)
|
||||
->setValue($object->getPolicy($capability));
|
||||
$fields[] = $policy_field;
|
||||
|
||||
if ($object instanceof PhabricatorSpacesInterface) {
|
||||
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
|
||||
$type_space = PhabricatorTransactions::TYPE_SPACE;
|
||||
if (isset($types[$type_space])) {
|
||||
$space_field = id(new PhabricatorSpaceEditField())
|
||||
->setKey('spacePHID')
|
||||
->setLabel(pht('Space'))
|
||||
->setEditTypeKey('space')
|
||||
->setDescription(
|
||||
pht('Shifts the object in the Spaces application.'))
|
||||
->setIsReorderable(false)
|
||||
->setAliases(array('space', 'policy.space'))
|
||||
->setTransactionType($type_space)
|
||||
->setValue($object->getSpacePHID());
|
||||
$fields[] = $space_field;
|
||||
|
||||
$policy_field->setSpaceField($space_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$extension->supportsObject($this, $object)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$edge_type = PhabricatorTransactions::TYPE_EDGE;
|
||||
$object_phid = $object->getPHID();
|
||||
$extension_fields = $extension->buildCustomEditFields($this, $object);
|
||||
|
||||
$project_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST;
|
||||
// TODO: Validate this in more detail with a more tailored error.
|
||||
assert_instances_of($extension_fields, 'PhabricatorEditField');
|
||||
|
||||
if ($object instanceof PhabricatorProjectInterface) {
|
||||
if (isset($types[$edge_type])) {
|
||||
if ($object_phid) {
|
||||
$project_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||
$object_phid,
|
||||
$project_edge_type);
|
||||
$project_phids = array_reverse($project_phids);
|
||||
} else {
|
||||
$project_phids = array();
|
||||
}
|
||||
|
||||
$edge_field = id(new PhabricatorProjectsEditField())
|
||||
->setKey('projectPHIDs')
|
||||
->setLabel(pht('Projects'))
|
||||
->setEditTypeKey('projects')
|
||||
->setDescription(pht('Add or remove associated projects.'))
|
||||
->setAliases(array('project', 'projects'))
|
||||
->setTransactionType($edge_type)
|
||||
->setMetadataValue('edge:type', $project_edge_type)
|
||||
->setValue($project_phids);
|
||||
$fields[] = $edge_field;
|
||||
foreach ($extension_fields as $field) {
|
||||
$fields[] = $field;
|
||||
}
|
||||
}
|
||||
|
||||
$subscribers_type = PhabricatorTransactions::TYPE_SUBSCRIBERS;
|
||||
|
||||
if ($object instanceof PhabricatorSubscribableInterface) {
|
||||
if (isset($types[$subscribers_type])) {
|
||||
if ($object_phid) {
|
||||
$sub_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID(
|
||||
$object_phid);
|
||||
} else {
|
||||
// TODO: Allow applications to provide default subscribers; Maniphest
|
||||
// does this at a minimum.
|
||||
$sub_phids = array();
|
||||
}
|
||||
|
||||
$subscribers_field = id(new PhabricatorSubscribersEditField())
|
||||
->setKey('subscriberPHIDs')
|
||||
->setLabel(pht('Subscribers'))
|
||||
->setEditTypeKey('subscribers')
|
||||
->setDescription(pht('Manage subscribers.'))
|
||||
->setAliases(array('subscriber', 'subscribers'))
|
||||
->setTransactionType($subscribers_type)
|
||||
->setValue($sub_phids);
|
||||
$fields[] = $subscribers_field;
|
||||
}
|
||||
}
|
||||
|
||||
$xaction = $object->getApplicationTransactionTemplate();
|
||||
$comment = $xaction->getApplicationTransactionCommentObject();
|
||||
if ($comment) {
|
||||
$comment_type = PhabricatorTransactions::TYPE_COMMENT;
|
||||
|
||||
$comment_field = id(new PhabricatorCommentEditField())
|
||||
->setKey('comment')
|
||||
->setLabel(pht('Comments'))
|
||||
->setDescription(pht('Add comments.'))
|
||||
->setAliases(array('comments'))
|
||||
->setIsHidden(true)
|
||||
->setTransactionType($comment_type)
|
||||
->setValue(null);
|
||||
$fields[] = $comment_field;
|
||||
}
|
||||
|
||||
$config = $this->getEditEngineConfiguration();
|
||||
$fields = $config->applyConfigurationToFields($this, $fields);
|
||||
|
||||
|
@ -678,6 +542,8 @@ abstract class PhabricatorEditEngine
|
|||
$validation_exception = null;
|
||||
if ($request->isFormPost()) {
|
||||
foreach ($fields as $field) {
|
||||
$field->setIsSubmittedForm(true);
|
||||
|
||||
if ($field->getIsLocked() || $field->getIsHidden()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -709,6 +575,20 @@ abstract class PhabricatorEditEngine
|
|||
->setURI($this->getObjectViewURI($object));
|
||||
} catch (PhabricatorApplicationTransactionValidationException $ex) {
|
||||
$validation_exception = $ex;
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$xaction_type = $field->getTransactionType();
|
||||
if ($xaction_type === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$message = $ex->getShortMessage($xaction_type);
|
||||
if ($message === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$field->setControlError($message);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ($this->getIsCreate()) {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCommentEditEngineExtension
|
||||
extends PhabricatorEditEngineExtension {
|
||||
|
||||
const EXTENSIONKEY = 'transactions.comment';
|
||||
|
||||
public function getExtensionPriority() {
|
||||
return 9000;
|
||||
}
|
||||
|
||||
public function isExtensionEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getExtensionName() {
|
||||
return pht('Comments');
|
||||
}
|
||||
|
||||
public function supportsObject(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object) {
|
||||
|
||||
$xaction = $object->getApplicationTransactionTemplate();
|
||||
|
||||
try {
|
||||
$comment = $xaction->getApplicationTransactionCommentObject();
|
||||
} catch (PhutilMethodNotImplementedException $ex) {
|
||||
$comment = null;
|
||||
}
|
||||
|
||||
return (bool)$comment;
|
||||
}
|
||||
|
||||
public function buildCustomEditFields(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object) {
|
||||
|
||||
$comment_type = PhabricatorTransactions::TYPE_COMMENT;
|
||||
|
||||
$comment_field = id(new PhabricatorCommentEditField())
|
||||
->setKey('comment')
|
||||
->setLabel(pht('Comments'))
|
||||
->setDescription(pht('Add comments.'))
|
||||
->setAliases(array('comments'))
|
||||
->setIsHidden(true)
|
||||
->setTransactionType($comment_type)
|
||||
->setValue(null);
|
||||
|
||||
return array(
|
||||
$comment_field,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorEditEngineExtension extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
|
||||
final public function getExtensionKey() {
|
||||
return $this->getPhobjectClassConstant('EXTENSIONKEY');
|
||||
}
|
||||
|
||||
final public function setViewer($viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
public function getExtensionPriority() {
|
||||
return 1000;
|
||||
}
|
||||
|
||||
abstract public function isExtensionEnabled();
|
||||
abstract public function getExtensionName();
|
||||
|
||||
abstract public function supportsObject(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object);
|
||||
|
||||
abstract public function buildCustomEditFields(
|
||||
PhabricatorEditEngine $engine,
|
||||
PhabricatorApplicationTransactionInterface $object);
|
||||
|
||||
final public static function getAllExtensions() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->setUniqueMethod('getExtensionKey')
|
||||
->setSortMethod('getExtensionPriority')
|
||||
->execute();
|
||||
}
|
||||
|
||||
final public static function getAllEnabledExtensions() {
|
||||
$extensions = self::getAllExtensions();
|
||||
|
||||
foreach ($extensions as $key => $extension) {
|
||||
if (!$extension->isExtensionEnabled()) {
|
||||
unset($extensions[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $extensions;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEditEngineExtensionModule
|
||||
extends PhabricatorConfigModule {
|
||||
|
||||
public function getModuleKey() {
|
||||
return 'editengine';
|
||||
}
|
||||
|
||||
public function getModuleName() {
|
||||
return pht('EditEngine Extensions');
|
||||
}
|
||||
|
||||
public function renderModuleStatus(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
|
||||
$extensions = PhabricatorEditEngineExtension::getAllExtensions();
|
||||
|
||||
$rows = array();
|
||||
foreach ($extensions as $extension) {
|
||||
$rows[] = array(
|
||||
$extension->getExtensionPriority(),
|
||||
get_class($extension),
|
||||
$extension->getExtensionName(),
|
||||
$extension->isExtensionEnabled()
|
||||
? pht('Yes')
|
||||
: pht('No'),
|
||||
);
|
||||
}
|
||||
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setHeaders(
|
||||
array(
|
||||
pht('Priority'),
|
||||
pht('Class'),
|
||||
pht('Name'),
|
||||
pht('Enabled'),
|
||||
))
|
||||
->setColumnClasses(
|
||||
array(
|
||||
null,
|
||||
null,
|
||||
'wide pri',
|
||||
null,
|
||||
));
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('EditEngine Extensions'))
|
||||
->setTable($table);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,6 +11,9 @@ final class PhabricatorDatasourceEditField
|
|||
}
|
||||
|
||||
public function getDatasource() {
|
||||
if (!$this->datasource) {
|
||||
throw new PhutilInvalidStateException('setDatasource');
|
||||
}
|
||||
return $this->datasource;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,12 +13,15 @@ abstract class PhabricatorEditField extends Phobject {
|
|||
private $metadata = array();
|
||||
private $description;
|
||||
private $editTypeKey;
|
||||
private $isRequired;
|
||||
|
||||
private $isLocked;
|
||||
private $isHidden;
|
||||
|
||||
private $isPreview;
|
||||
private $isEditDefaults;
|
||||
private $isSubmittedForm;
|
||||
private $controlError;
|
||||
|
||||
private $isReorderable = true;
|
||||
private $isDefaultable = true;
|
||||
|
@ -145,6 +148,33 @@ abstract class PhabricatorEditField extends Phobject {
|
|||
throw new PhutilMethodNotImplementedException();
|
||||
}
|
||||
|
||||
public function setIsSubmittedForm($is_submitted) {
|
||||
$this->isSubmittedForm = $is_submitted;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsSubmittedForm() {
|
||||
return $this->isSubmittedForm;
|
||||
}
|
||||
|
||||
public function setIsRequired($is_required) {
|
||||
$this->isRequired = $is_required;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getIsRequired() {
|
||||
return $this->isRequired;
|
||||
}
|
||||
|
||||
public function setControlError($control_error) {
|
||||
$this->controlError = $control_error;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getControlError() {
|
||||
return $this->controlError;
|
||||
}
|
||||
|
||||
protected function renderControl() {
|
||||
$control = $this->newControl();
|
||||
if ($control === null) {
|
||||
|
@ -176,6 +206,16 @@ abstract class PhabricatorEditField extends Phobject {
|
|||
|
||||
$control->setDisabled($disabled);
|
||||
|
||||
|
||||
if ($this->getIsSubmittedForm()) {
|
||||
$error = $this->getControlError();
|
||||
if ($error !== null) {
|
||||
$control->setError($error);
|
||||
}
|
||||
} else if ($this->getIsRequired()) {
|
||||
$control->setError(true);
|
||||
}
|
||||
|
||||
return $control;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue