mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-19 12:00:55 +01:00
Roughly generate transaction-oriented API methods from EditEngines
Summary: Ref T5873. Ref T9132. This is really rough and feels pretty flimsy at the edges (missing validation, generality, modularity, clean error handling, etc) but gets us most of the way toward generating plausible "whatever.edit" Conduit API methods from EditEngines. These methods are full-power methods which can do everything the edit form can, automatically support the same range of operations, and update when new fields are added. Test Plan: - Used new `paste.edit` to create a new Paste. - Used new `paste.edit` to update an existing paste. - Applied a variety of different transactions. - Hit a reasonable set of errors. {F941144} {F941145} {F941146} {F941147} Reviewers: chad Reviewed By: chad Maniphest Tasks: T5873, T9132 Differential Revision: https://secure.phabricator.com/D14393
This commit is contained in:
parent
3dec4c7dbd
commit
5030ba0401
10 changed files with 663 additions and 2 deletions
|
@ -1530,6 +1530,7 @@ phutil_register_library_map(array(
|
|||
'PasteCreateMailReceiver' => 'applications/paste/mail/PasteCreateMailReceiver.php',
|
||||
'PasteDefaultEditCapability' => 'applications/paste/capability/PasteDefaultEditCapability.php',
|
||||
'PasteDefaultViewCapability' => 'applications/paste/capability/PasteDefaultViewCapability.php',
|
||||
'PasteEditConduitAPIMethod' => 'applications/paste/conduit/PasteEditConduitAPIMethod.php',
|
||||
'PasteEmbedView' => 'applications/paste/view/PasteEmbedView.php',
|
||||
'PasteInfoConduitAPIMethod' => 'applications/paste/conduit/PasteInfoConduitAPIMethod.php',
|
||||
'PasteMailReceiver' => 'applications/paste/mail/PasteMailReceiver.php',
|
||||
|
@ -1571,6 +1572,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php',
|
||||
'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php',
|
||||
'PhabricatorApplicationEditEngine' => 'applications/transactions/editengine/PhabricatorApplicationEditEngine.php',
|
||||
'PhabricatorApplicationEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php',
|
||||
'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php',
|
||||
'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php',
|
||||
'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php',
|
||||
|
@ -2094,6 +2096,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEdgeConfig' => 'infrastructure/edges/constants/PhabricatorEdgeConfig.php',
|
||||
'PhabricatorEdgeConstants' => 'infrastructure/edges/constants/PhabricatorEdgeConstants.php',
|
||||
'PhabricatorEdgeCycleException' => 'infrastructure/edges/exception/PhabricatorEdgeCycleException.php',
|
||||
'PhabricatorEdgeEditType' => 'applications/transactions/edittype/PhabricatorEdgeEditType.php',
|
||||
'PhabricatorEdgeEditor' => 'infrastructure/edges/editor/PhabricatorEdgeEditor.php',
|
||||
'PhabricatorEdgeGraph' => 'infrastructure/edges/util/PhabricatorEdgeGraph.php',
|
||||
'PhabricatorEdgeQuery' => 'infrastructure/edges/query/PhabricatorEdgeQuery.php',
|
||||
|
@ -2101,6 +2104,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php',
|
||||
'PhabricatorEdgeTypeTestCase' => 'infrastructure/edges/type/__tests__/PhabricatorEdgeTypeTestCase.php',
|
||||
'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php',
|
||||
'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php',
|
||||
'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php',
|
||||
'PhabricatorElasticSearchEngine' => 'applications/search/engine/PhabricatorElasticSearchEngine.php',
|
||||
'PhabricatorElasticSearchSetupCheck' => 'applications/config/check/PhabricatorElasticSearchSetupCheck.php',
|
||||
|
@ -2936,6 +2940,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSetupIssue' => 'applications/config/issue/PhabricatorSetupIssue.php',
|
||||
'PhabricatorSetupIssueUIExample' => 'applications/uiexample/examples/PhabricatorSetupIssueUIExample.php',
|
||||
'PhabricatorSetupIssueView' => 'applications/config/view/PhabricatorSetupIssueView.php',
|
||||
'PhabricatorSimpleEditType' => 'applications/transactions/edittype/PhabricatorSimpleEditType.php',
|
||||
'PhabricatorSite' => 'aphront/site/PhabricatorSite.php',
|
||||
'PhabricatorSlowvoteApplication' => 'applications/slowvote/application/PhabricatorSlowvoteApplication.php',
|
||||
'PhabricatorSlowvoteChoice' => 'applications/slowvote/storage/PhabricatorSlowvoteChoice.php',
|
||||
|
@ -5457,6 +5462,7 @@ phutil_register_library_map(array(
|
|||
'PasteCreateMailReceiver' => 'PhabricatorMailReceiver',
|
||||
'PasteDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||
'PasteDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||
'PasteEditConduitAPIMethod' => 'PhabricatorApplicationEditEngineAPIMethod',
|
||||
'PasteEmbedView' => 'AphrontView',
|
||||
'PasteInfoConduitAPIMethod' => 'PasteConduitAPIMethod',
|
||||
'PasteMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
|
@ -5501,6 +5507,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController',
|
||||
'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController',
|
||||
'PhabricatorApplicationEditEngine' => 'Phobject',
|
||||
'PhabricatorApplicationEditEngineAPIMethod' => 'ConduitAPIMethod',
|
||||
'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView',
|
||||
'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController',
|
||||
'PhabricatorApplicationLaunchView' => 'AphrontTagView',
|
||||
|
@ -6121,6 +6128,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEdgeConfig' => 'PhabricatorEdgeConstants',
|
||||
'PhabricatorEdgeConstants' => 'Phobject',
|
||||
'PhabricatorEdgeCycleException' => 'Exception',
|
||||
'PhabricatorEdgeEditType' => 'PhabricatorEditType',
|
||||
'PhabricatorEdgeEditor' => 'Phobject',
|
||||
'PhabricatorEdgeGraph' => 'AbstractDirectedGraph',
|
||||
'PhabricatorEdgeQuery' => 'PhabricatorQuery',
|
||||
|
@ -6128,6 +6136,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorEdgeType' => 'Phobject',
|
||||
'PhabricatorEdgeTypeTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorEditField' => 'Phobject',
|
||||
'PhabricatorEditType' => 'Phobject',
|
||||
'PhabricatorEditor' => 'Phobject',
|
||||
'PhabricatorElasticSearchEngine' => 'PhabricatorSearchEngine',
|
||||
'PhabricatorElasticSearchSetupCheck' => 'PhabricatorSetupCheck',
|
||||
|
@ -7120,6 +7129,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSetupIssue' => 'Phobject',
|
||||
'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorSetupIssueView' => 'AphrontView',
|
||||
'PhabricatorSimpleEditType' => 'PhabricatorEditType',
|
||||
'PhabricatorSite' => 'AphrontSite',
|
||||
'PhabricatorSlowvoteApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorSlowvoteChoice' => 'PhabricatorSlowvoteDAO',
|
||||
|
|
19
src/applications/paste/conduit/PasteEditConduitAPIMethod.php
Normal file
19
src/applications/paste/conduit/PasteEditConduitAPIMethod.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
final class PasteEditConduitAPIMethod
|
||||
extends PhabricatorApplicationEditEngineAPIMethod {
|
||||
|
||||
public function getAPIMethodName() {
|
||||
return 'paste.edit';
|
||||
}
|
||||
|
||||
public function newEditEngine() {
|
||||
return new PhabricatorPasteEditEngine();
|
||||
}
|
||||
|
||||
public function getMethodSummary() {
|
||||
return pht(
|
||||
'Apply transactions to create a new paste or edit an existing one.');
|
||||
}
|
||||
|
||||
}
|
|
@ -72,6 +72,30 @@ final class PhabricatorPasteEditor
|
|||
$this->fileName = $name;
|
||||
}
|
||||
|
||||
protected function validateTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
$type,
|
||||
array $xactions) {
|
||||
|
||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
||||
switch ($type) {
|
||||
case PhabricatorPasteTransaction::TYPE_CONTENT:
|
||||
if (!$object->getFilePHID() && !$xactions) {
|
||||
$error = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Required'),
|
||||
pht('You must provide content to create a paste.'),
|
||||
null);
|
||||
|
||||
$error->setIsMissingFieldError(true);
|
||||
$errors[] = $error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
protected function getCustomTransactionOldValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
|
|
@ -41,6 +41,7 @@ final class PhabricatorPaste extends PhabricatorPasteDAO
|
|||
|
||||
return id(new PhabricatorPaste())
|
||||
->setTitle('')
|
||||
->setLanguage('')
|
||||
->setStatus(self::STATUS_ACTIVE)
|
||||
->setAuthorPHID($actor->getPHID())
|
||||
->setViewPolicy($view_policy)
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
<?php
|
||||
|
||||
|
||||
/**
|
||||
* @task web Responding to Web Requests
|
||||
* @task conduit Responding to Conduit Requests
|
||||
*/
|
||||
abstract class PhabricatorApplicationEditEngine extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
|
@ -47,6 +52,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
'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',
|
||||
|
@ -54,6 +60,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
'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',
|
||||
|
@ -61,6 +68,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
'capability' => PhabricatorPolicyCapability::CAN_JOIN,
|
||||
'label' => pht('Join Policy'),
|
||||
'description' => pht('Controls who can join the object.'),
|
||||
'edit' => 'join',
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -74,6 +82,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
$aliases = $spec['aliases'];
|
||||
$label = $spec['label'];
|
||||
$description = $spec['description'];
|
||||
$edit = $spec['edit'];
|
||||
|
||||
$policy_field = id(new PhabricatorPolicyEditField())
|
||||
->setKey($key)
|
||||
|
@ -83,6 +92,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
->setCapability($capability)
|
||||
->setPolicies($policies)
|
||||
->setTransactionType($type)
|
||||
->setEditTypeKey($edit)
|
||||
->setValue($object->getPolicy($capability));
|
||||
$fields[] = $policy_field;
|
||||
|
||||
|
@ -93,6 +103,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
$space_field = id(new PhabricatorSpaceEditField())
|
||||
->setKey('spacePHID')
|
||||
->setLabel(pht('Space'))
|
||||
->setEditTypeKey('space')
|
||||
->setDescription(
|
||||
pht('Shifts the object in the Spaces application.'))
|
||||
->setAliases(array('space', 'policy.space'))
|
||||
|
@ -126,6 +137,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
$edge_field = id(new PhabricatorDatasourceEditField())
|
||||
->setKey('projectPHIDs')
|
||||
->setLabel(pht('Projects'))
|
||||
->setEditTypeKey('projects')
|
||||
->setDescription(
|
||||
pht(
|
||||
'Add or remove associated projects.'))
|
||||
|
@ -154,6 +166,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
$subscribers_field = id(new PhabricatorDatasourceEditField())
|
||||
->setKey('subscriberPHIDs')
|
||||
->setLabel(pht('Subscribers'))
|
||||
->setEditTypeKey('subscribers')
|
||||
->setDescription(pht('Manage subscribers.'))
|
||||
->setDatasource(new PhabricatorMetaMTAMailableDatasource())
|
||||
->setAliases(array('subscriber', 'subscribers'))
|
||||
|
@ -240,11 +253,9 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
if (!$object) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$this->setIsCreate(false);
|
||||
} else {
|
||||
$object = $this->newEditableObject();
|
||||
|
||||
$this->setIsCreate(true);
|
||||
}
|
||||
|
||||
|
@ -443,4 +454,171 @@ abstract class PhabricatorApplicationEditEngine extends Phobject {
|
|||
return $actions;
|
||||
}
|
||||
|
||||
|
||||
/* -( Conduit )------------------------------------------------------------ */
|
||||
|
||||
|
||||
/**
|
||||
* Respond to a Conduit edit request.
|
||||
*
|
||||
* This method accepts a list of transactions to apply to an object, and
|
||||
* either edits an existing object or creates a new one.
|
||||
*
|
||||
* @task conduit
|
||||
*/
|
||||
final public function buildConduitResponse(ConduitAPIRequest $request) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
$phid = $request->getValue('objectPHID');
|
||||
if ($phid) {
|
||||
$object = $this->newObjectQuery()
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($phid))
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
))
|
||||
->executeOne();
|
||||
if (!$object) {
|
||||
throw new Exception(pht('No such object with PHID "%s".', $phid));
|
||||
}
|
||||
$this->setIsCreate(false);
|
||||
} else {
|
||||
$object = $this->newEditableObject();
|
||||
$this->setIsCreate(true);
|
||||
}
|
||||
|
||||
$fields = $this->buildEditFields($object);
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$field
|
||||
->setViewer($viewer)
|
||||
->setObject($object);
|
||||
}
|
||||
|
||||
$types = $this->getAllEditTypesFromFields($fields);
|
||||
$template = $object->getApplicationTransactionTemplate();
|
||||
|
||||
$xactions = $this->getConduitTransactions($request, $types, $template);
|
||||
|
||||
$editor = $object->getApplicationTransactionEditor()
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromConduitRequest($request)
|
||||
->setContinueOnNoEffect(true);
|
||||
|
||||
$xactions = $editor->applyTransactions($object, $xactions);
|
||||
|
||||
$xactions_struct = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
$xactions_struct[] = array(
|
||||
'phid' => $xaction->getPHID(),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'object' => array(
|
||||
'id' => $object->getID(),
|
||||
'phid' => $object->getPHID(),
|
||||
),
|
||||
'transactions' => $xactions_struct,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Generate transactions which can be applied from edit actions in a Conduit
|
||||
* request.
|
||||
*
|
||||
* @param ConduitAPIRequest The request.
|
||||
* @param list<PhabricatorEditType> Supported edit types.
|
||||
* @param PhabricatorApplicationTransaction Template transaction.
|
||||
* @return list<PhabricatorApplicationTransaction> Generated transactions.
|
||||
* @task conduit
|
||||
*/
|
||||
private function getConduitTransactions(
|
||||
ConduitAPIRequest $request,
|
||||
array $types,
|
||||
PhabricatorApplicationTransaction $template) {
|
||||
|
||||
$transactions_key = 'transactions';
|
||||
|
||||
$xactions = $request->getValue($transactions_key);
|
||||
if (!is_array($xactions)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Parameter "%s" is not a list of transactions.',
|
||||
$transactions_key));
|
||||
}
|
||||
|
||||
foreach ($xactions as $key => $xaction) {
|
||||
if (!is_array($xaction)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Parameter "%s" must contain a list of transaction descriptions, '.
|
||||
'but item with key "%s" is not a dictionary.',
|
||||
$transactions_key,
|
||||
$key));
|
||||
}
|
||||
|
||||
if (!array_key_exists('type', $xaction)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Parameter "%s" must contain a list of transaction descriptions, '.
|
||||
'but item with key "%s" is missing a "type" field. Each '.
|
||||
'transaction must have a type field.',
|
||||
$transactions_key,
|
||||
$key));
|
||||
}
|
||||
|
||||
$type = $xaction['type'];
|
||||
if (empty($types[$type])) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Transaction with key "%s" has invalid type "%s". This type is '.
|
||||
'not recognized. Valid types are: %s.',
|
||||
$key,
|
||||
$type,
|
||||
implode(', ', array_keys($types))));
|
||||
}
|
||||
}
|
||||
|
||||
$results = array();
|
||||
foreach ($xactions as $xaction) {
|
||||
$type = $types[$xaction['type']];
|
||||
|
||||
$results[] = $type->generateTransaction(
|
||||
clone $template,
|
||||
$xaction);
|
||||
}
|
||||
|
||||
return $results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return map<string, PhabricatorEditType>
|
||||
* @task conduit
|
||||
*/
|
||||
private function getAllEditTypesFromFields(array $fields) {
|
||||
$types = array();
|
||||
foreach ($fields as $field) {
|
||||
$field_types = $field->getEditTransactionTypes();
|
||||
foreach ($field_types as $field_type) {
|
||||
$field_type->setField($field);
|
||||
$types[$field_type->getEditType()] = $field_type;
|
||||
}
|
||||
}
|
||||
return $types;
|
||||
}
|
||||
|
||||
public function getAllEditTypes() {
|
||||
$object = $this->newEditableObject();
|
||||
$fields = $this->buildEditFields($object);
|
||||
return $this->getAllEditTypesFromFields($fields);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorApplicationEditEngineAPIMethod
|
||||
extends ConduitAPIMethod {
|
||||
|
||||
abstract public function newEditEngine();
|
||||
|
||||
public function getMethodStatus() {
|
||||
return self::METHOD_STATUS_UNSTABLE;
|
||||
}
|
||||
|
||||
public function getMethodStatusDescription() {
|
||||
return pht('ApplicationEditor methods are highly unstable.');
|
||||
}
|
||||
|
||||
final protected function defineParamTypes() {
|
||||
return array(
|
||||
'transactions' => 'list<map<string, wild>>',
|
||||
'objectPHID' => 'optional phid',
|
||||
);
|
||||
}
|
||||
|
||||
final protected function defineReturnType() {
|
||||
return 'map<string, wild>';
|
||||
}
|
||||
|
||||
final protected function execute(ConduitAPIRequest $request) {
|
||||
$engine = $this->newEditEngine()
|
||||
->setViewer($request->getUser());
|
||||
|
||||
return $engine->buildConduitResponse($request);
|
||||
}
|
||||
|
||||
final public function getMethodDescription() {
|
||||
// TODO: We don't currently have a real viewer in this method.
|
||||
$viewer = new PhabricatorUser();
|
||||
|
||||
$engine = $this->newEditEngine()
|
||||
->setViewer($viewer);
|
||||
|
||||
$types = $engine->getAllEditTypes();
|
||||
|
||||
$out = array();
|
||||
|
||||
$out[] = pht(<<<EOTEXT
|
||||
This is a standard **ApplicationEditor** method which allows you to create and
|
||||
modify objects by applying transactions.
|
||||
|
||||
Each transaction applies one change to the object. For example, to create an
|
||||
object with a specific title or change the title of an existing object you might
|
||||
start by building a transaction like this:
|
||||
|
||||
```lang=json, name=Example Single Transaction
|
||||
{
|
||||
"type": "title",
|
||||
"value": "New Object Title"
|
||||
}
|
||||
```
|
||||
|
||||
By passing a list of transactions in the `transactions` parameter, you can
|
||||
apply a sequence of edits. For example, you'll often pass a value like this to
|
||||
create an object with several field values or apply changes to multiple fields:
|
||||
|
||||
```lang=json, name=Example Transaction List
|
||||
[
|
||||
{
|
||||
"type": "title",
|
||||
"value": "New Object Title"
|
||||
},
|
||||
{
|
||||
"type": "body",
|
||||
"value": "New body text for the object."
|
||||
},
|
||||
{
|
||||
"type": "projects.add",
|
||||
"value": ["PHID-PROJ-1111", "PHID-PROJ-2222"]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Exactly which types of edits are available depends on the object you're editing.
|
||||
|
||||
|
||||
Creating Objects
|
||||
----------------
|
||||
|
||||
To create an object, pass a list of `transactions` but leave `objectPHID`
|
||||
empty. This will create a new object with the initial field values you
|
||||
specify.
|
||||
|
||||
|
||||
Editing Objects
|
||||
---------------
|
||||
|
||||
To edit an object, pass a list of `transactions` and specify an object to
|
||||
apply them to with `objectPHID`. This will apply the changes to the object.
|
||||
|
||||
|
||||
Return Type
|
||||
-----------
|
||||
|
||||
WARNING: The structure of the return value from these methods is likely to
|
||||
change as ApplicationEditor evolves.
|
||||
|
||||
Return values look something like this for now:
|
||||
|
||||
```lang=json, name=Example Return Value
|
||||
{
|
||||
"object": {
|
||||
"phid": "PHID-XXXX-1111"
|
||||
},
|
||||
"transactions": [
|
||||
{
|
||||
"phid": "PHID-YYYY-1111",
|
||||
},
|
||||
{
|
||||
"phid": "PHID-YYYY-2222",
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The `object` key contains information about the object which was created or
|
||||
edited.
|
||||
|
||||
The `transactions` key contains information about the transactions which were
|
||||
actually applied. For many reasons, the transactions which actually apply may
|
||||
be greater or fewer in number than the transactions you provided, or may differ
|
||||
in their nature in other ways.
|
||||
|
||||
|
||||
Edit Types
|
||||
==========
|
||||
|
||||
This API method supports these edit types:
|
||||
EOTEXT
|
||||
);
|
||||
|
||||
$key = pht('Key');
|
||||
$summary = pht('Summary');
|
||||
$description = pht('Description');
|
||||
$head_type = pht('Type');
|
||||
|
||||
$table = array();
|
||||
$table[] = "| {$key} | {$summary} |";
|
||||
$table[] = '|--------|----------------|';
|
||||
foreach ($types as $type) {
|
||||
$edit_type = $type->getEditType();
|
||||
$edit_summary = $type->getSummary();
|
||||
$table[] = "| `{$edit_type}` | {$edit_summary} |";
|
||||
}
|
||||
|
||||
$out[] = implode("\n", $table);
|
||||
|
||||
foreach ($types as $type) {
|
||||
$section = array();
|
||||
$section[] = pht('Edit Type: %s', $type->getEditType());
|
||||
$section[] = '---------';
|
||||
$section[] = null;
|
||||
$section[] = $type->getDescription();
|
||||
$section[] = null;
|
||||
$section[] = pht(
|
||||
'This edit generates transactions of type `%s` internally.',
|
||||
$type->getTransactionType());
|
||||
$section[] = null;
|
||||
|
||||
$type_description = pht(
|
||||
'Use `%s` to select this edit type.',
|
||||
$type->getEditType());
|
||||
|
||||
$value_type = $type->getValueType();
|
||||
$value_description = $type->getValueDescription();
|
||||
|
||||
$table = array();
|
||||
$table[] = "| {$key} | {$head_type} | {$description} |";
|
||||
$table[] = '|--------|--------------|----------------|';
|
||||
$table[] = "| `type` | `const` | {$type_description} |";
|
||||
$table[] = "| `value` | `{$value_type}` | {$value_description} |";
|
||||
$section[] = implode("\n", $table);
|
||||
|
||||
$out[] = implode("\n", $section);
|
||||
}
|
||||
|
||||
$out = implode("\n\n", $out);
|
||||
return $out;
|
||||
}
|
||||
|
||||
}
|
|
@ -12,6 +12,8 @@ abstract class PhabricatorEditField extends Phobject {
|
|||
private $transactionType;
|
||||
private $metadata = array();
|
||||
private $description;
|
||||
private $editTypeKey;
|
||||
|
||||
|
||||
public function setKey($key) {
|
||||
$this->key = $key;
|
||||
|
@ -223,4 +225,75 @@ abstract class PhabricatorEditField extends Phobject {
|
|||
return 'string';
|
||||
}
|
||||
|
||||
public function setEditTypeKey($edit_type_key) {
|
||||
$this->editTypeKey = $edit_type_key;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEditTypeKey() {
|
||||
if ($this->editTypeKey === null) {
|
||||
return $this->getKey();
|
||||
}
|
||||
return $this->editTypeKey;
|
||||
}
|
||||
|
||||
public function getEditTransactionTypes() {
|
||||
$transaction_type = $this->getTransactionType();
|
||||
$type_key = $this->getEditTypeKey();
|
||||
|
||||
// TODO: This is a pretty big pile of hard-coded hacks for now.
|
||||
|
||||
$edge_types = array(
|
||||
PhabricatorTransactions::TYPE_EDGE => array(
|
||||
'+' => pht('Add projects.'),
|
||||
'-' => pht('Remove projects.'),
|
||||
'=' => pht('Set associated projects, overwriting current value.'),
|
||||
),
|
||||
PhabricatorTransactions::TYPE_SUBSCRIBERS => array(
|
||||
'+' => pht('Add subscribers.'),
|
||||
'-' => pht('Remove subscribers.'),
|
||||
'=' => pht('Set subscribers, overwriting current value.'),
|
||||
),
|
||||
);
|
||||
|
||||
if (isset($edge_types[$transaction_type])) {
|
||||
$base = id(new PhabricatorEdgeEditType())
|
||||
->setTransactionType($transaction_type)
|
||||
->setMetadata($this->metadata);
|
||||
|
||||
$strings = $edge_types[$transaction_type];
|
||||
|
||||
$add = id(clone $base)
|
||||
->setEditType($type_key.'.add')
|
||||
->setEdgeOperation('+')
|
||||
->setDescription($strings['+'])
|
||||
->setValueDescription(pht('List of PHIDs to add.'));
|
||||
$rem = id(clone $base)
|
||||
->setEditType($type_key.'.remove')
|
||||
->setEdgeOperation('-')
|
||||
->setDescription($strings['-'])
|
||||
->setValueDescription(pht('List of PHIDs to remove.'));
|
||||
$set = id(clone $base)
|
||||
->setEditType($type_key.'.set')
|
||||
->setEdgeOperation('=')
|
||||
->setDescription($strings['='])
|
||||
->setValueDescription(pht('List of PHIDs to set.'));
|
||||
|
||||
return array(
|
||||
$add,
|
||||
$rem,
|
||||
$set,
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
id(new PhabricatorSimpleEditType())
|
||||
->setEditType($type_key)
|
||||
->setTransactionType($transaction_type)
|
||||
->setValueType($this->getHTTPParameterType())
|
||||
->setDescription($this->getDescription())
|
||||
->setMetadata($this->metadata),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorEdgeEditType extends PhabricatorEditType {
|
||||
|
||||
private $edgeOperation;
|
||||
private $valueDescription;
|
||||
|
||||
public function setEdgeOperation($edge_operation) {
|
||||
$this->edgeOperation = $edge_operation;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEdgeOperation() {
|
||||
return $this->edgeOperation;
|
||||
}
|
||||
|
||||
public function getValueType() {
|
||||
return 'list<phid>';
|
||||
}
|
||||
|
||||
public function generateTransaction(
|
||||
PhabricatorApplicationTransaction $template,
|
||||
array $spec) {
|
||||
|
||||
$value = idx($spec, 'value');
|
||||
$value = array_fuse($value);
|
||||
$value = array(
|
||||
$this->getEdgeOperation() => $value,
|
||||
);
|
||||
|
||||
$template
|
||||
->setTransactionType($this->getTransactionType())
|
||||
->setNewValue($value);
|
||||
|
||||
foreach ($this->getMetadata() as $key => $value) {
|
||||
$template->setMetadataValue($key, $value);
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
public function setValueDescription($value_description) {
|
||||
$this->valueDescription = $value_description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValueDescription() {
|
||||
return $this->valueDescription;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorEditType extends Phobject {
|
||||
|
||||
private $editType;
|
||||
private $transactionType;
|
||||
private $field;
|
||||
private $description;
|
||||
private $summary;
|
||||
private $metadata = array();
|
||||
|
||||
public function setDescription($description) {
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setSummary($summary) {
|
||||
$this->summary = $summary;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSummary() {
|
||||
if ($this->summary === null) {
|
||||
return $this->getDescription();
|
||||
}
|
||||
return $this->summary;
|
||||
}
|
||||
|
||||
public function setField(PhabricatorEditField $field) {
|
||||
$this->field = $field;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getField() {
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
public function setEditType($edit_type) {
|
||||
$this->editType = $edit_type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getEditType() {
|
||||
return $this->editType;
|
||||
}
|
||||
|
||||
public function setMetadata($metadata) {
|
||||
$this->metadata = $metadata;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMetadata() {
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
public function setTransactionType($transaction_type) {
|
||||
$this->transactionType = $transaction_type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTransactionType() {
|
||||
return $this->transactionType;
|
||||
}
|
||||
|
||||
abstract public function generateTransaction(
|
||||
PhabricatorApplicationTransaction $template,
|
||||
array $spec);
|
||||
|
||||
abstract public function getValueType();
|
||||
abstract public function getValueDescription();
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSimpleEditType extends PhabricatorEditType {
|
||||
|
||||
private $valueType;
|
||||
private $valueDescription;
|
||||
|
||||
public function setValueType($value_type) {
|
||||
$this->valueType = $value_type;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValueType() {
|
||||
return $this->valueType;
|
||||
}
|
||||
|
||||
public function generateTransaction(
|
||||
PhabricatorApplicationTransaction $template,
|
||||
array $spec) {
|
||||
|
||||
$template
|
||||
->setTransactionType($this->getTransactionType())
|
||||
->setNewValue(idx($spec, 'value'));
|
||||
|
||||
foreach ($this->getMetadata() as $key => $value) {
|
||||
$template->setMetadataValue($key, $value);
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
public function setValueDescription($value_description) {
|
||||
$this->valueDescription = $value_description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getValueDescription() {
|
||||
return $this->valueDescription;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue